From a8df7d0ef92eca28c610206c6748daf537ac0586 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Tue, 8 Apr 2025 00:02:13 -0700 Subject: objtool: Fix INSN_CONTEXT_SWITCH handling in validate_unret() The !CONFIG_IA32_EMULATION version of xen_entry_SYSCALL_compat() ends with a SYSCALL instruction which is classified by objtool as INSN_CONTEXT_SWITCH. Unlike validate_branch(), validate_unret() doesn't consider INSN_CONTEXT_SWITCH in a non-function to be a dead end, so it keeps going past the end of xen_entry_SYSCALL_compat(), resulting in the following warning: vmlinux.o: warning: objtool: xen_reschedule_interrupt+0x2a: RET before UNTRAIN Fix that by adding INSN_CONTEXT_SWITCH handling to validate_unret() to match what validate_branch() is already doing. Fixes: a09a6e2399ba ("objtool: Add entry UNRET validation") Reported-by: Andrew Cooper Signed-off-by: Josh Poimboeuf Signed-off-by: Ingo Molnar Cc: Linus Torvalds Link: https://lore.kernel.org/r/f5eda46fd09f15b1f5cde3d9ae3b92b958342add.1744095216.git.jpoimboe@kernel.org --- tools/objtool/check.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'tools/objtool/check.c') diff --git a/tools/objtool/check.c b/tools/objtool/check.c index 4a1f6c3169b3..c81b070ca495 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -3886,6 +3886,11 @@ static int validate_unret(struct objtool_file *file, struct instruction *insn) WARN_INSN(insn, "RET before UNTRAIN"); return 1; + case INSN_CONTEXT_SWITCH: + if (insn_func(insn)) + break; + return 0; + case INSN_NOP: if (insn->retpoline_safe) return 0; -- cgit v1.2.3 From fe1042b1ef79e4d5df33d5c0f0ce936493714eec Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Tue, 8 Apr 2025 00:02:14 -0700 Subject: objtool: Split INSN_CONTEXT_SWITCH into INSN_SYSCALL and INSN_SYSRET INSN_CONTEXT_SWITCH is ambiguous. It can represent both call semantics (SYSCALL, SYSENTER) and return semantics (SYSRET, IRET, RETS, RETU). Those differ significantly: calls preserve control flow whereas returns terminate it. Objtool uses an arbitrary rule for INSN_CONTEXT_SWITCH that almost works by accident: if in a function, keep going; otherwise stop. It should instead be based on the semantics of the underlying instruction. In preparation for improving that, split INSN_CONTEXT_SWITCH into INSN_SYCALL and INSN_SYSRET. No functional change. Signed-off-by: Josh Poimboeuf Signed-off-by: Ingo Molnar Cc: Linus Torvalds Link: https://lore.kernel.org/r/19a76c74d2c051d3bc9a775823cafc65ad267a7a.1744095216.git.jpoimboe@kernel.org --- tools/objtool/check.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'tools/objtool/check.c') diff --git a/tools/objtool/check.c b/tools/objtool/check.c index c81b070ca495..2c703b960420 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -3684,7 +3684,8 @@ static int validate_branch(struct objtool_file *file, struct symbol *func, break; - case INSN_CONTEXT_SWITCH: + case INSN_SYSCALL: + case INSN_SYSRET: if (func) { if (!next_insn || !next_insn->hint) { WARN_INSN(insn, "unsupported instruction in callable function"); @@ -3886,7 +3887,8 @@ static int validate_unret(struct objtool_file *file, struct instruction *insn) WARN_INSN(insn, "RET before UNTRAIN"); return 1; - case INSN_CONTEXT_SWITCH: + case INSN_SYSCALL: + case INSN_SYSRET: if (insn_func(insn)) break; return 0; -- cgit v1.2.3 From 9f9cc012c2cbac4833746a0182e06a8eec940d19 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Tue, 8 Apr 2025 00:02:15 -0700 Subject: objtool: Stop UNRET validation on UD2 In preparation for simplifying INSN_SYSCALL, make validate_unret() terminate control flow on UD2 just like validate_branch() already does. Signed-off-by: Josh Poimboeuf Signed-off-by: Ingo Molnar Cc: Linus Torvalds Link: https://lore.kernel.org/r/ce841269e7e28c8b7f32064464a9821034d724ff.1744095216.git.jpoimboe@kernel.org --- tools/objtool/check.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'tools/objtool/check.c') diff --git a/tools/objtool/check.c b/tools/objtool/check.c index 2c703b960420..2dd89b0f4d5e 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -3902,6 +3902,9 @@ static int validate_unret(struct objtool_file *file, struct instruction *insn) break; } + if (insn->dead_end) + return 0; + if (!next) { WARN_INSN(insn, "teh end!"); return 1; -- cgit v1.2.3 From 2dbbca9be4e5ed68d0972a2bcf4561d9cb85b7b7 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Tue, 8 Apr 2025 00:02:16 -0700 Subject: objtool, xen: Fix INSN_SYSCALL / INSN_SYSRET semantics Objtool uses an arbitrary rule for INSN_SYSCALL and INSN_SYSRET that almost works by accident: if it's in a function, control flow continues after the instruction, otherwise it terminates. That behavior should instead be based on the semantics of the underlying instruction. Change INSN_SYSCALL to always preserve control flow and INSN_SYSRET to always terminate it. The changed semantic for INSN_SYSCALL requires a tweak to the !CONFIG_IA32_EMULATION version of xen_entry_SYSCALL_compat(). In Xen, SYSCALL is a hypercall which usually returns. But in this case it's a hypercall to IRET which doesn't return. Add UD2 to tell objtool to terminate control flow, and to prevent undefined behavior at runtime. Signed-off-by: Josh Poimboeuf Signed-off-by: Ingo Molnar Reviewed-by: Juergen Gross # for the Xen part Cc: Linus Torvalds Link: https://lore.kernel.org/r/19453dfe9a0431b7f016e9dc16d031cad3812a50.1744095216.git.jpoimboe@kernel.org --- tools/objtool/check.c | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) (limited to 'tools/objtool/check.c') diff --git a/tools/objtool/check.c b/tools/objtool/check.c index 2dd89b0f4d5e..69f94bc47499 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -3685,14 +3685,19 @@ static int validate_branch(struct objtool_file *file, struct symbol *func, break; case INSN_SYSCALL: + if (func && (!next_insn || !next_insn->hint)) { + WARN_INSN(insn, "unsupported instruction in callable function"); + return 1; + } + + break; + case INSN_SYSRET: - if (func) { - if (!next_insn || !next_insn->hint) { - WARN_INSN(insn, "unsupported instruction in callable function"); - return 1; - } - break; + if (func && (!next_insn || !next_insn->hint)) { + WARN_INSN(insn, "unsupported instruction in callable function"); + return 1; } + return 0; case INSN_STAC: @@ -3888,9 +3893,9 @@ static int validate_unret(struct objtool_file *file, struct instruction *insn) return 1; case INSN_SYSCALL: + break; + case INSN_SYSRET: - if (insn_func(insn)) - break; return 0; case INSN_NOP: -- cgit v1.2.3 From 2d12c6fb78753925f494ca9079e2383529e8ae0e Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Tue, 8 Apr 2025 01:21:14 -0700 Subject: objtool: Remove ANNOTATE_IGNORE_ALTERNATIVE from CLAC/STAC ANNOTATE_IGNORE_ALTERNATIVE adds additional noise to the code generated by CLAC/STAC alternatives, hurting readability for those whose read uaccess-related code generation on a regular basis. Remove the annotation specifically for the "NOP patched with CLAC/STAC" case in favor of a manual check. Leave the other uses of that annotation in place as they're less common and more difficult to detect. Suggested-by: Linus Torvalds Signed-off-by: Josh Poimboeuf Signed-off-by: Ingo Molnar Acked-by: Linus Torvalds Link: https://lore.kernel.org/r/fc972ba4995d826fcfb8d02733a14be8d670900b.1744098446.git.jpoimboe@kernel.org --- tools/objtool/check.c | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) (limited to 'tools/objtool/check.c') diff --git a/tools/objtool/check.c b/tools/objtool/check.c index 69f94bc47499..b649049b6a11 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -3505,6 +3505,34 @@ next_orig: return next_insn_same_sec(file, alt_group->orig_group->last_insn); } +static bool skip_alt_group(struct instruction *insn) +{ + struct instruction *alt_insn = insn->alts ? insn->alts->insn : NULL; + + /* ANNOTATE_IGNORE_ALTERNATIVE */ + if (insn->alt_group && insn->alt_group->ignore) + return true; + + /* + * For NOP patched with CLAC/STAC, only follow the latter to avoid + * impossible code paths combining patched CLAC with unpatched STAC + * or vice versa. + * + * ANNOTATE_IGNORE_ALTERNATIVE could have been used here, but Linus + * requested not to do that to avoid hurting .s file readability + * around CLAC/STAC alternative sites. + */ + + if (!alt_insn) + return false; + + /* Don't override ASM_{CLAC,STAC}_UNSAFE */ + if (alt_insn->alt_group && alt_insn->alt_group->ignore) + return false; + + return alt_insn->type == INSN_CLAC || alt_insn->type == INSN_STAC; +} + /* * Follow the branch starting at the given instruction, and recursively follow * any other branches (jumps). Meanwhile, track the frame pointer state at @@ -3625,7 +3653,7 @@ static int validate_branch(struct objtool_file *file, struct symbol *func, } } - if (insn->alt_group && insn->alt_group->ignore) + if (skip_alt_group(insn)) return 0; if (handle_insn_ops(insn, next_insn, &state)) -- cgit v1.2.3 From a3cd5f507b72c0532c3345b6913557efab34f405 Mon Sep 17 00:00:00 2001 From: Miguel Ojeda Date: Sun, 13 Apr 2025 02:23:38 +0200 Subject: objtool/rust: add one more `noreturn` Rust function for Rust 1.86.0 Starting with Rust 1.86.0 (see upstream commit b151b513ba2b ("Insert null checks for pointer dereferences when debug assertions are enabled") [1]), under some kernel configurations with `CONFIG_RUST_DEBUG_ASSERTIONS=y`, one may trigger a new `objtool` warning: rust/kernel.o: warning: objtool: _R..._6kernel9workqueue6system() falls through to next function _R...9workqueue14system_highpri() due to a call to the `noreturn` symbol: core::panicking::panic_null_pointer_dereference Thus add it to the list so that `objtool` knows it is actually `noreturn`. See commit 56d680dd23c3 ("objtool/rust: list `noreturn` Rust functions") for more details. Cc: stable@vger.kernel.org # Needed in 6.12.y and later (Rust is pinned in older LTSs). Fixes: 56d680dd23c3 ("objtool/rust: list `noreturn` Rust functions") Link: https://github.com/rust-lang/rust/commit/b151b513ba2b65c7506ec1a80f2712bbd09154d1 [1] Reviewed-by: Alice Ryhl Link: https://lore.kernel.org/r/20250413002338.1741593-1-ojeda@kernel.org Signed-off-by: Miguel Ojeda --- tools/objtool/check.c | 1 + 1 file changed, 1 insertion(+) (limited to 'tools/objtool/check.c') diff --git a/tools/objtool/check.c b/tools/objtool/check.c index 4a1f6c3169b3..67006eeb30c8 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -225,6 +225,7 @@ static bool is_rust_noreturn(const struct symbol *func) str_ends_with(func->name, "_4core9panicking14panic_nounwind") || str_ends_with(func->name, "_4core9panicking18panic_bounds_check") || str_ends_with(func->name, "_4core9panicking19assert_failed_inner") || + str_ends_with(func->name, "_4core9panicking30panic_null_pointer_dereference") || str_ends_with(func->name, "_4core9panicking36panic_misaligned_pointer_dereference") || strstr(func->name, "_4core9panicking13assert_failed") || strstr(func->name, "_4core9panicking11panic_const24panic_const_") || -- cgit v1.2.3