summaryrefslogtreecommitdiff
path: root/kernel
diff options
context:
space:
mode:
authorBreno Leitao <leitao@debian.org>2026-07-29 07:44:40 -0700
committerPeter Zijlstra <peterz@infradead.org>2026-07-31 10:32:19 +0200
commitcc679d7a6303e84d769f2afcde1fc51c51f127cd (patch)
treecac7d9148b4bc6d251d312ea6589a16d7f9c3c4c /kernel
parentf5098b6bae761e346ebcd9da7f95622c04733cff (diff)
uprobes: Fix NULL pointer dereference in hprobe_expire()
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
Diffstat (limited to 'kernel')
-rw-r--r--kernel/events/uprobes.c2
1 files changed, 1 insertions, 1 deletions
diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c
index 4084e926e284..6300b216012c 100644
--- a/kernel/events/uprobes.c
+++ b/kernel/events/uprobes.c
@@ -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;
}
/*