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
285 }285 }
286#endif286#endif
287 287
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
288 /*297 /*
289 * struct alt_inst entries. From the header (alternative.h):298 * struct alt_inst entries. From the header (alternative.h):
290 * "Alternative instructions for different CPU types or capabilities"299 * "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
383 memset(insn, 0, sizeof(*insn));383 memset(insn, 0, sizeof(*insn));
384 INIT_LIST_HEAD(&insn->alts);384 INIT_LIST_HEAD(&insn->alts);
385 INIT_LIST_HEAD(&insn->stack_ops);385 INIT_LIST_HEAD(&insn->stack_ops);
386 INIT_LIST_HEAD(&insn->call_node);
386 387
387 insn->sec = sec;388 insn->sec = sec;
388 insn->offset = offset;389 insn->offset = offset;
@@ -420,8 +421,9 @@ static int decode_instructions(struct objtool_file *file) · 31 unmodified lines
420 421
421 sym_for_each_insn(file, func, insn) {422 sym_for_each_insn(file, func, insn) {
422 insn->func = func;423 insn->func = func;
423 if (insn->type == INSN_ENDBR) {424 if (insn->type == INSN_ENDBR && list_empty(&insn->call_node)) {
424 if (insn->offset == insn->func->offset) {425 if (insn->offset == insn->func->offset) {
426 list_add_tail(&insn->call_node, &file->endbr_list);
425 file->nr_endbr++;427 file->nr_endbr++;
426 } else {428 } else {
427 file->nr_endbr_int++;429 file->nr_endbr_int++;
@@ -742,6 +744,58 @@ static int create_retpoline_sites_sections(struct objtool_file *file) · 314 unmodified lines
742 return 0;744 return 0;
743}745}
744 746
747static 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
745static int create_mcount_loc_sections(struct objtool_file *file)799static int create_mcount_loc_sections(struct objtool_file *file)
746{800{
747 struct section *sec;801 struct section *sec;
@@ -3120,8 +3174,12 @@ validate_ibt_reloc(struct objtool_file *file, struct reloc *reloc) · 2372 unmodified lines
3120 if (!dest)3174 if (!dest)
3121 return NULL;3175 return NULL;
3122 3176
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
3124 return NULL;3181 return NULL;
3182 }
3125 3183
3126 if (reloc->sym->static_call_tramp)3184 if (reloc->sym->static_call_tramp)
3127 return NULL;3185 return NULL;
@@ -3860,6 +3918,13 @@ int check(struct objtool_file *file) · 732 unmodified lines
3860 warnings += ret;3918 warnings += ret;
3861 }3919 }
3862 3920
3921 if (ibt) {
3922 ret = create_ibt_endbr_seal_sections(file);
3923 if (ret < 0)
3924 goto out;
3925 warnings += ret;
3926 }
3927
3863 if (stats) {3928 if (stats) {
3864 printf("nr_insns_visited: %ld\n", nr_insns_visited);3929 printf("nr_insns_visited: %ld\n", nr_insns_visited);
3865 printf("nr_cfi: %ld\n", nr_cfi);3930 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
26 struct list_head retpoline_call_list;26 struct list_head retpoline_call_list;
27 struct list_head static_call_list;27 struct list_head static_call_list;
28 struct list_head mcount_loc_list;28 struct list_head mcount_loc_list;
29 struct list_head endbr_list;
29 bool ignore_unreachables, c_file, hints, rodata;30 bool ignore_unreachables, c_file, hints, rodata;
30 31
31 unsigned int nr_endbr;32 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
128 INIT_LIST_HEAD(&file.retpoline_call_list);128 INIT_LIST_HEAD(&file.retpoline_call_list);
129 INIT_LIST_HEAD(&file.static_call_list);129 INIT_LIST_HEAD(&file.static_call_list);
130 INIT_LIST_HEAD(&file.mcount_loc_list);130 INIT_LIST_HEAD(&file.mcount_loc_list);
131 INIT_LIST_HEAD(&file.endbr_list);
131 file.c_file = !vmlinux && find_section_by_name(file.elf, ".comment");132 file.c_file = !vmlinux && find_section_by_name(file.elf, ".comment");
132 file.ignore_unreachables = no_unreachable;133 file.ignore_unreachables = no_unreachable;
133 file.hints = false;134 file.hints = false;