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