Commit-ID:  bf4d1a83758368c842c94cab9661a75ca98bc848
Gitweb:     http://git.kernel.org/tip/bf4d1a83758368c842c94cab9661a75ca98bc848
Author:     Josh Poimboeuf <jpoim...@redhat.com>
AuthorDate: Thu, 10 Aug 2017 16:37:26 -0500
Committer:  Ingo Molnar <mi...@kernel.org>
CommitDate: Fri, 11 Aug 2017 14:06:15 +0200

objtool: Track DRAP separately from callee-saved registers

When GCC realigns a function's stack, it sometimes uses %r13 as the DRAP
register, like:

  push  %r13
  lea   0x10(%rsp), %r13
  and   $0xfffffffffffffff0, %rsp
  pushq -0x8(%r13)
  push  %rbp
  mov   %rsp, %rbp
  push  %r13
  ...
  mov   -0x8(%rbp),%r13
  leaveq
  lea   -0x10(%r13), %rsp
  pop   %r13
  retq

Since %r13 was pushed onto the stack twice, its two stack locations need
to be stored separately.  The first push of %r13 is its original value,
and the second push of %r13 is the caller's stack frame address.

Since %r13 is a callee-saved register, we need to track the stack
location of its original value separately from the DRAP register.

This fixes the following false positive warning:

  lib/ubsan.o: warning: objtool: val_to_string.constprop.7()+0x97: leave 
instruction with modified stack frame

Reported-by: Arnd Bergmann <a...@arndb.de>
Signed-off-by: Josh Poimboeuf <jpoim...@redhat.com>
Cc: Andy Lutomirski <l...@kernel.org>
Cc: Borislav Petkov <b...@alien8.de>
Cc: Brian Gerst <brge...@gmail.com>
Cc: Denys Vlasenko <dvlas...@redhat.com>
Cc: H. Peter Anvin <h...@zytor.com>
Cc: Linus Torvalds <torva...@linux-foundation.org>
Cc: Peter Zijlstra <pet...@infradead.org>
Cc: Thomas Gleixner <t...@linutronix.de>
Fixes: baa41469a7b9 ("objtool: Implement stack validation 2.0")
Link: 
http://lkml.kernel.org/r/3da23a6d4c5b3c1e21fc2ccc21a73941b97ff20a.1502401017.git.jpoim...@redhat.com
Signed-off-by: Ingo Molnar <mi...@kernel.org>
---
 tools/objtool/check.c | 54 ++++++++++++++++++++++++++++-----------------------
 tools/objtool/check.h |  2 +-
 2 files changed, 31 insertions(+), 25 deletions(-)

diff --git a/tools/objtool/check.c b/tools/objtool/check.c
index 5814e90..1737592 100644
--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -221,6 +221,7 @@ static void clear_insn_state(struct insn_state *state)
        for (i = 0; i < CFI_NUM_REGS; i++)
                state->regs[i].base = CFI_UNDEFINED;
        state->drap_reg = CFI_UNDEFINED;
+       state->drap_offset = -1;
 }
 
 /*
@@ -1110,8 +1111,7 @@ static int update_insn_state_regs(struct instruction 
*insn, struct insn_state *s
 static void save_reg(struct insn_state *state, unsigned char reg, int base,
                     int offset)
 {
-       if ((arch_callee_saved_reg(reg) ||
-           (state->drap && reg == state->drap_reg)) &&
+       if (arch_callee_saved_reg(reg) &&
            state->regs[reg].base == CFI_UNDEFINED) {
                state->regs[reg].base = base;
                state->regs[reg].offset = offset;
@@ -1281,7 +1281,6 @@ static int update_insn_state(struct instruction *insn, 
struct insn_state *state)
                                cfa->base = state->drap_reg;
                                cfa->offset = state->stack_size = 0;
                                state->drap = true;
-
                        }
 
                        /*
@@ -1299,17 +1298,19 @@ static int update_insn_state(struct instruction *insn, 
struct insn_state *state)
                                cfa->base = CFI_SP;
                        }
 
-                       if (regs[op->dest.reg].offset == -state->stack_size) {
+                       if (state->drap && cfa->base == CFI_BP_INDIRECT &&
+                           op->dest.type == OP_DEST_REG &&
+                           op->dest.reg == state->drap_reg &&
+                           state->drap_offset == -state->stack_size) {
 
-                               if (state->drap && cfa->base == CFI_BP_INDIRECT 
&&
-                                   op->dest.type == OP_DEST_REG &&
-                                   op->dest.reg == state->drap_reg) {
+                               /* drap: pop %drap */
+                               cfa->base = state->drap_reg;
+                               cfa->offset = 0;
+                               state->drap_offset = -1;
 
-                                       /* drap: pop %drap */
-                                       cfa->base = state->drap_reg;
-                                       cfa->offset = 0;
-                               }
+                       } else if (regs[op->dest.reg].offset == 
-state->stack_size) {
 
+                               /* pop %reg */
                                restore_reg(state, op->dest.reg);
                        }
 
@@ -1321,14 +1322,18 @@ static int update_insn_state(struct instruction *insn, 
struct insn_state *state)
 
                case OP_SRC_REG_INDIRECT:
                        if (state->drap && op->src.reg == CFI_BP &&
+                           op->src.offset == state->drap_offset) {
+
+                               /* drap: mov disp(%rbp), %drap */
+                               cfa->base = state->drap_reg;
+                               cfa->offset = 0;
+                               state->drap_offset = -1;
+                       }
+
+                       if (state->drap && op->src.reg == CFI_BP &&
                            op->src.offset == regs[op->dest.reg].offset) {
 
                                /* drap: mov disp(%rbp), %reg */
-                               if (op->dest.reg == state->drap_reg) {
-                                       cfa->base = state->drap_reg;
-                                       cfa->offset = 0;
-                               }
-
                                restore_reg(state, op->dest.reg);
 
                        } else if (op->src.reg == cfa->base &&
@@ -1364,8 +1369,8 @@ static int update_insn_state(struct instruction *insn, 
struct insn_state *state)
                                cfa->base = CFI_BP_INDIRECT;
                                cfa->offset = -state->stack_size;
 
-                               /* save drap so we know when to undefine it */
-                               save_reg(state, op->src.reg, CFI_CFA, 
-state->stack_size);
+                               /* save drap so we know when to restore it */
+                               state->drap_offset = -state->stack_size;
 
                        } else if (op->src.reg == CFI_BP && cfa->base == 
state->drap_reg) {
 
@@ -1399,8 +1404,8 @@ static int update_insn_state(struct instruction *insn, 
struct insn_state *state)
                                cfa->base = CFI_BP_INDIRECT;
                                cfa->offset = op->dest.offset;
 
-                               /* save drap so we know when to undefine it */
-                               save_reg(state, op->src.reg, CFI_CFA, 
op->dest.offset);
+                               /* save drap offset so we know when to restore 
it */
+                               state->drap_offset = op->dest.offset;
                        }
 
                        else if (regs[op->src.reg].base == CFI_UNDEFINED) {
@@ -1491,11 +1496,12 @@ static bool insn_state_match(struct instruction *insn, 
struct insn_state *state)
                          insn->sec, insn->offset, state1->type, state2->type);
 
        } else if (state1->drap != state2->drap ||
-                (state1->drap && state1->drap_reg != state2->drap_reg)) {
-               WARN_FUNC("stack state mismatch: drap1=%d(%d) drap2=%d(%d)",
+                (state1->drap && state1->drap_reg != state2->drap_reg) ||
+                (state1->drap && state1->drap_offset != state2->drap_offset)) {
+               WARN_FUNC("stack state mismatch: drap1=%d(%d,%d) 
drap2=%d(%d,%d)",
                          insn->sec, insn->offset,
-                         state1->drap, state1->drap_reg,
-                         state2->drap, state2->drap_reg);
+                         state1->drap, state1->drap_reg, state1->drap_offset,
+                         state2->drap, state2->drap_reg, state2->drap_offset);
 
        } else
                return true;
diff --git a/tools/objtool/check.h b/tools/objtool/check.h
index c9af11f..9f11301 100644
--- a/tools/objtool/check.h
+++ b/tools/objtool/check.h
@@ -32,7 +32,7 @@ struct insn_state {
        unsigned char type;
        bool bp_scratch;
        bool drap;
-       int drap_reg;
+       int drap_reg, drap_offset;
 };
 
 struct instruction {

Reply via email to