[PATCH] arm64: insn: Route BTI to simulate_nop to avoid XOL/SS at function entry

Khaja Hussain Shaik Khaji
2025-11-06T10:50:11Z

On arm64 with branch protection, functions typically begin with a BTI
(Branch Target Identification) landing pad. Today the decoder treats BTI
as requiring out-of-line single-step (XOL), allocating a slot and placing
an SS-BRK. Under SMP this leaves a small window before DAIF is masked
where an asynchronous exception or nested probe can interleave and clear
current_kprobe, resulting in an SS-BRK panic.

Handle BTI like NOP in the decoder and simulate it (advance PC by one
instruction). This avoids XOL/SS-BRK at these sites and removes the
single-step window, while preserving correctness for kprobes since BTI’s
branch-target enforcement has no program-visible effect in this EL1
exception context.

In practice BTI is most commonly observed at function entry, so the main
effect of this change is to eliminate entry-site single-stepping. Other
instructions and non-entry sites are unaffected.

Signed-off-by: Khaja Hussain Shaik Khaji <khaja.khaji@oss.qualcomm.com>
---
arch/arm64/include/asm/insn.h | 5 -----
arch/arm64/kernel/probes/decode-insn.c | 9 ++++++---
arch/arm64/kernel/probes/simulate-insn.c | 1 +
3 files changed, 7 insertions(+), 8 deletions(-)

diff --git a/arch/arm64/include/asm/insn.h b/arch/arm64/include/asm/insn.h
index 18c7811774d3..7e80cc1f0c3d 100644
--- a/arch/arm64/include/asm/insn.h
+++ b/arch/arm64/include/asm/insn.h
@@ -452,11 +452,6 @@ static __always_inline bool aarch64_insn_is_steppable_hint(u32 insn)
case AARCH64_INSN_HINT_PACIASP:
case AARCH64_INSN_HINT_PACIBZ:
case AARCH64_INSN_HINT_PACIBSP:
- case AARCH64_INSN_HINT_BTI:
- case AARCH64_INSN_HINT_BTIC:
- case AARCH64_INSN_HINT_BTIJ:
- case AARCH64_INSN_HINT_BTIJC:
- case AARCH64_INSN_HINT_NOP:
return true;
default:
return false;
diff --git a/arch/arm64/kernel/probes/decode-insn.c b/arch/arm64/kernel/probes/decode-insn.c
index 6438bf62e753..7ce2cf5e21d3 100644
--- a/arch/arm64/kernel/probes/decode-insn.c
+++ b/arch/arm64/kernel/probes/decode-insn.c
@@ -79,10 +79,13 @@ enum probe_insn __kprobes
arm_probe_decode_insn(u32 insn, struct arch_probe_insn *api)
{
/*
- * While 'nop' instruction can execute in the out-of-line slot,
- * simulating them in breakpoint handling offers better performance.
+ * NOP and BTI (Branch Target Identification) have no program‑visible side
+ * effects for kprobes purposes. Simulate them to avoid XOL/SS‑BRK and the
+ * small single‑step window. BTI’s branch‑target enforcement semantics are
+ * irrelevant in this EL1 kprobe context, so advancing PC by one insn is
+ * sufficient here.
*/
- if (aarch64_insn_is_nop(insn)) {
+ if (aarch64_insn_is_nop(insn) || aarch64_insn_is_bti(insn)) {
api->handler = simulate_nop;
return INSN_GOOD_NO_SLOT;
}
diff --git a/arch/arm64/kernel/probes/simulate-insn.c b/arch/arm64/kernel/probes/simulate-insn.c
index 4c6d2d712fbd..b83312cb70ba 100644
--- a/arch/arm64/kernel/probes/simulate-insn.c
+++ b/arch/arm64/kernel/probes/simulate-insn.c
@@ -200,5 +200,6 @@ simulate_ldrsw_literal(u32 opcode, long addr, struct pt_regs *regs)
void __kprobes
simulate_nop(u32 opcode, long addr, struct pt_regs *regs)
{
+ /* Also used as BTI simulator: both just advance PC by one insn. */
arm64_skip_faulting_instruction(regs, AARCH64_INSN_SIZE);
}
--
2.34.1