]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
uprobes: Fix NULL pointer dereference in hprobe_expire()
authorBreno Leitao <leitao@debian.org>
Wed, 29 Jul 2026 14:44:40 +0000 (07:44 -0700)
committerPeter Zijlstra <peterz@infradead.org>
Fri, 31 Jul 2026 08:32:19 +0000 (10:32 +0200)
Forking a task that has a pending uretprobe can oops the kernel with a
NULL pointer dereference in the clone() path:

  BUG: kernel NULL pointer dereference, address: 0000000000000018
  Oops: 0002 [#1] SMP NOPTI
  RIP: 0010:hprobe_expire
  CR2: 0000000000000018
  Call Trace:
   uprobe_copy_process
   copy_process
   kernel_clone
   __x64_sys_clone
   do_syscall_64
   entry_SYSCALL_64_after_hwframe

This was found on real hosts on Meta fleet.

I've got the impression that this is what is happening:

  CPU 1                          CPU 2 (traced task)
  -----                          -------------------
                                 hit uprobe, prepare_uretprobe():
                                   hprobe LEASED, refcount >= 1
  uprobe_unregister()
    put_uprobe(): refcount -> 0
                                 fork() -> dup_utask()
                                   hprobe_expire(hprobe, true)
                                     try_get_uprobe() -> NULL
                                     get_uprobe(NULL)   <-- Oops

Only take the extra reference when the uprobe is non-NULL; a NULL means
it is gone and is the correct value to return.

Fixes: dd1a7567784e ("uprobes: SRCU-protect uretprobe lifetime (with timeout)")
Signed-off-by: Breno Leitao <leitao@debian.org>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Acked-by: Oleg Nesterov <oleg@redhat.com>
Acked-by: Andrii Nakryiko <andrii@kernel.org>
Cc: stable@vger.kernel.org
Link: https://patch.msgid.link/20260729-uprobe-v1-1-61896b87c867@debian.org
kernel/events/uprobes.c

index 4084e926e284487ea7e28b63721797b20f0dfefd..6300b216012cbe3d3064e91fd30609d3689bcb20 100644 (file)
@@ -830,7 +830,7 @@ static struct uprobe *hprobe_expire(struct hprobe *hprobe, bool get)
                if (try_cmpxchg(&hprobe->state, &hstate, uprobe ? HPROBE_STABLE : HPROBE_GONE)) {
                        /* We won the race, we are the ones to unlock SRCU */
                        __srcu_read_unlock(&uretprobes_srcu, hprobe->srcu_idx);
-                       return get ? get_uprobe(uprobe) : uprobe;
+                       return get && uprobe ? get_uprobe(uprobe) : uprobe;
                }
 
                /*