summaryrefslogtreecommitdiffstats
path: root/tools/objtool
diff options
context:
space:
mode:
authorJosh Poimboeuf <jpoimboe@redhat.com>2017-08-10 16:37:26 -0500
committerIngo Molnar <mingo@kernel.org>2017-08-11 14:06:15 +0200
commitbf4d1a83758368c842c94cab9661a75ca98bc848 (patch)
tree8e6787d0a5c44d6c997f849efde293dea00aeee6 /tools/objtool
parent12b25729a194198e3c4289adaddc4115b10b094e (diff)
downloadop-kernel-dev-bf4d1a83758368c842c94cab9661a75ca98bc848.zip
op-kernel-dev-bf4d1a83758368c842c94cab9661a75ca98bc848.tar.gz
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 <arnd@arndb.de> Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com> Cc: Andy Lutomirski <luto@kernel.org> Cc: Borislav Petkov <bp@alien8.de> Cc: Brian Gerst <brgerst@gmail.com> Cc: Denys Vlasenko <dvlasenk@redhat.com> Cc: H. Peter Anvin <hpa@zytor.com> Cc: Linus Torvalds <torvalds@linux-foundation.org> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Thomas Gleixner <tglx@linutronix.de> Fixes: baa41469a7b9 ("objtool: Implement stack validation 2.0") Link: http://lkml.kernel.org/r/3da23a6d4c5b3c1e21fc2ccc21a73941b97ff20a.1502401017.git.jpoimboe@redhat.com Signed-off-by: Ingo Molnar <mingo@kernel.org>
Diffstat (limited to 'tools/objtool')
-rw-r--r--tools/objtool/check.c54
-rw-r--r--tools/objtool/check.h2
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 {
OpenPOWER on IntegriCloud