From ed1cb76ebdeb88cf0603b9cb543f43f09ab704a1 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Thu, 3 Oct 2024 17:31:10 -0700 Subject: [PATCH 1/5] objtool: Detect non-relocated text references When kernel IBT is enabled, objtool detects all text references in order to determine which functions can be indirectly branched to. In text, such references look like one of the following: mov $0x0,%rax R_X86_64_32S .init.text+0x7e0a0 lea 0x0(%rip),%rax R_X86_64_PC32 autoremove_wake_function-0x4 Either way the function pointer is denoted by a relocation, so objtool just reads that. However there are some "lea xxx(%rip)" cases which don't use relocations because they're referencing code in the same translation unit. Objtool doesn't have visibility to those. The only currently known instances of that are a few hand-coded asm text references which don't actually need ENDBR. So it's not actually a problem at the moment. However if we enable -fpie, the compiler would start generating them and there would definitely be bugs in the IBT sealing. Detect non-relocated text references and handle them appropriately. [ Note: I removed the manual static_call_tramp check -- that should already be handled by the noendbr check. ] Reported-by: Ard Biesheuvel Tested-by: Ard Biesheuvel Acked-by: Peter Zijlstra (Intel) Signed-off-by: Josh Poimboeuf --- arch/x86/kernel/acpi/wakeup_64.S | 1 + arch/x86/kernel/head_64.S | 1 + tools/objtool/arch/x86/decode.c | 15 ++-- tools/objtool/check.c | 112 +++++++++++++++------------ tools/objtool/include/objtool/arch.h | 1 + 5 files changed, 77 insertions(+), 53 deletions(-) diff --git a/arch/x86/kernel/acpi/wakeup_64.S b/arch/x86/kernel/acpi/wakeup_64.S index 94ff83f3d3fe..b200a193beeb 100644 --- a/arch/x86/kernel/acpi/wakeup_64.S +++ b/arch/x86/kernel/acpi/wakeup_64.S @@ -87,6 +87,7 @@ SYM_FUNC_START(do_suspend_lowlevel) .align 4 .Lresume_point: + ANNOTATE_NOENDBR /* We don't restore %rax, it must be 0 anyway */ movq $saved_context, %rax movq saved_context_cr4(%rax), %rbx diff --git a/arch/x86/kernel/head_64.S b/arch/x86/kernel/head_64.S index 16752b8dfa89..56163e2124cf 100644 --- a/arch/x86/kernel/head_64.S +++ b/arch/x86/kernel/head_64.S @@ -77,6 +77,7 @@ SYM_CODE_START_NOALIGN(startup_64) lretq .Lon_kernel_cs: + ANNOTATE_NOENDBR UNWIND_HINT_END_OF_STACK #ifdef CONFIG_AMD_MEM_ENCRYPT diff --git a/tools/objtool/arch/x86/decode.c b/tools/objtool/arch/x86/decode.c index ed6bff0e01dc..fe1362c34564 100644 --- a/tools/objtool/arch/x86/decode.c +++ b/tools/objtool/arch/x86/decode.c @@ -456,10 +456,6 @@ int arch_decode_instruction(struct objtool_file *file, const struct section *sec if (!rex_w) break; - /* skip RIP relative displacement */ - if (is_RIP()) - break; - /* skip nontrivial SIB */ if (have_SIB()) { modrm_rm = sib_base; @@ -467,6 +463,12 @@ int arch_decode_instruction(struct objtool_file *file, const struct section *sec break; } + /* lea disp(%rip), %dst */ + if (is_RIP()) { + insn->type = INSN_LEA_RIP; + break; + } + /* lea disp(%src), %dst */ ADD_OP(op) { op->src.offset = ins.displacement.value; @@ -737,7 +739,10 @@ int arch_decode_instruction(struct objtool_file *file, const struct section *sec break; } - insn->immediate = ins.immediate.nbytes ? ins.immediate.value : 0; + if (ins.immediate.nbytes) + insn->immediate = ins.immediate.value; + else if (ins.displacement.nbytes) + insn->immediate = ins.displacement.value; return 0; } diff --git a/tools/objtool/check.c b/tools/objtool/check.c index 6604f5d038aa..7fc96c30b79c 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -4392,6 +4392,51 @@ static bool noendbr_range(struct objtool_file *file, struct instruction *insn) return insn->offset == sym->offset + sym->len; } +static int __validate_ibt_insn(struct objtool_file *file, struct instruction *insn, + struct instruction *dest) +{ + if (dest->type == INSN_ENDBR) { + mark_endbr_used(dest); + return 0; + } + + if (insn_func(dest) && insn_func(insn) && + insn_func(dest)->pfunc == insn_func(insn)->pfunc) { + /* + * Anything from->to self is either _THIS_IP_ or + * IRET-to-self. + * + * There is no sane way to annotate _THIS_IP_ since the + * compiler treats the relocation as a constant and is + * happy to fold in offsets, skewing any annotation we + * do, leading to vast amounts of false-positives. + * + * There's also compiler generated _THIS_IP_ through + * KCOV and such which we have no hope of annotating. + * + * As such, blanket accept self-references without + * issue. + */ + return 0; + } + + /* + * Accept anything ANNOTATE_NOENDBR. + */ + if (dest->noendbr) + return 0; + + /* + * Accept if this is the instruction after a symbol + * that is (no)endbr -- typical code-range usage. + */ + if (noendbr_range(file, dest)) + return 0; + + WARN_INSN(insn, "relocation to !ENDBR: %s", offstr(dest->sec, dest->offset)); + return 1; +} + static int validate_ibt_insn(struct objtool_file *file, struct instruction *insn) { struct instruction *dest; @@ -4404,6 +4449,7 @@ static int validate_ibt_insn(struct objtool_file *file, struct instruction *insn * direct/indirect branches: */ switch (insn->type) { + case INSN_CALL: case INSN_CALL_DYNAMIC: case INSN_JUMP_CONDITIONAL: @@ -4413,6 +4459,23 @@ static int validate_ibt_insn(struct objtool_file *file, struct instruction *insn case INSN_RETURN: case INSN_NOP: return 0; + + case INSN_LEA_RIP: + if (!insn_reloc(file, insn)) { + /* local function pointer reference without reloc */ + + off = arch_jump_destination(insn); + + dest = find_insn(file, insn->sec, off); + if (!dest) { + WARN_INSN(insn, "corrupt function pointer reference"); + return 1; + } + + return __validate_ibt_insn(file, insn, dest); + } + break; + default: break; } @@ -4423,13 +4486,6 @@ static int validate_ibt_insn(struct objtool_file *file, struct instruction *insn reloc_offset(reloc) + 1, (insn->offset + insn->len) - (reloc_offset(reloc) + 1))) { - /* - * static_call_update() references the trampoline, which - * doesn't have (or need) ENDBR. Skip warning in that case. - */ - if (reloc->sym->static_call_tramp) - continue; - off = reloc->sym->offset; if (reloc_type(reloc) == R_X86_64_PC32 || reloc_type(reloc) == R_X86_64_PLT32) @@ -4441,47 +4497,7 @@ static int validate_ibt_insn(struct objtool_file *file, struct instruction *insn if (!dest) continue; - if (dest->type == INSN_ENDBR) { - mark_endbr_used(dest); - continue; - } - - if (insn_func(dest) && insn_func(insn) && - insn_func(dest)->pfunc == insn_func(insn)->pfunc) { - /* - * Anything from->to self is either _THIS_IP_ or - * IRET-to-self. - * - * There is no sane way to annotate _THIS_IP_ since the - * compiler treats the relocation as a constant and is - * happy to fold in offsets, skewing any annotation we - * do, leading to vast amounts of false-positives. - * - * There's also compiler generated _THIS_IP_ through - * KCOV and such which we have no hope of annotating. - * - * As such, blanket accept self-references without - * issue. - */ - continue; - } - - /* - * Accept anything ANNOTATE_NOENDBR. - */ - if (dest->noendbr) - continue; - - /* - * Accept if this is the instruction after a symbol - * that is (no)endbr -- typical code-range usage. - */ - if (noendbr_range(file, dest)) - continue; - - WARN_INSN(insn, "relocation to !ENDBR: %s", offstr(dest->sec, dest->offset)); - - warnings++; + warnings += __validate_ibt_insn(file, insn, dest); } return warnings; diff --git a/tools/objtool/include/objtool/arch.h b/tools/objtool/include/objtool/arch.h index 0b303eba660e..d63b46a19f39 100644 --- a/tools/objtool/include/objtool/arch.h +++ b/tools/objtool/include/objtool/arch.h @@ -28,6 +28,7 @@ enum insn_type { INSN_CLD, INSN_TRAP, INSN_ENDBR, + INSN_LEA_RIP, INSN_OTHER, }; From 32b504854bd96f707a03c6ddecb0af9d7fbc4775 Mon Sep 17 00:00:00 2001 From: HONG Yifan Date: Tue, 8 Oct 2024 23:47:17 +0000 Subject: [PATCH 2/5] objtool: Also include tools/include/uapi When building objtool against a sysroot that contains a stripped down version of the UAPI headers, the following error happens: In file included from arch/x86/decode.c:10: In file included from .../tools/arch/x86/include/asm/insn.h:10: In file included from /include/asm/byteorder.h:9: In file included from /include/linux/byteorder/little_endian.h:15: In file included from /include/linux/stddef.h:9: In file included from .../tools/include/linux/compiler_types.h:36: .../tools/include/linux/compiler-gcc.h:3:2: error: "Please don't include directly, include instead." 3 | #error "Please don't include directly, include instead." | ^ 1 error generated. As hinted by the error, this is because /include/linux/stddef.h (a stripped-down version of uapi/include/linux/stddef.h) includes linux/compiler_types.h directly. However, this gets resolved to tools/include/linux/compiler_types.h, which is not expected to be included directly. To resolve this, I added tools/include/uapi to the include paths when building objtool. With this trick, linux/stddef.h is resolved to tools/include/uapi/linux/stddef.h, which doesn't include linux/compiler_types.h. Signed-off-by: HONG Yifan Signed-off-by: Josh Poimboeuf --- tools/objtool/Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/objtool/Makefile b/tools/objtool/Makefile index bf7f7f84ac62..f56e27727534 100644 --- a/tools/objtool/Makefile +++ b/tools/objtool/Makefile @@ -24,6 +24,7 @@ LIBELF_LIBS := $(shell $(HOSTPKG_CONFIG) libelf --libs 2>/dev/null || echo -lel all: $(OBJTOOL) INCLUDES := -I$(srctree)/tools/include \ + -I$(srctree)/tools/include/uapi \ -I$(srctree)/tools/arch/$(HOSTARCH)/include/uapi \ -I$(srctree)/tools/arch/$(SRCARCH)/include \ -I$(srctree)/tools/objtool/include \ From 3bf19a0fb690022ec22ce87a5afeb1030cbcb56c Mon Sep 17 00:00:00 2001 From: Zheng Yejian Date: Fri, 13 Sep 2024 10:45:01 +0800 Subject: [PATCH 3/5] x86/unwind/orc: Fix unwind for newly forked tasks When arch_stack_walk_reliable() is called to unwind for newly forked tasks, the return value is negative which means the call stack is unreliable. This obviously does not meet expectations. The root cause is that after commit 3aec4ecb3d1f ("x86: Rewrite ret_from_fork() in C"), the 'ret_addr' of newly forked task is changed to 'ret_from_fork_asm' (see copy_thread()), then at the start of the unwind, it is incorrectly interprets not as a "signal" one because 'ret_from_fork' is still used to determine the initial "signal" (see __unwind_start()). Then the address gets incorrectly decremented in the call to orc_find() (see unwind_next_frame()) and resulting in the incorrect ORC data. To fix it, check 'ret_from_fork_asm' rather than 'ret_from_fork' in __unwind_start(). Fixes: 3aec4ecb3d1f ("x86: Rewrite ret_from_fork() in C") Signed-off-by: Zheng Yejian Signed-off-by: Josh Poimboeuf --- arch/x86/kernel/unwind_orc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/kernel/unwind_orc.c b/arch/x86/kernel/unwind_orc.c index d00c28aaa5be..d4705a348a80 100644 --- a/arch/x86/kernel/unwind_orc.c +++ b/arch/x86/kernel/unwind_orc.c @@ -723,7 +723,7 @@ void __unwind_start(struct unwind_state *state, struct task_struct *task, state->sp = task->thread.sp + sizeof(*frame); state->bp = READ_ONCE_NOCHECK(frame->bp); state->ip = READ_ONCE_NOCHECK(frame->ret_addr); - state->signal = (void *)state->ip == ret_from_fork; + state->signal = (void *)state->ip == ret_from_fork_asm; } if (get_stack_info((unsigned long *)state->sp, state->task, From 56ac7bd2c58a4e93d19f0ccb181035d075b315d3 Mon Sep 17 00:00:00 2001 From: Carlos Llamas Date: Mon, 12 Aug 2024 23:01:20 +0000 Subject: [PATCH 4/5] Revert "scripts/faddr2line: Check only two symbols when calculating symbol size" This reverts commit c02904f05ff805d6c0631634d5751ebd338f75ec. Such commit assumed that only two symbols are relevant for the symbol size calculation. However, this can lead to an incorrect symbol size calculation when there are mapping symbols emitted by readelf. For instance, when feeding 'update_irq_load_avg+0x1c/0x1c4', faddr2line might need to process the following readelf lines: 784284: ffffffc0081cca30 428 FUNC GLOBAL DEFAULT 2 update_irq_load_avg 87319: ffffffc0081ccb0c 0 NOTYPE LOCAL DEFAULT 2 $x.62522 87321: ffffffc0081ccbdc 0 NOTYPE LOCAL DEFAULT 2 $x.62524 87323: ffffffc0081ccbe0 0 NOTYPE LOCAL DEFAULT 2 $x.62526 87325: ffffffc0081ccbe4 0 NOTYPE LOCAL DEFAULT 2 $x.62528 87327: ffffffc0081ccbe8 0 NOTYPE LOCAL DEFAULT 2 $x.62530 87329: ffffffc0081ccbec 0 NOTYPE LOCAL DEFAULT 2 $x.62532 87331: ffffffc0081ccbf0 0 NOTYPE LOCAL DEFAULT 2 $x.62534 87332: ffffffc0081ccbf4 0 NOTYPE LOCAL DEFAULT 2 $x.62535 783403: ffffffc0081ccbf4 424 FUNC GLOBAL DEFAULT 2 sched_pelt_multiplier The symbol size of 'update_irq_load_avg' should be calculated with the address of 'sched_pelt_multiplier', after skipping the mapping symbols seen in between. However, the offending commit cuts the list short and faddr2line incorrectly assumes 'update_irq_load_avg' is the last symbol in the section, resulting in: $ scripts/faddr2line vmlinux update_irq_load_avg+0x1c/0x1c4 skipping update_irq_load_avg address at 0xffffffc0081cca4c due to size mismatch (0x1c4 != 0x3ff9a59988) no match for update_irq_load_avg+0x1c/0x1c4 After reverting the commit the issue is resolved: $ scripts/faddr2line vmlinux update_irq_load_avg+0x1c/0x1c4 update_irq_load_avg+0x1c/0x1c4: cpu_of at kernel/sched/sched.h:1109 (inlined by) update_irq_load_avg at kernel/sched/pelt.c:481 Fixes: c02904f05ff8 ("scripts/faddr2line: Check only two symbols when calculating symbol size") Signed-off-by: Carlos Llamas Acked-by: Will Deacon Acked-by: Brian Johannesmeyer Signed-off-by: Josh Poimboeuf --- scripts/faddr2line | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/faddr2line b/scripts/faddr2line index fe0cc45f03be..1fa6beef9f97 100755 --- a/scripts/faddr2line +++ b/scripts/faddr2line @@ -252,7 +252,7 @@ __faddr2line() { found=2 break fi - done < <(echo "${ELF_SYMS}" | sed 's/\[.*\]//' | ${AWK} -v sec=$sym_sec '$7 == sec' | sort --key=2 | ${GREP} -A1 --no-group-separator " ${sym_name}$") + done < <(echo "${ELF_SYMS}" | sed 's/\[.*\]//' | ${AWK} -v sec=$sym_sec '$7 == sec' | sort --key=2) if [[ $found = 0 ]]; then warn "can't find symbol: sym_name: $sym_name sym_sec: $sym_sec sym_addr: $sym_addr sym_elf_size: $sym_elf_size" From d5173f7537505315557d8580e3a648f07f17deda Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Fri, 8 Nov 2024 10:32:02 +0100 Subject: [PATCH 5/5] objtool: Exclude __tracepoints data from ENDBR checks For some, as of yet unexplained reason, Clang-19, but not GCC, generates and endless stream of: drivers/iio/imu/bno055/bno055_ser.o: warning: objtool: __tracepoint_send_chunk+0x20: data relocation to !ENDBR: __SCT__tp_func_send_chunk+0x0 drivers/iio/imu/bno055/bno055_ser.o: warning: objtool: __tracepoint_cmd_retry+0x20: data relocation to !ENDBR: __SCT__tp_func_cmd_retry+0x0 drivers/iio/imu/bno055/bno055_ser.o: warning: objtool: __tracepoint_write_reg+0x20: data relocation to !ENDBR: __SCT__tp_func_write_reg+0x0 drivers/iio/imu/bno055/bno055_ser.o: warning: objtool: __tracepoint_read_reg+0x20: data relocation to !ENDBR: __SCT__tp_func_read_reg+0x0 drivers/iio/imu/bno055/bno055_ser.o: warning: objtool: __tracepoint_recv+0x20: data relocation to !ENDBR: __SCT__tp_func_recv+0x0 Which is entirely correct, but harmless. Add the __tracepoints section to the exclusion list. Signed-off-by: Peter Zijlstra (Intel) Link: https://lkml.kernel.org/r/20241108184618.GG38786@noisy.programming.kicks-ass.net --- tools/objtool/check.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/objtool/check.c b/tools/objtool/check.c index 7fc96c30b79c..f7586f82b967 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -4573,6 +4573,7 @@ static int validate_ibt(struct objtool_file *file) !strcmp(sec->name, "__jump_table") || !strcmp(sec->name, "__mcount_loc") || !strcmp(sec->name, ".kcfi_traps") || + !strcmp(sec->name, "__tracepoints") || strstr(sec->name, "__patchable_function_entries")) continue;