FAILED: patch "[PATCH] fprobe: Use guard(rcu_sched_notrace) and check" failed to apply to 6.18-stable tree
Subsystems (2)
[email protected]
maintainerThe patch below does not apply to the 6.18-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.
To reproduce the conflict and resubmit, you may use the following commands:
git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-6.18.y
git checkout FETCH_HEAD
git cherry-pick -x e0a6249190402a28f1e2925a81acf573157d55ef
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<[email protected]>' --in-reply-to '2026100341-letter-overstate-ddf7@gregkh' --subject-prefix 'PATCH 6.18.y' 'HEAD^..'
Possible dependencies:
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From e0a6249190402a28f1e2925a81acf573157d55ef Mon Sep 17 00:00:00 2001
From: "Masami Hiramatsu (Google)" <[email protected]>
Date: Tue, 29 Sep 2026 09:19:12 +0900
Subject: [PATCH] fprobe: Use guard(rcu_sched_notrace) and check
rcu_is_watching()
unregister_fprobe() and unregister_fprobe_async() (used by BPF kprobe-multi) rely on standard RCU grace periods (synchronize_rcu() and call_rcu()) to wait until in-flight fprobe handlers complete before freeing the fprobe.
However, if an fprobe handler executes while RCU is not watching (such as in the idle loop or nohz_full extended quiescent states), standard RCU does not track preemption-disabled sections. Consequently, synchronize_rcu() does not wait for those executions, which can lead to a use-after-free if the fprobe is freed immediately after unregistration. Ensure handlers exit early when !rcu_is_watching().
Furthermore, fprobe_fgraph_entry() and fprobe_ftrace_entry() previously used guard(rcu)() and rcu_read_lock(), which invoke lockdep on every hit under CONFIG_PROVE_LOCKING. This adds overhead and can cause lockdep recursion if probed functions interact with lockdep.
Since rhltable_lookup() and rhl_for_each_entry_rcu() use rcu_dereference_all_check() (which checks rcu_read_lock_any_held()), holding preemption disabled via rcu_read_lock_sched_notrace() is fully valid and sufficient so long as rcu_is_watching() is true.
Define and use guard(rcu_sched_notrace)() across fprobe_ftrace_entry(), fprobe_fgraph_entry(), and fprobe_return(). This eliminates fast-path rcu_read_lock() and lockdep overhead while guaranteeing safe grace period synchronization.
kernel/trace/fprobe.c +16 -10
@@ -47,6 +47,10 @@ static struct rhltable fprobe_ip_table;static DEFINE_MUTEX (fprobe_mutex);static struct fgraph_ops fprobe_graph_ops; +DEFINE_LOCK_GUARD_0 (rcu_sched_notrace, +rcu_read_lock_sched_notrace (), + rcu_read_unlock_sched_notrace()) +static u32 fprobe_node_hashfn (const void *data,u32 len,u32 seed) {return hash_ptr (*(unsigned long **)data,32 );
@@ -329,16 +333,14 @@ static void fprobe_ftrace_entry(unsigned long ip, unsigned long parent_ip,struct fprobe *fp;int bit; +if (!rcu_is_watching ()) +return ; + bit =ftrace_test_recursion_trylock (ip, parent_ip);if (bit <0 )return ; -/* -* ftrace_test_recursion_trylock() disables preemption, but -* rhltable_lookup() checks whether rcu_read_lcok is held. -* So we take rcu_read_lock() here. -*/ -rcu_read_lock (); +guard (rcu_sched_notrace)(); head =rhltable_lookup (&fprobe_ip_table, &ip, fprobe_rht_params);rhl_for_each_entry_rcu (node, pos, head, hlist) {
@@ -353,7 +355,6 @@ static void fprobe_ftrace_entry(unsigned long ip, unsigned long parent_ip,else __fprobe_handler (ip, parent_ip, fp, fregs,NULL ); } -rcu_read_unlock ();ftrace_test_recursion_unlock (bit); }NOKPROBE_SYMBOL (fprobe_ftrace_entry);
@@ -567,10 +568,13 @@ static int fprobe_fgraph_entry(struct ftrace_graph_ent *trace, struct fgraph_opsstruct fprobe *fp;int used, ret; +if (!rcu_is_watching ()) +return 0 ; +if (WARN_ON_ONCE (!fregs))return 0 ; -guard (rcu)(); +guard (rcu_sched_notrace)(); head =rhltable_lookup (&fprobe_ip_table, &func, fprobe_rht_params); reserved_words =0 ;rhl_for_each_entry_rcu (node, pos, head, hlist) {
@@ -665,13 +669,16 @@ static void fprobe_return(struct ftrace_graph_ret *trace,int size, curr;int size_words; +if (!rcu_is_watching ()) +return ; + fgraph_data = (unsigned long *)fgraph_retrieve_data (gops->idx, &size);if (WARN_ON_ONCE (!fgraph_data))return ; size_words =SIZE_IN_LONG (size); ret_ip =ftrace_regs_get_instruction_pointer (fregs); -preempt_disable_notrace (); +guard (rcu_sched_notrace)(); curr =0 ;while (size_words > curr) {
@@ -687,7 +694,6 @@ static void fprobe_return(struct ftrace_graph_ret *trace, } curr += size; } -preempt_enable_notrace (); }NOKPROBE_SYMBOL (fprobe_return);
Sasha Levin
maintainerPATCH 1/2 tracing: fprobe: fix suspicious rcu usage in fprobe_entry
From: Menglong Dong <[email protected]>
[ Upstream commit ceb5d8d367d6d37a220023df443d8b67c2f4d1cd ]
rcu_read_lock() is not needed in fprobe_entry, but rcu_dereference_check() is used in rhltable_lookup(), which causes suspicious RCU usage warning:
WARNING: suspicious RCU usage
6.17.0-rc1-00001-gdfe0d675df82 #1 Tainted: G S
-----------------------------
include/linux/rhashtable.h:602 suspicious rcu_dereference_check() usage!
......
stack backtrace: CPU: 1 UID: 0 PID: 4652 Comm: ftracetest Tainted: G S Tainted: [S]=CPU_OUT_OF_SPEC, [I]=FIRMWARE_WORKAROUND Hardware name: Dell Inc. OptiPlex 7040/0Y7WYT, BIOS 1.1.1 10/07/2015 Call Trace: <TASK> dump_stack_lvl+0x7c/0x90 lockdep_rcu_suspicious+0x14f/0x1c0 __rhashtable_lookup+0x1e0/0x260 ? __pfx_kernel_clone+0x10/0x10 fprobe_entry+0x9a/0x450 ? __lock_acquire+0x6b0/0xca0 ? find_held_lock+0x2b/0x80 ? __pfx_fprobe_entry+0x10/0x10 ? __pfx_kernel_clone+0x10/0x10 ? lock_acquire+0x14c/0x2d0 ? __might_fault+0x74/0xc0 function_graph_enter_regs+0x2a0/0x550 ? __do_sys_clone+0xb5/0x100 ? __pfx_function_graph_enter_regs+0x10/0x10 ? _copy_to_user+0x58/0x70 ? __pfx_kernel_clone+0x10/0x10 ? __x64_sys_rt_sigprocmask+0x114/0x180 ? __pfx___x64_sys_rt_sigprocmask+0x10/0x10 ? __pfx_kernel_clone+0x10/0x10 ftrace_graph_func+0x87/0xb0
As we discussed in [1], fix this by using guard(rcu)() in fprobe_entry() to protect the rhltable_lookup() and rhl_for_each_entry_rcu() with rcu_read_lock and suppress this warning.
Stable-dep-of: e0a624919040 ("fprobe: Use guard(rcu_sched_notrace) and check rcu_is_watching()")
--- kernel/trace/fprobe.c | 1 + 1 file changed, 1 insertion(+)
kernel/trace/fprobe.c +1 -0
@@ -428,6 +428,7 @@ static int fprobe_fgraph_entry(struct ftrace_graph_ent *trace, struct fgraph_opsif (WARN_ON_ONCE (!fregs))return 0 ; +guard (rcu)(); head =rhltable_lookup (&fprobe_ip_table, &func, fprobe_rht_params); reserved_words =0 ;rhl_for_each_entry_rcu (node, pos, head, hlist) {
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 2/2 fprobe: Use guard(rcu_sched_notrace) and check rcu_is_watching()
From: "Masami Hiramatsu (Google)" <[email protected]>
[ Upstream commit e0a6249190402a28f1e2925a81acf573157d55ef ]
unregister_fprobe() and unregister_fprobe_async() (used by BPF kprobe-multi) rely on standard RCU grace periods (synchronize_rcu() and call_rcu()) to wait until in-flight fprobe handlers complete before freeing the fprobe.
However, if an fprobe handler executes while RCU is not watching (such as in the idle loop or nohz_full extended quiescent states), standard RCU does not track preemption-disabled sections. Consequently, synchronize_rcu() does not wait for those executions, which can lead to a use-after-free if the fprobe is freed immediately after unregistration. Ensure handlers exit early when !rcu_is_watching().
Furthermore, fprobe_fgraph_entry() and fprobe_ftrace_entry() previously used guard(rcu)() and rcu_read_lock(), which invoke lockdep on every hit under CONFIG_PROVE_LOCKING. This adds overhead and can cause lockdep recursion if probed functions interact with lockdep.
Since rhltable_lookup() and rhl_for_each_entry_rcu() use rcu_dereference_all_check() (which checks rcu_read_lock_any_held()), holding preemption disabled via rcu_read_lock_sched_notrace() is fully valid and sufficient so long as rcu_is_watching() is true.
Define and use guard(rcu_sched_notrace)() across fprobe_ftrace_entry(), fprobe_fgraph_entry(), and fprobe_return(). This eliminates fast-path rcu_read_lock() and lockdep overhead while guaranteeing safe grace period synchronization.
--- kernel/trace/fprobe.c | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-)
kernel/trace/fprobe.c +16 -10
@@ -47,6 +47,10 @@ static struct rhltable fprobe_ip_table;static DEFINE_MUTEX (fprobe_mutex);static struct fgraph_ops fprobe_graph_ops; +DEFINE_LOCK_GUARD_0 (rcu_sched_notrace, +rcu_read_lock_sched_notrace (), + rcu_read_unlock_sched_notrace()) +static u32 fprobe_node_hashfn (const void *data,u32 len,u32 seed) {return hash_ptr (*(unsigned long **)data,32 );
@@ -260,16 +264,14 @@ static void fprobe_ftrace_entry(unsigned long ip, unsigned long parent_ip,struct fprobe *fp;int bit; +if (!rcu_is_watching ()) +return ; + bit =ftrace_test_recursion_trylock (ip, parent_ip);if (bit <0 )return ; -/* -* ftrace_test_recursion_trylock() disables preemption, but -* rhltable_lookup() checks whether rcu_read_lcok is held. -* So we take rcu_read_lock() here. -*/ -rcu_read_lock (); +guard (rcu_sched_notrace)(); head =rhltable_lookup (&fprobe_ip_table, &ip, fprobe_rht_params);rhl_for_each_entry_rcu (node, pos, head, hlist) {
@@ -284,7 +286,6 @@ static void fprobe_ftrace_entry(unsigned long ip, unsigned long parent_ip,else __fprobe_handler (ip, parent_ip, fp, fregs,NULL ); } -rcu_read_unlock ();ftrace_test_recursion_unlock (bit); }NOKPROBE_SYMBOL (fprobe_ftrace_entry);
@@ -425,10 +426,13 @@ static int fprobe_fgraph_entry(struct ftrace_graph_ent *trace, struct fgraph_opsstruct fprobe *fp;int used, ret; +if (!rcu_is_watching ()) +return 0 ; +if (WARN_ON_ONCE (!fregs))return 0 ; -guard (rcu)(); +guard (rcu_sched_notrace)(); head =rhltable_lookup (&fprobe_ip_table, &func, fprobe_rht_params); reserved_words =0 ;rhl_for_each_entry_rcu (node, pos, head, hlist) {
@@ -521,13 +525,16 @@ static void fprobe_return(struct ftrace_graph_ret *trace,int size, curr;int size_words; +if (!rcu_is_watching ()) +return ; + fgraph_data = (unsigned long *)fgraph_retrieve_data (gops->idx, &size);if (WARN_ON_ONCE (!fgraph_data))return ; size_words =SIZE_IN_LONG (size); ret_ip =ftrace_regs_get_instruction_pointer (fregs); -preempt_disable_notrace (); +guard (rcu_sched_notrace)(); curr =0 ;while (size_words > curr) {
@@ -543,7 +550,6 @@ static void fprobe_return(struct ftrace_graph_ret *trace, } curr += size; } -preempt_enable_notrace (); }NOKPROBE_SYMBOL (fprobe_return);
Signature · 1 line
2.53.0