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]>

Reply via email to