* [PATCH] tracing/user_events: Don't free an event that a dynamic event still references
@ 2026-10-31 21:03 David Carlier
2026-10-31 21:15 ` sashiko-bot
0 siblings, 1 reply; 2+ messages in thread
From: David Carlier @ 2026-10-31 21:03 UTC (permalink / raw)
To: Steven Rostedt, Masami Hiramatsu
Cc: Mathieu Desnoyers, linux-trace-kernel, linux-kernel,
David Carlier, stable
An eprobe takes a reference on the call of the event it attaches to,
and kprobe, uprobe, fprobe and synth events all refuse to go away while
trace_event_dyn_busy() says such a reference exists. user_events never
checks it, so a user event can be freed under an eprobe:
echo 'u:test u32 a' >> dynamic_events
echo 'e:ep user_events/test a=$a' >> dynamic_events
echo '-:user_events/test' >> dynamic_events
echo '-:eprobes/ep' >> dynamic_events
The last write drops the reference on the freed call.
Return -EBUSY from destroy_user_event() in that case.
Fixes: 7f5a08c79df3 ("user_events: Add minimal support for trace_event into ftrace")
Cc: stable@vger.kernel.org
Signed-off-by: David Carlier <devnexen@gmail.com>
---
A non-persistent event whose last fd closes while an eprobe
holds it now stays registered after the eprobe is gone. Can follow
up with a retry on the eprobe put if wanted.
kernel/trace/trace_events_user.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/kernel/trace/trace_events_user.c b/kernel/trace/trace_events_user.c
index f658c3a77aa7..49b857a7f1d7 100644
--- a/kernel/trace/trace_events_user.c
+++ b/kernel/trace/trace_events_user.c
@@ -1506,6 +1506,10 @@ static int destroy_user_event(struct user_event *user)
lockdep_assert_held(&event_mutex);
+ /* If there is a reference to a dynamic event */
+ if (trace_event_dyn_busy(&user->call))
+ return -EBUSY;
+
/*
* Detach the fields before removing the call. Removing the event
* frees the field list memory (trace_destroy_fields() is run on
--
2.55.0
^ permalink raw reply related [flat|nested] 2+ messages in thread
* Re: [PATCH] tracing/user_events: Don't free an event that a dynamic event still references
2026-10-31 21:03 [PATCH] tracing/user_events: Don't free an event that a dynamic event still references David Carlier
@ 2026-10-31 21:15 ` sashiko-bot
0 siblings, 0 replies; 2+ messages in thread
From: sashiko-bot @ 2026-10-31 21:15 UTC (permalink / raw)
To: David Carlier; +Cc: linux-trace-kernel
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] tracing/user_events: permanent leak of non-persistent event in destroy_user_event()
--
commit 426e76d7362e855d5b85cce6bb2e395443a0a474
Author: David Carlier <devnexen@gmail.com>
tracing/user_events: Don't free an event that a dynamic event still references
This commit prevents destroy_user_event() from freeing a user_event while a
dynamic event still holds a reference on user->call by checking
trace_event_dyn_busy(&user->call) and returning -EBUSY.
> diff --git a/kernel/trace/trace_events_user.c b/kernel/trace/trace_events_user.c
> index f658c3a77aa7a..49b857a7f1d78 100644
> --- a/kernel/trace/trace_events_user.c
> +++ b/kernel/trace/trace_events_user.c
> @@ -1506,6 +1506,10 @@ static int destroy_user_event(struct user_event *user)
>
> lockdep_assert_held(&event_mutex);
>
> + /* If there is a reference to a dynamic event */
> + if (trace_event_dyn_busy(&user->call))
> + return -EBUSY;
[Severity: High]
Does returning -EBUSY here cause non-persistent user_event instances to leak
and become permanently undeletable if their last reference is dropped while
user->call.refcnt is non-zero?
When a non-persistent event is created via DIAG_IOCSREG without
USER_EVENT_REG_PERSIST, user->refcnt tracks active user and probe references.
If a dynamic event consumer holds a reference on user->call (such as an
eprobe via find_and_get_event(), a histogram action via
trace_get_event_file(), or an enable_event trigger during teardown in
event_enable_trigger_free() before trigger_kthread runs
enable_trigger_private_data_free()) when the last user->refcnt reference is
dropped in user_event_put(), delayed_destroy_user_event() runs:
kernel/trace/trace_events_user.c:delayed_destroy_user_event() {
...
if (!refcount_dec_and_test(&user->refcnt))
goto out;
if (destroy_user_event(user)) {
...
pr_warn("user_events: Unable to delete event\n");
refcount_set(&user->refcnt, 1);
}
out:
mutex_unlock(&event_mutex);
}
Because destroy_user_event() now returns -EBUSY, delayed_destroy_user_event()
resets user->refcnt to 1 without setting USER_EVENT_REG_PERSIST in
user->reg_flags, and with no owner holding that reference.
Once the eprobe or trigger later drops its reference on user->call, how can
this user_event ever be freed?
1. Manual deletion via dynamic_events in user_event_free(), via DIAG_IOCSDEL
in delete_user_event(), or via dyn_events_release_all() ->
user_event_is_busy() all check user_event_last_ref():
kernel/trace/trace_events_user.c:user_event_last_ref() {
int last = 0;
if (user->reg_flags & USER_EVENT_REG_PERSIST)
last = 1;
return refcount_read(&user->refcnt) == last;
}
Because USER_EVENT_REG_PERSIST is not set, user_event_last_ref() requires
user->refcnt to be 0, so manual deletion permanently fails with -EBUSY.
2. Re-opening and closing the event increments user->refcnt from 1 to 2 and
decrements it back to 1 in user_event_put(), never reaching 0 to re-queue
delayed_destroy_user_event().
As a result, the struct user_event, its fields and validators, its tracefs
entry, and a current_user_events quota slot appear to be leaked until reboot.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261031210420.501187-1-devnexen@gmail.com?part=1
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-10-31 21:15 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-31 21:03 [PATCH] tracing/user_events: Don't free an event that a dynamic event still references David Carlier
2026-10-31 21:15 ` sashiko-bot
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.