summaryrefslogtreecommitdiff
path: root/kernel/trace
diff options
context:
space:
mode:
authorJosef Bacik <josef@toxicpanda.com>2026-10-17 16:37:11 +0000
committerAlexei Starovoitov <ast@kernel.org>2026-10-17 21:58:22 +0000
commit3ccd700543f16a2b2ab656f35dcaeb6bd7c45e22 (patch)
treeb07711c24b70bcb734ca4e9f17fa057af9241fa7 /kernel/trace
parent12d07c38e1b0f79e48a37abd7103194212f2da8c (diff)
bpf: Read the probe of a perf event under event_mutex
For a perf event on a kprobe or uprobe event which was made in tracefs, bpf_get_kprobe_info() and bpf_get_uprobe_info() look the probe up by name in the list of dynamic events, and hand a pointer to its symbol or file name back to the caller. None of the callers holds event_mutex. The perf event keeps its trace event alive, nothing else. Other dynamic events are removed from the list and freed during the walk. And a probe which shares its event with other probes can be deleted while the event is in use, so even the probe which is found can be freed, together with the string, before the caller has measured and copied it. The result is a use-after-free in the walk, in strlen() and in the copy to user space, or a NULL pointer dereference. The readers are BPF_OBJ_GET_INFO_BY_FD and fdinfo of a perf link, for whoever holds a descriptor of the link, and BPF_TASK_FD_QUERY. The other side is anybody who may write to dynamic_events, kprobe_events or uprobe_events. Take event_mutex in bpf_get_perf_event_info() and give the caller a copy of the name, which it has to free. Fixes: 41bdc4b40ed6 ("bpf: introduce bpf subcommand BPF_TASK_FD_QUERY") Signed-off-by: Josef Bacik <josef@toxicpanda.com> Signed-off-by: Alexei Starovoitov <ast@kernel.org> Link: https://patch.msgid.link/20261017163745.3341787-10-alexei.starovoitov@gmail.com
Diffstat (limited to 'kernel/trace')
-rw-r--r--kernel/trace/bpf_trace.c13
1 files changed, 13 insertions, 0 deletions
diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c
index 5465e7eba238..304b426bd44d 100644
--- a/kernel/trace/bpf_trace.c
+++ b/kernel/trace/bpf_trace.c
@@ -2209,6 +2209,7 @@ int bpf_probe_unregister(struct bpf_raw_event_map *btp, struct bpf_raw_tp_link *
return tracepoint_probe_unregister(btp->tp, (void *)btp->bpf_func, link);
}
+/* On success the caller releases *buf with kfree_const() */
int bpf_get_perf_event_info(const struct perf_event *event, u32 *prog_id,
u32 *fd_type, const char **buf,
u64 *probe_offset, u64 *probe_addr,
@@ -2218,6 +2219,12 @@ int bpf_get_perf_event_info(const struct perf_event *event, u32 *prog_id,
struct bpf_prog *prog;
int flags, err = 0;
+ /*
+ * The list of dynamic events is walked, and a probe which has siblings
+ * can be deleted, with its strings, while its event is in use.
+ */
+ guard(mutex)(&event_mutex);
+
prog = event->prog;
if (!prog)
return -ENOENT;
@@ -2258,6 +2265,12 @@ int bpf_get_perf_event_info(const struct perf_event *event, u32 *prog_id,
#endif
}
+ if (!err && *buf) {
+ *buf = kstrdup_const(*buf, GFP_KERNEL);
+ if (!*buf)
+ err = -ENOMEM;
+ }
+
return err;
}