On Sun, 30 Aug 2026 23:27:23 +0900 "Masami Hiramatsu (Google)" <[email protected]> wrote:
> From: Masami Hiramatsu (Google) <[email protected]> > > __within_kprobe_blacklist() traverses kprobe_blacklist without holding > kprobe_mutex. When a module is unloaded, kprobe_remove_area_blacklist() > removes blacklist entries and immediately frees them with kfree(). > A concurrent call to within_kprobe_blacklist() can therefore dereference > freed memory. > > Furthermore, within_kprobe_blacklist() can be called in atomic or > non-preemptible contexts where the sleeping kprobe_mutex cannot be taken. > > Protect kprobe_blacklist with RCU. Use guard(rcu)() and > list_for_each_entry_rcu() for traversal, list_add_tail_rcu() for > insertions, list_del_rcu() for deletions, and kfree_rcu() to reclaim > entries safely after a grace period. I realized this is required even without wprobe. So let me take this as a stable fix with following tags. Fixes: 376e242429bf ("kprobes: Introduce NOKPROBE_SYMBOL() macro to maintain kprobes blacklist") Cc: [email protected] Reported-by: Sashiko <[email protected]> Closes: https://lore.kernel.org/all/[email protected]/ Thanks, > > Assisted-by: Antigravity:gemini-3.7-flash > Signed-off-by: Masami Hiramatsu (Google) <[email protected]> > --- > Changes in v13: > - Newly added. > --- > include/linux/kprobes.h | 1 + > kernel/kprobes.c | 14 ++++++++++---- > 2 files changed, 11 insertions(+), 4 deletions(-) > > diff --git a/include/linux/kprobes.h b/include/linux/kprobes.h > index 8c4f3bb24429..e6de7ae55bda 100644 > --- a/include/linux/kprobes.h > +++ b/include/linux/kprobes.h > @@ -181,6 +181,7 @@ struct kprobe_blacklist_entry { > struct list_head list; > unsigned long start_addr; > unsigned long end_addr; > + struct rcu_head rcu; > }; > > #ifdef CONFIG_KPROBES > diff --git a/kernel/kprobes.c b/kernel/kprobes.c > index bfc89083daa9..6337da5cab9e 100644 > --- a/kernel/kprobes.c > +++ b/kernel/kprobes.c > @@ -1447,8 +1447,14 @@ static bool __within_kprobe_blacklist(unsigned long > addr) > /* > * If 'kprobe_blacklist' is defined, check the address and > * reject any probe registration in the prohibited area. > + * Note: this can return true during transition period where > + * (start_addr, end_addr) in the black list is shrinking > + * but old entry has not been removed yet. This is acceptable > + * because the worst case is that we reject more probes than > + * we should. > */ > - list_for_each_entry(ent, &kprobe_blacklist, list) { > + guard(rcu)(); > + list_for_each_entry_rcu(ent, &kprobe_blacklist, list) { > if (addr >= ent->start_addr && addr < ent->end_addr) > return true; > } > @@ -2509,7 +2515,7 @@ int kprobe_add_ksym_blacklist(unsigned long entry) > ent->start_addr = entry; > ent->end_addr = entry + size; > INIT_LIST_HEAD(&ent->list); > - list_add_tail(&ent->list, &kprobe_blacklist); > + list_add_tail_rcu(&ent->list, &kprobe_blacklist); > > return (int)size; > } > @@ -2603,8 +2609,8 @@ static void kprobe_remove_area_blacklist(unsigned long > start, unsigned long end) > list_for_each_entry_safe(ent, n, &kprobe_blacklist, list) { > if (ent->start_addr < start || ent->start_addr >= end) > continue; > - list_del(&ent->list); > - kfree(ent); > + list_del_rcu(&ent->list); > + kfree_rcu(ent, rcu); > } > } > > -- Masami Hiramatsu (Google) <[email protected]>
