Linux Trace Kernel
 help / color / mirror / Atom feed
* [v2 PATCH] kprobes: skip non-symbol addresses in kprobe_add_ksym_blacklist()
@ 2026-05-06  1:27 Jianpeng Chang
  2026-05-07  1:46 ` Masami Hiramatsu
  0 siblings, 1 reply; 2+ messages in thread
From: Jianpeng Chang @ 2026-05-06  1:27 UTC (permalink / raw)
  To: naveen, davem, mhiramat, arnd, mark.rutland, catalin.marinas
  Cc: linux-kernel, linux-trace-kernel, linux-arch, stable,
	Jianpeng Chang

When kprobe_add_area_blacklist() iterates through a section like
.kprobes.text, the start address may not correspond to a named symbol.
On ARM64 with CONFIG_DYNAMIC_FTRACE_WITH_CALL_OPS=y (introduced by
commit baaf553d3bc3 ("arm64: Implement
HAVE_DYNAMIC_FTRACE_WITH_CALL_OPS")), the compiler flag
-fpatchable-function-entry=4,2 inserts 2 NOPs before each function entry
point for ftrace call_ops. These pre-function NOPs sit at the section base
address, before the first named function symbol. The compiler emits a $x
mapping symbol at offset 0x00 to mark the start of code, but
find_kallsyms_symbol() ignores mapping symbols.

Without CONFIG_DYNAMIC_FTRACE_WITH_CALL_OPS (e.g. defconfig), no
pre-function NOPs are inserted, the first function starts at offset
0x00, and the bug does not trigger.

This only affects modules that have a .kprobes.text section (i.e. those
using the __kprobes annotation). Modules using NOKPROBE_SYMBOL() instead
(like kretprobe_example.ko) blacklist exact function addresses via the
_kprobe_blacklist section and are not affected.

For kprobe_example.ko on ARM64 with -fpatchable-function-entry=4,2,
the .kprobes.text section layout is:

  offset 0x00: $x + 2 NOPs    (mapping symbol + ftrace preamble)
  offset 0x08: handler_post   (64 bytes)
  offset 0x50: handler_pre    (68 bytes)

kprobe_add_area_blacklist() starts iterating from the section base
address (offset 0x00), which only has the $x mapping symbol.
kprobe_add_ksym_blacklist() then calls kallsyms_lookup_size_offset()
for this address, which goes through:

  kallsyms_lookup_size_offset()
    -> module_address_lookup()
      -> find_kallsyms_symbol()

find_kallsyms_symbol() scans all module symbols to find the closest
preceding symbol.

Since no named text symbol exists at offset 0x00,
find_kallsyms_symbol() picks __UNIQUE_ID_vermagic (a .modinfo symbol
whose address is in the temporary image) as the "best" match. The
computed "size" = next_text_symbol - modinfo_symbol spans across
these two unrelated memory regions, creating a blacklist entry with
a bogus range of tens of terabytes.

Whether this causes a visible failure depends on address randomization,
here is what happens on Raspberry Pi 4/5:

  - On RPi5, the bogus size was ~35 TB. start + size stayed within
    64-bit range, so the blacklist entry covered the entire kernel
    text. register_kprobe() in the module's own init function failed
    with -EINVAL.

  - On RPi4, the bogus size was ~75 TB. start + size overflowed
    64 bits and wrapped to a small address near zero. The range
    check (addr >= start && addr < end) then failed because end
    wrapped around, so the bogus entry was accidentally harmless
    and kprobes worked by luck.

The same bug exists on both machines, but randomization determines whether
the integer overflow masks it or not.

Fix this by adding notrace to the __kprobes macro. Functions in
.kprobes.text are kprobe infrastructure handlers that should never be
traced by ftrace. With notrace, the compiler stops inserting them and the
non-symbol gap at the section start disappears entirely.

Fixes: baaf553d3bc3 ("arm64: Implement HAVE_DYNAMIC_FTRACE_WITH_CALL_OPS")
Signed-off-by: Jianpeng Chang <jianpeng.chang.cn@windriver.com>
---
v2: 
  - use notrace instead of skipping the nops
v1: https://lore.kernel.org/all/20260427073545.3656835-1-jianpeng.chang.cn@windriver.com/

 include/asm-generic/kprobes.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/include/asm-generic/kprobes.h b/include/asm-generic/kprobes.h
index 060eab094e5a..5290a2b2e15a 100644
--- a/include/asm-generic/kprobes.h
+++ b/include/asm-generic/kprobes.h
@@ -14,7 +14,7 @@ static unsigned long __used					\
 	_kbl_addr_##fname = (unsigned long)fname;
 # define NOKPROBE_SYMBOL(fname)	__NOKPROBE_SYMBOL(fname)
 /* Use this to forbid a kprobes attach on very low level functions */
-# define __kprobes	__section(".kprobes.text")
+# define __kprobes	notrace __section(".kprobes.text")
 # define nokprobe_inline	__always_inline
 #else
 # define NOKPROBE_SYMBOL(fname)
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 2+ messages in thread

* Re: [v2 PATCH] kprobes: skip non-symbol addresses in kprobe_add_ksym_blacklist()
  2026-05-06  1:27 [v2 PATCH] kprobes: skip non-symbol addresses in kprobe_add_ksym_blacklist() Jianpeng Chang
@ 2026-05-07  1:46 ` Masami Hiramatsu
  0 siblings, 0 replies; 2+ messages in thread
From: Masami Hiramatsu @ 2026-05-07  1:46 UTC (permalink / raw)
  To: Jianpeng Chang
  Cc: naveen, davem, arnd, mark.rutland, catalin.marinas, linux-kernel,
	linux-trace-kernel, linux-arch, stable

On Wed, 6 May 2026 09:27:06 +0800
Jianpeng Chang <jianpeng.chang.cn@windriver.com> wrote:

> When kprobe_add_area_blacklist() iterates through a section like
> .kprobes.text, the start address may not correspond to a named symbol.
> On ARM64 with CONFIG_DYNAMIC_FTRACE_WITH_CALL_OPS=y (introduced by
> commit baaf553d3bc3 ("arm64: Implement
> HAVE_DYNAMIC_FTRACE_WITH_CALL_OPS")), the compiler flag
> -fpatchable-function-entry=4,2 inserts 2 NOPs before each function entry
> point for ftrace call_ops. These pre-function NOPs sit at the section base
> address, before the first named function symbol. The compiler emits a $x
> mapping symbol at offset 0x00 to mark the start of code, but
> find_kallsyms_symbol() ignores mapping symbols.
> 
> Without CONFIG_DYNAMIC_FTRACE_WITH_CALL_OPS (e.g. defconfig), no
> pre-function NOPs are inserted, the first function starts at offset
> 0x00, and the bug does not trigger.
> 
> This only affects modules that have a .kprobes.text section (i.e. those
> using the __kprobes annotation). Modules using NOKPROBE_SYMBOL() instead
> (like kretprobe_example.ko) blacklist exact function addresses via the
> _kprobe_blacklist section and are not affected.
> 
> For kprobe_example.ko on ARM64 with -fpatchable-function-entry=4,2,
> the .kprobes.text section layout is:
> 
>   offset 0x00: $x + 2 NOPs    (mapping symbol + ftrace preamble)
>   offset 0x08: handler_post   (64 bytes)
>   offset 0x50: handler_pre    (68 bytes)
> 
> kprobe_add_area_blacklist() starts iterating from the section base
> address (offset 0x00), which only has the $x mapping symbol.
> kprobe_add_ksym_blacklist() then calls kallsyms_lookup_size_offset()
> for this address, which goes through:
> 
>   kallsyms_lookup_size_offset()
>     -> module_address_lookup()
>       -> find_kallsyms_symbol()
> 
> find_kallsyms_symbol() scans all module symbols to find the closest
> preceding symbol.
> 
> Since no named text symbol exists at offset 0x00,
> find_kallsyms_symbol() picks __UNIQUE_ID_vermagic (a .modinfo symbol
> whose address is in the temporary image) as the "best" match. The
> computed "size" = next_text_symbol - modinfo_symbol spans across
> these two unrelated memory regions, creating a blacklist entry with
> a bogus range of tens of terabytes.
> 
> Whether this causes a visible failure depends on address randomization,
> here is what happens on Raspberry Pi 4/5:
> 
>   - On RPi5, the bogus size was ~35 TB. start + size stayed within
>     64-bit range, so the blacklist entry covered the entire kernel
>     text. register_kprobe() in the module's own init function failed
>     with -EINVAL.
> 
>   - On RPi4, the bogus size was ~75 TB. start + size overflowed
>     64 bits and wrapped to a small address near zero. The range
>     check (addr >= start && addr < end) then failed because end
>     wrapped around, so the bogus entry was accidentally harmless
>     and kprobes worked by luck.
> 
> The same bug exists on both machines, but randomization determines whether
> the integer overflow masks it or not.
> 
> Fix this by adding notrace to the __kprobes macro. Functions in
> .kprobes.text are kprobe infrastructure handlers that should never be
> traced by ftrace. With notrace, the compiler stops inserting them and the
> non-symbol gap at the section start disappears entirely.
> 

Thanks, this looks good to me!

> Fixes: baaf553d3bc3 ("arm64: Implement HAVE_DYNAMIC_FTRACE_WITH_CALL_OPS")
> Signed-off-by: Jianpeng Chang <jianpeng.chang.cn@windriver.com>
> ---
> v2: 
>   - use notrace instead of skipping the nops
> v1: https://lore.kernel.org/all/20260427073545.3656835-1-jianpeng.chang.cn@windriver.com/
> 
>  include/asm-generic/kprobes.h | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/include/asm-generic/kprobes.h b/include/asm-generic/kprobes.h
> index 060eab094e5a..5290a2b2e15a 100644
> --- a/include/asm-generic/kprobes.h
> +++ b/include/asm-generic/kprobes.h
> @@ -14,7 +14,7 @@ static unsigned long __used					\
>  	_kbl_addr_##fname = (unsigned long)fname;
>  # define NOKPROBE_SYMBOL(fname)	__NOKPROBE_SYMBOL(fname)
>  /* Use this to forbid a kprobes attach on very low level functions */
> -# define __kprobes	__section(".kprobes.text")
> +# define __kprobes	notrace __section(".kprobes.text")
>  # define nokprobe_inline	__always_inline
>  #else
>  # define NOKPROBE_SYMBOL(fname)
> -- 
> 2.54.0
> 


-- 
Masami Hiramatsu (Google) <mhiramat@kernel.org>

^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2026-05-07  1:46 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-06  1:27 [v2 PATCH] kprobes: skip non-symbol addresses in kprobe_add_ksym_blacklist() Jianpeng Chang
2026-05-07  1:46 ` Masami Hiramatsu

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox