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.
Showing the changes to tools/objtool (3 of 4 files). Show all
Unified · Split
| @@ -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 | ||
| 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 | |||
| 745 | static int create_mcount_loc_sections(struct objtool_file *file) | 799 | static 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); |
| @@ -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; |
| @@ -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; |