Messages in this thread Patch in this message | | | Date | Tue, 17 Mar 2020 18:02:53 +0100 | From | Peter Zijlstra <> | Subject | [PATCH v2 19/19] objtool: Detect loading function pointers across noinstr |
| |
Detect if noinstr text loads functions pointers from regular text, doing so is a definite sign that indirect function calls are unsafe.
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> --- tools/objtool/arch.h | 2 + tools/objtool/check.c | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++ tools/objtool/check.h | 2 - 3 files changed, 71 insertions(+), 1 deletion(-)
--- a/tools/objtool/arch.h +++ b/tools/objtool/arch.h @@ -75,4 +75,6 @@ int arch_decode_instruction(struct elf * bool arch_callee_saved_reg(unsigned char reg); +#define MAX_INSN_SIZE 15 + #endif /* _ARCH_H */ --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -42,6 +42,25 @@ struct instruction *find_insn(struct obj return NULL; } +static struct instruction *find_insn_containing(struct objtool_file *file, struct section *sec, + unsigned long offset) +{ + struct instruction *insn; + unsigned long o; + + for_offset_range(o, offset - MAX_INSN_SIZE - 1, offset) { + hash_for_each_possible(file->insn_hash, insn, hash, sec_offset_hash(sec, o)) { + if (insn->sec != sec) + continue; + + if (insn->offset <= offset && insn->offset + insn->len > offset) + return insn; + } + } + + return NULL; +} + static struct instruction *next_insn_same_sec(struct objtool_file *file, struct instruction *insn) { @@ -2102,6 +2121,29 @@ static int validate_return(struct symbol return 0; } +static int validate_rela(struct instruction *insn, struct insn_state *state) +{ + /* + * Assume that any text rela that's not a CALL or JMP is a load of a + * function pointer. + */ + + switch (insn->type) { + case INSN_CALL: + case INSN_CALL_DYNAMIC: + case INSN_JUMP_CONDITIONAL: + case INSN_JUMP_UNCONDITIONAL: + return 0; + } + + if (state->noinstr && state->instr <= 0 && insn->has_text_rela) { + WARN_FUNC("loading non-noinstr function pointer", insn->sec, insn->offset); + return 1; + } + + return 0; +} + /* * Follow the branch starting at the given instruction, and recursively follow * any other branches (jumps). Meanwhile, track the frame pointer state at @@ -2217,6 +2259,10 @@ static int validate_branch(struct objtoo return 0; } + ret = validate_rela(insn, &state); + if (ret) + return ret; + switch (insn->type) { case INSN_RETURN: @@ -2485,6 +2531,25 @@ static bool ignore_unreachable_insn(stru return false; } +static void prepare_insn_rela(struct objtool_file *file, struct section *sec) +{ + struct instruction *insn; + struct rela *rela; + + if (!sec->rela) + return; + + list_for_each_entry(rela, &sec->rela->rela_list, list) { + insn = find_insn_containing(file, sec, rela->offset); + if (!insn) + continue; + + insn->has_text_rela = rela->sym && rela->sym->sec && + rela->sym->sec->text && + !rela->sym->sec->noinstr; + } +} + static int validate_sec_functions(struct objtool_file *file, struct section *sec) { struct symbol *func; @@ -2507,6 +2572,9 @@ static int validate_sec_functions(struct if (vmlinux) state.noinstr = sec->noinstr; + if (state.noinstr) + prepare_insn_rela(file, sec); + list_for_each_entry(func, &sec->symbol_list, list) { if (func->type != STT_FUNC) continue; --- a/tools/objtool/check.h +++ b/tools/objtool/check.h @@ -36,7 +36,7 @@ struct instruction { enum insn_type type; unsigned long immediate; bool alt_group, dead_end, ignore, hint, save, restore, ignore_alts; - bool retpoline_safe; + bool retpoline_safe, has_text_rela; s8 instr; u8 visited; struct symbol *call_dest;
| |