[tip:core/urgent] objtool: Detect falling through to the next function

From: tip-bot for Josh Poimboeuf
Date: Sat Apr 16 2016 - 05:32:01 EST


Commit-ID: b1547d3101e74e809b9790174b27f1080747b009
Gitweb: http://git.kernel.org/tip/b1547d3101e74e809b9790174b27f1080747b009
Author: Josh Poimboeuf <jpoimboe@xxxxxxxxxx>
AuthorDate: Fri, 15 Apr 2016 09:17:10 -0500
Committer: Ingo Molnar <mingo@xxxxxxxxxx>
CommitDate: Sat, 16 Apr 2016 11:14:17 +0200

objtool: Detect falling through to the next function

There are several cases in compiled C code where a function may not
return at the end, and may instead fall through to the next function.

That may indicate a bug in the code, or a gcc bug, or even an objtool
bug. But in each case, objtool reports an unhelpful warning, something
like:

drivers/scsi/qla2xxx/qla_attr.o: warning: objtool: qla2x00_get_fc_host_stats()+0x0: duplicate frame pointer save
drivers/scsi/qla2xxx/qla_attr.o: warning: objtool: qla2x00_get_fc_host_stats()+0x0: frame pointer state mismatch

Detect this situation and print a more useful error message:

drivers/scsi/qla2xxx/qla_attr.o: warning: objtool: qla2x00_get_host_fabric_name() falls through to next function qla2x00_get_starget_node_name()

Also add some information about this warning and its potential causes to
the documentation.

Reported-by: kbuild test robot <fengguang.wu@xxxxxxxxx>
Signed-off-by: Josh Poimboeuf <jpoimboe@xxxxxxxxxx>
Cc: Alexander Shishkin <alexander.shishkin@xxxxxxxxxxxxxxx>
Cc: Andy Lutomirski <luto@xxxxxxxxxxxxxx>
Cc: Arnaldo Carvalho de Melo <acme@xxxxxxxxxx>
Cc: Borislav Petkov <bp@xxxxxxxxx>
Cc: Brian Gerst <brgerst@xxxxxxxxx>
Cc: Denys Vlasenko <dvlasenk@xxxxxxxxxx>
Cc: H. Peter Anvin <hpa@xxxxxxxxx>
Cc: Jiri Olsa <jolsa@xxxxxxxxxx>
Cc: Linus Torvalds <torvalds@xxxxxxxxxxxxxxxxxxxx>
Cc: Peter Zijlstra <peterz@xxxxxxxxxxxxx>
Cc: Thomas Gleixner <tglx@xxxxxxxxxxxxx>
Link: http://lkml.kernel.org/r/caa4ec6c687931db805e692d4e4bf06cd87d33e6.1460729697.git.jpoimboe@xxxxxxxxxx
Signed-off-by: Ingo Molnar <mingo@xxxxxxxxxx>
---
tools/objtool/Documentation/stack-validation.txt | 38 +++++++++++++++-----
tools/objtool/builtin-check.c | 46 ++++++++++++++++--------
2 files changed, 61 insertions(+), 23 deletions(-)

diff --git a/tools/objtool/Documentation/stack-validation.txt b/tools/objtool/Documentation/stack-validation.txt
index 5a95896..55a60d3 100644
--- a/tools/objtool/Documentation/stack-validation.txt
+++ b/tools/objtool/Documentation/stack-validation.txt
@@ -299,18 +299,38 @@ they mean, and suggestions for how to fix them.
Errors in .c files
------------------

-If you're getting an objtool error in a compiled .c file, chances are
-the file uses an asm() statement which has a "call" instruction. An
-asm() statement with a call instruction must declare the use of the
-stack pointer in its output operand. For example, on x86_64:
+1. c_file.o: warning: objtool: funcA() falls through to next function funcB()

- register void *__sp asm("rsp");
- asm volatile("call func" : "+r" (__sp));
+ This means that funcA() doesn't end with a return instruction or an
+ unconditional jump, and that objtool has determined that the function
+ can fall through into the next function. There could be different
+ reasons for this:

-Otherwise the stack frame may not get created before the call.
+ 1) funcA()'s last instruction is a call to a "noreturn" function like
+ panic(). In this case the noreturn function needs to be added to
+ objtool's hard-coded global_noreturns array. Feel free to bug the
+ objtool maintainer, or you can submit a patch.

-Another possible cause for errors in C code is if the Makefile removes
--fno-omit-frame-pointer or adds -fomit-frame-pointer to the gcc options.
+ 2) funcA() uses the unreachable() annotation in a section of code
+ that is actually reachable.
+
+ 3) If funcA() calls an inline function, the object code for funcA()
+ might be corrupt due to a gcc bug. For more details, see:
+ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=70646
+
+2. If you're getting any other objtool error in a compiled .c file, it
+ may be because the file uses an asm() statement which has a "call"
+ instruction. An asm() statement with a call instruction must declare
+ the use of the stack pointer in its output operand. For example, on
+ x86_64:
+
+ register void *__sp asm("rsp");
+ asm volatile("call func" : "+r" (__sp));
+
+ Otherwise the stack frame may not get created before the call.
+
+3. Another possible cause for errors in C code is if the Makefile removes
+ -fno-omit-frame-pointer or adds -fomit-frame-pointer to the gcc options.

Also see the above section for .S file errors for more information what
the individual error messages mean.
diff --git a/tools/objtool/builtin-check.c b/tools/objtool/builtin-check.c
index 157a0f9..e8a1e69 100644
--- a/tools/objtool/builtin-check.c
+++ b/tools/objtool/builtin-check.c
@@ -54,6 +54,7 @@ struct instruction {
struct symbol *call_dest;
struct instruction *jump_dest;
struct list_head alts;
+ struct symbol *func;
};

struct alternative {
@@ -66,7 +67,7 @@ struct objtool_file {
struct list_head insn_list;
DECLARE_HASHTABLE(insn_hash, 16);
struct section *rodata, *whitelist;
- bool ignore_unreachables;
+ bool ignore_unreachables, c_file;
};

const char *objname;
@@ -229,7 +230,7 @@ static int __dead_end_function(struct objtool_file *file, struct symbol *func,
}
}

- if (insn->type == INSN_JUMP_DYNAMIC)
+ if (insn->type == INSN_JUMP_DYNAMIC && list_empty(&insn->alts))
/* sibling call */
return 0;
}
@@ -249,6 +250,7 @@ static int dead_end_function(struct objtool_file *file, struct symbol *func)
static int decode_instructions(struct objtool_file *file)
{
struct section *sec;
+ struct symbol *func;
unsigned long offset;
struct instruction *insn;
int ret;
@@ -282,6 +284,21 @@ static int decode_instructions(struct objtool_file *file)
hash_add(file->insn_hash, &insn->hash, insn->offset);
list_add_tail(&insn->list, &file->insn_list);
}
+
+ list_for_each_entry(func, &sec->symbol_list, list) {
+ if (func->type != STT_FUNC)
+ continue;
+
+ if (!find_insn(file, sec, func->offset)) {
+ WARN("%s(): can't find starting instruction",
+ func->name);
+ return -1;
+ }
+
+ func_for_each_insn(file, func, insn)
+ if (!insn->func)
+ insn->func = func;
+ }
}

return 0;
@@ -824,6 +841,7 @@ static int validate_branch(struct objtool_file *file,
struct alternative *alt;
struct instruction *insn;
struct section *sec;
+ struct symbol *func = NULL;
unsigned char state;
int ret;

@@ -838,6 +856,16 @@ static int validate_branch(struct objtool_file *file,
}

while (1) {
+ if (file->c_file && insn->func) {
+ if (func && func != insn->func) {
+ WARN("%s() falls through to next function %s()",
+ func->name, insn->func->name);
+ return 1;
+ }
+
+ func = insn->func;
+ }
+
if (insn->visited) {
if (frame_state(insn->state) != frame_state(state)) {
WARN_FUNC("frame pointer state mismatch",
@@ -848,13 +876,6 @@ static int validate_branch(struct objtool_file *file,
return 0;
}

- /*
- * Catch a rare case where a noreturn function falls through to
- * the next function.
- */
- if (is_fentry_call(insn) && (state & STATE_FENTRY))
- return 0;
-
insn->visited = true;
insn->state = state;

@@ -1060,12 +1081,8 @@ static int validate_functions(struct objtool_file *file)
continue;

insn = find_insn(file, sec, func->offset);
- if (!insn) {
- WARN("%s(): can't find starting instruction",
- func->name);
- warnings++;
+ if (!insn)
continue;
- }

ret = validate_branch(file, insn, 0);
warnings += ret;
@@ -1162,6 +1179,7 @@ int cmd_check(int argc, const char **argv)
file.whitelist = find_section_by_name(file.elf, "__func_stack_frame_non_standard");
file.rodata = find_section_by_name(file.elf, ".rodata");
file.ignore_unreachables = false;
+ file.c_file = find_section_by_name(file.elf, ".comment");

ret = decode_sections(&file);
if (ret < 0)