Re: [RFC PATCH v2 0/4] Mark Kprobe debug exception paths noinstr

From: Ada Couprie Diaz

Date: Tue Nov 03 2026 - 11:03:37 EST


On 30/10/2026 14:23, Will Deacon wrote:

[+Ada]
Thanks for bringing this to my attention !
On Thu, Aug 13, 2026 at 06:47:31AM +0000, Hongyan Xia wrote:
From: Hongyan Xia <hongyan.xia@xxxxxxxxxxxxx>

Commit 879a6754d3d1 ("arm64: kprobes: Only handle faults originating
from XOL slot") and 23f851ac0078 ("arm64: kprobes: Allow reentering
kprobes while single-stepping") band-aided two corner cases in the arm64
kprobe path: Nested page faults inside Kprobe and nested Kprobe SS
handling. Although the two patches were taken, Will asked whether we
should keep patching things like this each time we find a corner case,
and whether making Kprobe noinstr is the correct answer [1].

This series attempts to do exactly that: Making the Kprobe path noinstr.
The kprobe handlers become noinstr end to end. Apart from marking
functions noinstr, we also force-inline some static inline helper
functions because certain compilers still outline them, breaking
noinstr.

Note that arm64 has no objtool so at the moment noinstr is more of a
contract that must be followed, instead of actually enforcing control
flow. Instrumentation can still happen if someone with root privileges
does something stupid, but now it is regarded as a bug rather than a
legitimate use case that the kernel needs to cover.

Just to make sure I am not missing something : as far as I can tell,
Kprobe checks the section the instruction to be instrumented is in
and will refuse to instrument anything within `.noinstr.text`.[0]
Given that this series converts the whole call trees of the Kprobe
functions to `noinstr`, should that not make it safe ?
(My understanding is that Kprobe is the only one checking the noinstr
section, all the other instrumentations and sanitizers do not.)

[1] https://lore.kernel.org/all/alpuL10h7-OK2hFb@willie-the-truck/

Changed in v2:
- Remove instrumentation_{begin/end}() markers as they do nothing
- Fold __always_inline into the patches that actually need them
- Remove the debug exception path changes unrelated to Kprobe. This
series is only about Kprobe for now
- Drop the kprobe_page_fault() patch for now. It's a question whether we
even need it at all. Will send a patch for it separately.

Hongyan Xia (4):
arm64/entry: Make debug_exception_enter/exit() noinstr
arm64/debug: Make the Kprobe functions noinstr
arm64/kprobes: Make the entire Kprobe noinstr
Revert "arm64: kprobes: Allow reentering kprobes while
single-stepping"

arch/arm64/include/asm/esr.h | 2 +-
arch/arm64/include/asm/kprobes.h | 15 ++------
arch/arm64/include/asm/percpu.h | 2 +-
arch/arm64/include/asm/preempt.h | 4 +-
arch/arm64/include/asm/ptrace.h | 4 +-
arch/arm64/kernel/debug-monitors.c | 6 +--
arch/arm64/kernel/entry-common.c | 13 ++-----
arch/arm64/kernel/probes/kprobes.c | 59 ++++++++++--------------------
include/linux/kprobes.h | 8 ++--
Given the lack of objtool enforcement (as you note above), I wonder
whether it would be more robust to do this on a per-compilation unit
basis? i.e. disable instrumentation for debug-monitors.c, entry-common.c
and kprobes.c?

I think that marking whole compilations units as `noinstr` would be great too,
though I am not sure how it would affect some other sanitizers/instrumentation.

Unfortunately, I couldn't find a way to do that in the Makefile :/ In
which case, perhaps this series is the best we can do, but I thought I'd
ask the question in case somebody has any bright ideas.

Will

I found out the same thing when I initially started working on making sure
alternative-callbacks where `noinstr`/patch safe, so this pushed me to dig
a bit further.


There are two parts to this, the first one I believe to be relatively simple :
GCOV, KCOV, KASAN, KCSAN, KMSAN, UBSAN and ftrace all have a way to be
turned off for specific compilation units in the Makefiles. We already
use them in some places : `KCOV_INSTRUMENT_entry-common.o := n`.

We could introduce a similar variable to disable all instrumentations,
re-using what is already in place : something like
`NO_INSTRUMENT_entry-common.o := 1`, which would be checked before all
the other control variables in `scripts/Makefile.lib` and override them.


The second part is Kprobe itself, which would be much more painful to
disable as it does not rely on compiler instrumentation or other build
options we could use.
However, Kprobe /does/ check at runtime if it is allowed to probe
a location, refusing to do so if it is in `.noinstr.text` or if the function
name is part of its explicit deny list.

So, looking at the shenanigans KVM does for the nVHE hypervisor
symbols[1], I think it could be possible to either :
1. move the `.text` section into `.noinstr.text`, or
2. add all symbols to the `_kprobe_blacklist` section, as if adding
`NOKPROBE_SYMBOL()` to all functions within the compilation unit.

I played around by hand with the first idea using `objcopy` and was able
to combine the `.text` section into an existing `.noinstr.text` section,
preserving both, but that did not update the relocations, so the kernel
immediately crashed.
This leads me to think that adding the symbols to the `_kprobe_blacklist`
section with a LD script might be the more straightforward solution.

I have no experience with LD scripts but I would be happy to have a go,
if that sounds like something that could work *and* worth the effort.
Otherwise I think this series might be the best we can do !

Kind regards,
Ada

[0] : https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/kernel/kprobes.c?id=6628e7d4c6b25c8d18f1345e3a20d21829aae7e4#n2840
[1] : https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/arm64/kvm/hyp/nvhe/Makefile?id=6628e7d4c6b25c8d18f1345e3a20d21829aae7e4#n38