From 89bc853eae4ad125030ef99f207ba76c2f00a26e Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Tue, 8 Mar 2022 16:30:55 +0100 Subject: objtool: Find unused ENDBR instructions Find all ENDBR instructions which are never referenced and stick them in a section such that the kernel can poison them, sealing the functions from ever being an indirect call target. This removes about 1-in-4 ENDBR instructions. Signed-off-by: Peter Zijlstra (Intel) Acked-by: Josh Poimboeuf Link: https://lore.kernel.org/r/20220308154319.763643193@infradead.org --- tools/objtool/check.c | 69 ++++++++++++++++++++++++++++++++- tools/objtool/include/objtool/objtool.h | 1 + tools/objtool/objtool.c | 1 + 3 files changed, 69 insertions(+), 2 deletions(-) (limited to 'tools/objtool') diff --git a/tools/objtool/check.c b/tools/objtool/check.c index d4cf831edc28..6de5085e3e5a 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -383,6 +383,7 @@ static int decode_instructions(struct objtool_file *file) memset(insn, 0, sizeof(*insn)); INIT_LIST_HEAD(&insn->alts); INIT_LIST_HEAD(&insn->stack_ops); + INIT_LIST_HEAD(&insn->call_node); insn->sec = sec; insn->offset = offset; @@ -420,8 +421,9 @@ static int decode_instructions(struct objtool_file *file) sym_for_each_insn(file, func, insn) { insn->func = func; - if (insn->type == INSN_ENDBR) { + if (insn->type == INSN_ENDBR && list_empty(&insn->call_node)) { if (insn->offset == insn->func->offset) { + list_add_tail(&insn->call_node, &file->endbr_list); file->nr_endbr++; } else { file->nr_endbr_int++; @@ -742,6 +744,58 @@ static int create_retpoline_sites_sections(struct objtool_file *file) return 0; } +static int create_ibt_endbr_seal_sections(struct objtool_file *file) +{ + struct instruction *insn; + struct section *sec; + int idx; + + sec = find_section_by_name(file->elf, ".ibt_endbr_seal"); + if (sec) { + WARN("file already has .ibt_endbr_seal, skipping"); + return 0; + } + + idx = 0; + list_for_each_entry(insn, &file->endbr_list, call_node) + idx++; + + if (stats) { + printf("ibt: ENDBR at function start: %d\n", file->nr_endbr); + printf("ibt: ENDBR inside functions: %d\n", file->nr_endbr_int); + printf("ibt: superfluous ENDBR: %d\n", idx); + } + + if (!idx) + return 0; + + sec = elf_create_section(file->elf, ".ibt_endbr_seal", 0, + sizeof(int), idx); + if (!sec) { + WARN("elf_create_section: .ibt_endbr_seal"); + return -1; + } + + idx = 0; + list_for_each_entry(insn, &file->endbr_list, call_node) { + + int *site = (int *)sec->data->d_buf + idx; + *site = 0; + + if (elf_add_reloc_to_insn(file->elf, sec, + idx * sizeof(int), + R_X86_64_PC32, + insn->sec, insn->offset)) { + WARN("elf_add_reloc_to_insn: .ibt_endbr_seal"); + return -1; + } + + idx++; + } + + return 0; +} + static int create_mcount_loc_sections(struct objtool_file *file) { struct section *sec; @@ -3120,8 +3174,12 @@ validate_ibt_reloc(struct objtool_file *file, struct reloc *reloc) if (!dest) return NULL; - if (dest->type == INSN_ENDBR) + if (dest->type == INSN_ENDBR) { + if (!list_empty(&dest->call_node)) + list_del_init(&dest->call_node); + return NULL; + } if (reloc->sym->static_call_tramp) return NULL; @@ -3860,6 +3918,13 @@ int check(struct objtool_file *file) warnings += ret; } + if (ibt) { + ret = create_ibt_endbr_seal_sections(file); + if (ret < 0) + goto out; + warnings += ret; + } + if (stats) { printf("nr_insns_visited: %ld\n", nr_insns_visited); printf("nr_cfi: %ld\n", nr_cfi); diff --git a/tools/objtool/include/objtool/objtool.h b/tools/objtool/include/objtool/objtool.h index fa3c7fa1ca9c..7a5c13a78f87 100644 --- a/tools/objtool/include/objtool/objtool.h +++ b/tools/objtool/include/objtool/objtool.h @@ -26,6 +26,7 @@ struct objtool_file { struct list_head retpoline_call_list; struct list_head static_call_list; struct list_head mcount_loc_list; + struct list_head endbr_list; bool ignore_unreachables, c_file, hints, rodata; unsigned int nr_endbr; diff --git a/tools/objtool/objtool.c b/tools/objtool/objtool.c index bdf699f6552b..b09946f4e1d6 100644 --- a/tools/objtool/objtool.c +++ b/tools/objtool/objtool.c @@ -128,6 +128,7 @@ struct objtool_file *objtool_open_read(const char *_objname) INIT_LIST_HEAD(&file.retpoline_call_list); INIT_LIST_HEAD(&file.static_call_list); INIT_LIST_HEAD(&file.mcount_loc_list); + INIT_LIST_HEAD(&file.endbr_list); file.c_file = !vmlinux && find_section_by_name(file.elf, ".comment"); file.ignore_unreachables = no_unreachable; file.hints = false; -- cgit v1.2.3