Skip to content

Commit 89bc853

Browse files
author
Peter Zijlstra
committed
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) <[email protected]> Acked-by: Josh Poimboeuf <[email protected]> Link: https://lore.kernel.org/r/[email protected]
1 parent 08f87a9 commit 89bc853

File tree

4 files changed

+78
-2
lines changed

4 files changed

+78
-2
lines changed

arch/x86/kernel/vmlinux.lds.S

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,15 @@ SECTIONS
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

Lines changed: 67 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,7 @@ static int decode_instructions(struct objtool_file *file)
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)
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)
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)
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)
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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ struct objtool_file {
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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ struct objtool_file *objtool_open_read(const char *_objname)
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;

0 commit comments

Comments
 (0)