History

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.

Peter Zijlstra committed 4 years ago

Commit 89bc8531 parent 08f87a94 files · +78 −2Browse files

Unified · Split

4 files
  1. arch/x86/kernel/vmlinux.lds.S +9 −0
  2. tools/objtool/check.c +67 −2
  3. tools/objtool/include/objtool/objtool.h +1 −0
  4. tools/objtool/objtool.c +1 −0
arch/x86/kernel/vmlinux.lds.S+9−0
@@ -285,6 +285,15 @@ SECTIONS · 284 unmodified lines
285285 }
286286 #endif
287287
288+#ifdef CONFIG_X86_KERNEL_IBT
289+ . = ALIGN(8);
290+ .ibt_endbr_seal : AT(ADDR(.ibt_endbr_seal) - LOAD_OFFSET) {
291+ __ibt_endbr_seal = .;
292+ *(.ibt_endbr_seal)
293+ __ibt_endbr_seal_end = .;
294+ }
295+#endif
296+
288297 /*
289298 * struct alt_inst entries. From the header (alternative.h):
290299 * "Alternative instructions for different CPU types or capabilities"
tools/objtool/check.c+67−2
@@ -383,6 +383,7 @@ static int decode_instructions(struct objtool_file *file) · 382 unmodified lines
383383 memset(insn, 0, sizeof(*insn));
384384 INIT_LIST_HEAD(&insn->alts);
385385 INIT_LIST_HEAD(&insn->stack_ops);
386+ INIT_LIST_HEAD(&insn->call_node);
386387
387388 insn->sec = sec;
388389 insn->offset = offset;
@@ -420,8 +421,9 @@ static int decode_instructions(struct objtool_file *file) · 31 unmodified lines
420421
421422 sym_for_each_insn(file, func, insn) {
422423 insn->func = func;
423− if (insn->type == INSN_ENDBR) {
424+ if (insn->type == INSN_ENDBR && list_empty(&insn->call_node)) {
424425 if (insn->offset == insn->func->offset) {
426+ list_add_tail(&insn->call_node, &file->endbr_list);
425427 file->nr_endbr++;
426428 } else {
427429 file->nr_endbr_int++;
@@ -742,6 +744,58 @@ static int create_retpoline_sites_sections(struct objtool_file *file) · 314 unmodified lines
742744 return 0;
743745 }
744746
747+static int create_ibt_endbr_seal_sections(struct objtool_file *file)
748+{
749+ struct instruction *insn;
750+ struct section *sec;
751+ int idx;
752+
753+ sec = find_section_by_name(file->elf, ".ibt_endbr_seal");
754+ if (sec) {
755+ WARN("file already has .ibt_endbr_seal, skipping");
756+ return 0;
757+ }
758+
759+ idx = 0;
760+ list_for_each_entry(insn, &file->endbr_list, call_node)
761+ idx++;
762+
763+ if (stats) {
764+ printf("ibt: ENDBR at function start: %d\n", file->nr_endbr);
765+ printf("ibt: ENDBR inside functions: %d\n", file->nr_endbr_int);
766+ printf("ibt: superfluous ENDBR: %d\n", idx);
767+ }
768+
769+ if (!idx)
770+ return 0;
771+
772+ sec = elf_create_section(file->elf, ".ibt_endbr_seal", 0,
773+ sizeof(int), idx);
774+ if (!sec) {
775+ WARN("elf_create_section: .ibt_endbr_seal");
776+ return -1;
777+ }
778+
779+ idx = 0;
780+ list_for_each_entry(insn, &file->endbr_list, call_node) {
781+
782+ int *site = (int *)sec->data->d_buf + idx;
783+ *site = 0;
784+
785+ if (elf_add_reloc_to_insn(file->elf, sec,
786+ idx * sizeof(int),
787+ R_X86_64_PC32,
788+ insn->sec, insn->offset)) {
789+ WARN("elf_add_reloc_to_insn: .ibt_endbr_seal");
790+ return -1;
791+ }
792+
793+ idx++;
794+ }
795+
796+ return 0;
797+}
798+
745799 static int create_mcount_loc_sections(struct objtool_file *file)
746800 {
747801 struct section *sec;
@@ -3120,8 +3174,12 @@ validate_ibt_reloc(struct objtool_file *file, struct reloc *reloc) · 2372 unmodified lines
31203174 if (!dest)
31213175 return NULL;
31223176
3123− if (dest->type == INSN_ENDBR)
3177+ if (dest->type == INSN_ENDBR) {
3178+ if (!list_empty(&dest->call_node))
3179+ list_del_init(&dest->call_node);
3180+
31243181 return NULL;
3182+ }
31253183
31263184 if (reloc->sym->static_call_tramp)
31273185 return NULL;
@@ -3860,6 +3918,13 @@ int check(struct objtool_file *file) · 732 unmodified lines
38603918 warnings += ret;
38613919 }
38623920
3921+ if (ibt) {
3922+ ret = create_ibt_endbr_seal_sections(file);
3923+ if (ret < 0)
3924+ goto out;
3925+ warnings += ret;
3926+ }
3927+
38633928 if (stats) {
38643929 printf("nr_insns_visited: %ld\n", nr_insns_visited);
38653930 printf("nr_cfi: %ld\n", nr_cfi);
tools/objtool/include/objtool/objtool.h+1−0
@@ -26,6 +26,7 @@ struct objtool_file { · 25 unmodified lines
2626 struct list_head retpoline_call_list;
2727 struct list_head static_call_list;
2828 struct list_head mcount_loc_list;
29+ struct list_head endbr_list;
2930 bool ignore_unreachables, c_file, hints, rodata;
3031
3132 unsigned int nr_endbr;
tools/objtool/objtool.c+1−0
@@ -128,6 +128,7 @@ struct objtool_file *objtool_open_read(const char *_objname) · 127 unmodified lines
128128 INIT_LIST_HEAD(&file.retpoline_call_list);
129129 INIT_LIST_HEAD(&file.static_call_list);
130130 INIT_LIST_HEAD(&file.mcount_loc_list);
131+ INIT_LIST_HEAD(&file.endbr_list);
131132 file.c_file = !vmlinux && find_section_by_name(file.elf, ".comment");
132133 file.ignore_unreachables = no_unreachable;
133134 file.hints = false;