|
Miroslav Benes |
7a3804 |
From: Peter Zijlstra <peterz@infradead.org>
|
|
Miroslav Benes |
7a3804 |
Date: Wed, 6 Mar 2019 12:58:15 +0100
|
|
Miroslav Benes |
7a3804 |
Subject: objtool: Fix sibling call detection
|
|
Miroslav Benes |
7a3804 |
Git-commit: 54262aa2830151f89699fa8a6c5aa05f0992e672
|
|
Miroslav Benes |
7a3804 |
Patch-mainline: v5.2-rc1
|
|
Miroslav Benes |
7a3804 |
References: bsc#1202396
|
|
Miroslav Benes |
7a3804 |
|
|
Miroslav Benes |
7a3804 |
It turned out that we failed to detect some sibling calls;
|
|
Miroslav Benes |
7a3804 |
specifically those without relocation records; like:
|
|
Miroslav Benes |
7a3804 |
|
|
Miroslav Benes |
7a3804 |
$ ./objdump-func.sh defconfig-build/mm/kasan/generic.o __asan_loadN
|
|
Miroslav Benes |
7a3804 |
0000 0000000000000840 <__asan_loadN>:
|
|
Miroslav Benes |
7a3804 |
0000 840: 48 8b 0c 24 mov (%rsp),%rcx
|
|
Miroslav Benes |
7a3804 |
0004 844: 31 d2 xor %edx,%edx
|
|
Miroslav Benes |
7a3804 |
0006 846: e9 45 fe ff ff jmpq 690 <check_memory_region>
|
|
Miroslav Benes |
7a3804 |
|
|
Miroslav Benes |
7a3804 |
So extend the cross-function jump to also consider those that are not
|
|
Miroslav Benes |
7a3804 |
between known (or newly detected) parent/child functions, as
|
|
Miroslav Benes |
7a3804 |
sibling-cals when they jump to the start of the function.
|
|
Miroslav Benes |
7a3804 |
|
|
Miroslav Benes |
7a3804 |
The second part of that condition is to deal with random jumps to the
|
|
Miroslav Benes |
7a3804 |
middle of other function, as can be found in
|
|
Miroslav Benes |
7a3804 |
arch/x86/lib/copy_user_64.S for example.
|
|
Miroslav Benes |
7a3804 |
|
|
Miroslav Benes |
7a3804 |
This then (with later patches applied) makes the above recognise the
|
|
Miroslav Benes |
7a3804 |
sibling call:
|
|
Miroslav Benes |
7a3804 |
|
|
Miroslav Benes |
7a3804 |
mm/kasan/generic.o: warning: objtool: __asan_loadN()+0x6: call to check_memory_region() with UACCESS enabled
|
|
Miroslav Benes |
7a3804 |
|
|
Miroslav Benes |
7a3804 |
Also make sure to set insn->call_dest for sibling calls so we can know
|
|
Miroslav Benes |
7a3804 |
who we're calling. This is useful information when printing validation
|
|
Miroslav Benes |
7a3804 |
warnings later.
|
|
Miroslav Benes |
7a3804 |
|
|
Miroslav Benes |
7a3804 |
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
|
|
Miroslav Benes |
7a3804 |
Acked-by: Josh Poimboeuf <jpoimboe@redhat.com>
|
|
Miroslav Benes |
7a3804 |
Cc: Borislav Petkov <bp@alien8.de>
|
|
Miroslav Benes |
7a3804 |
Cc: Linus Torvalds <torvalds@linux-foundation.org>
|
|
Miroslav Benes |
7a3804 |
Cc: Peter Zijlstra <peterz@infradead.org>
|
|
Miroslav Benes |
7a3804 |
Cc: Thomas Gleixner <tglx@linutronix.de>
|
|
Miroslav Benes |
7a3804 |
Signed-off-by: Ingo Molnar <mingo@kernel.org>
|
|
Miroslav Benes |
7a3804 |
Acked-by: Miroslav Benes <mbenes@suse.cz>
|
|
Miroslav Benes |
7a3804 |
---
|
|
Miroslav Benes |
7a3804 |
tools/objtool/check.c | 86 ++++++++++++++++++++++++++++++++-------------------
|
|
Miroslav Benes |
7a3804 |
1 file changed, 55 insertions(+), 31 deletions(-)
|
|
Miroslav Benes |
7a3804 |
|
|
Miroslav Benes |
7a3804 |
diff --git a/tools/objtool/check.c b/tools/objtool/check.c
|
|
Miroslav Benes |
7a3804 |
index 5264a305d658..8118361295dd 100644
|
|
Miroslav Benes |
7a3804 |
--- a/tools/objtool/check.c
|
|
Miroslav Benes |
7a3804 |
+++ b/tools/objtool/check.c
|
|
Miroslav Benes |
7a3804 |
@@ -515,7 +515,8 @@ static int add_jump_destinations(struct objtool_file *file)
|
|
Miroslav Benes |
7a3804 |
continue;
|
|
Miroslav Benes |
7a3804 |
} else {
|
|
Miroslav Benes |
7a3804 |
/* sibling call */
|
|
Miroslav Benes |
7a3804 |
- insn->jump_dest = 0;
|
|
Miroslav Benes |
7a3804 |
+ insn->call_dest = rela->sym;
|
|
Miroslav Benes |
7a3804 |
+ insn->jump_dest = NULL;
|
|
Miroslav Benes |
7a3804 |
continue;
|
|
Miroslav Benes |
7a3804 |
}
|
|
Miroslav Benes |
7a3804 |
|
|
Miroslav Benes |
7a3804 |
@@ -537,25 +538,38 @@ static int add_jump_destinations(struct objtool_file *file)
|
|
Miroslav Benes |
7a3804 |
}
|
|
Miroslav Benes |
7a3804 |
|
|
Miroslav Benes |
7a3804 |
/*
|
|
Miroslav Benes |
7a3804 |
- * For GCC 8+, create parent/child links for any cold
|
|
Miroslav Benes |
7a3804 |
- * subfunctions. This is _mostly_ redundant with a similar
|
|
Miroslav Benes |
7a3804 |
- * initialization in read_symbols().
|
|
Miroslav Benes |
7a3804 |
- *
|
|
Miroslav Benes |
7a3804 |
- * If a function has aliases, we want the *first* such function
|
|
Miroslav Benes |
7a3804 |
- * in the symbol table to be the subfunction's parent. In that
|
|
Miroslav Benes |
7a3804 |
- * case we overwrite the initialization done in read_symbols().
|
|
Miroslav Benes |
7a3804 |
- *
|
|
Miroslav Benes |
7a3804 |
- * However this code can't completely replace the
|
|
Miroslav Benes |
7a3804 |
- * read_symbols() code because this doesn't detect the case
|
|
Miroslav Benes |
7a3804 |
- * where the parent function's only reference to a subfunction
|
|
Miroslav Benes |
7a3804 |
- * is through a switch table.
|
|
Miroslav Benes |
7a3804 |
+ * Cross-function jump.
|
|
Miroslav Benes |
7a3804 |
*/
|
|
Miroslav Benes |
7a3804 |
if (insn->func && insn->jump_dest->func &&
|
|
Miroslav Benes |
7a3804 |
- insn->func != insn->jump_dest->func &&
|
|
Miroslav Benes |
7a3804 |
- !strstr(insn->func->name, ".cold.") &&
|
|
Miroslav Benes |
7a3804 |
- strstr(insn->jump_dest->func->name, ".cold.")) {
|
|
Miroslav Benes |
7a3804 |
- insn->func->cfunc = insn->jump_dest->func;
|
|
Miroslav Benes |
7a3804 |
- insn->jump_dest->func->pfunc = insn->func;
|
|
Miroslav Benes |
7a3804 |
+ insn->func != insn->jump_dest->func) {
|
|
Miroslav Benes |
7a3804 |
+
|
|
Miroslav Benes |
7a3804 |
+ /*
|
|
Miroslav Benes |
7a3804 |
+ * For GCC 8+, create parent/child links for any cold
|
|
Miroslav Benes |
7a3804 |
+ * subfunctions. This is _mostly_ redundant with a
|
|
Miroslav Benes |
7a3804 |
+ * similar initialization in read_symbols().
|
|
Miroslav Benes |
7a3804 |
+ *
|
|
Miroslav Benes |
7a3804 |
+ * If a function has aliases, we want the *first* such
|
|
Miroslav Benes |
7a3804 |
+ * function in the symbol table to be the subfunction's
|
|
Miroslav Benes |
7a3804 |
+ * parent. In that case we overwrite the
|
|
Miroslav Benes |
7a3804 |
+ * initialization done in read_symbols().
|
|
Miroslav Benes |
7a3804 |
+ *
|
|
Miroslav Benes |
7a3804 |
+ * However this code can't completely replace the
|
|
Miroslav Benes |
7a3804 |
+ * read_symbols() code because this doesn't detect the
|
|
Miroslav Benes |
7a3804 |
+ * case where the parent function's only reference to a
|
|
Miroslav Benes |
7a3804 |
+ * subfunction is through a switch table.
|
|
Miroslav Benes |
7a3804 |
+ */
|
|
Miroslav Benes |
7a3804 |
+ if (!strstr(insn->func->name, ".cold.") &&
|
|
Miroslav Benes |
7a3804 |
+ strstr(insn->jump_dest->func->name, ".cold.")) {
|
|
Miroslav Benes |
7a3804 |
+ insn->func->cfunc = insn->jump_dest->func;
|
|
Miroslav Benes |
7a3804 |
+ insn->jump_dest->func->pfunc = insn->func;
|
|
Miroslav Benes |
7a3804 |
+
|
|
Miroslav Benes |
7a3804 |
+ } else if (insn->jump_dest->func->pfunc != insn->func->pfunc &&
|
|
Miroslav Benes |
7a3804 |
+ insn->jump_dest->offset == insn->jump_dest->func->offset) {
|
|
Miroslav Benes |
7a3804 |
+
|
|
Miroslav Benes |
7a3804 |
+ /* sibling class */
|
|
Miroslav Benes |
7a3804 |
+ insn->call_dest = insn->jump_dest->func;
|
|
Miroslav Benes |
7a3804 |
+ insn->jump_dest = NULL;
|
|
Miroslav Benes |
7a3804 |
+ }
|
|
Miroslav Benes |
7a3804 |
}
|
|
Miroslav Benes |
7a3804 |
}
|
|
Miroslav Benes |
7a3804 |
|
|
Miroslav Benes |
7a3804 |
@@ -1785,6 +1799,17 @@ static bool insn_state_match(struct instruction *insn, struct insn_state *state)
|
|
Miroslav Benes |
7a3804 |
return false;
|
|
Miroslav Benes |
7a3804 |
}
|
|
Miroslav Benes |
7a3804 |
|
|
Miroslav Benes |
7a3804 |
+static int validate_sibling_call(struct instruction *insn, struct insn_state *state)
|
|
Miroslav Benes |
7a3804 |
+{
|
|
Miroslav Benes |
7a3804 |
+ if (has_modified_stack_frame(state)) {
|
|
Miroslav Benes |
7a3804 |
+ WARN_FUNC("sibling call from callable instruction with modified stack frame",
|
|
Miroslav Benes |
7a3804 |
+ insn->sec, insn->offset);
|
|
Miroslav Benes |
7a3804 |
+ return 1;
|
|
Miroslav Benes |
7a3804 |
+ }
|
|
Miroslav Benes |
7a3804 |
+
|
|
Miroslav Benes |
7a3804 |
+ return 0;
|
|
Miroslav Benes |
7a3804 |
+}
|
|
Miroslav Benes |
7a3804 |
+
|
|
Miroslav Benes |
7a3804 |
/*
|
|
Miroslav Benes |
7a3804 |
* Follow the branch starting at the given instruction, and recursively follow
|
|
Miroslav Benes |
7a3804 |
* any other branches (jumps). Meanwhile, track the frame pointer state at
|
|
Miroslav Benes |
7a3804 |
@@ -1935,9 +1960,14 @@ static int validate_branch(struct objtool_file *file, struct instruction *first,
|
|
Miroslav Benes |
7a3804 |
|
|
Miroslav Benes |
7a3804 |
case INSN_JUMP_CONDITIONAL:
|
|
Miroslav Benes |
7a3804 |
case INSN_JUMP_UNCONDITIONAL:
|
|
Miroslav Benes |
7a3804 |
- if (insn->jump_dest &&
|
|
Miroslav Benes |
7a3804 |
- (!func || !insn->jump_dest->func ||
|
|
Miroslav Benes |
7a3804 |
- insn->jump_dest->func->pfunc == func)) {
|
|
Miroslav Benes |
7a3804 |
+ if (func && !insn->jump_dest) {
|
|
Miroslav Benes |
7a3804 |
+ ret = validate_sibling_call(insn, &state);
|
|
Miroslav Benes |
7a3804 |
+ if (ret)
|
|
Miroslav Benes |
7a3804 |
+ return ret;
|
|
Miroslav Benes |
7a3804 |
+
|
|
Miroslav Benes |
7a3804 |
+ } else if (insn->jump_dest &&
|
|
Miroslav Benes |
7a3804 |
+ (!func || !insn->jump_dest->func ||
|
|
Miroslav Benes |
7a3804 |
+ insn->jump_dest->func->pfunc == func)) {
|
|
Miroslav Benes |
7a3804 |
ret = validate_branch(file, insn->jump_dest,
|
|
Miroslav Benes |
7a3804 |
state);
|
|
Miroslav Benes |
7a3804 |
if (ret) {
|
|
Miroslav Benes |
7a3804 |
@@ -1945,11 +1975,6 @@ static int validate_branch(struct objtool_file *file, struct instruction *first,
|
|
Miroslav Benes |
7a3804 |
BT_FUNC("(branch)", insn);
|
|
Miroslav Benes |
7a3804 |
return ret;
|
|
Miroslav Benes |
7a3804 |
}
|
|
Miroslav Benes |
7a3804 |
-
|
|
Miroslav Benes |
7a3804 |
- } else if (func && has_modified_stack_frame(&state)) {
|
|
Miroslav Benes |
7a3804 |
- WARN_FUNC("sibling call from callable instruction with modified stack frame",
|
|
Miroslav Benes |
7a3804 |
- sec, insn->offset);
|
|
Miroslav Benes |
7a3804 |
- return 1;
|
|
Miroslav Benes |
7a3804 |
}
|
|
Miroslav Benes |
7a3804 |
|
|
Miroslav Benes |
7a3804 |
if (insn->type == INSN_JUMP_UNCONDITIONAL)
|
|
Miroslav Benes |
7a3804 |
@@ -1958,11 +1983,10 @@ static int validate_branch(struct objtool_file *file, struct instruction *first,
|
|
Miroslav Benes |
7a3804 |
break;
|
|
Miroslav Benes |
7a3804 |
|
|
Miroslav Benes |
7a3804 |
case INSN_JUMP_DYNAMIC:
|
|
Miroslav Benes |
7a3804 |
- if (func && list_empty(&insn->alts) &&
|
|
Miroslav Benes |
7a3804 |
- has_modified_stack_frame(&state)) {
|
|
Miroslav Benes |
7a3804 |
- WARN_FUNC("sibling call from callable instruction with modified stack frame",
|
|
Miroslav Benes |
7a3804 |
- sec, insn->offset);
|
|
Miroslav Benes |
7a3804 |
- return 1;
|
|
Miroslav Benes |
7a3804 |
+ if (func && list_empty(&insn->alts)) {
|
|
Miroslav Benes |
7a3804 |
+ ret = validate_sibling_call(insn, &state);
|
|
Miroslav Benes |
7a3804 |
+ if (ret)
|
|
Miroslav Benes |
7a3804 |
+ return ret;
|
|
Miroslav Benes |
7a3804 |
}
|
|
Miroslav Benes |
7a3804 |
|
|
Miroslav Benes |
7a3804 |
return 0;
|
|
Miroslav Benes |
7a3804 |
|