perf bpf-event: Fix use-after-free in synthesis

[ Upstream commit d7b67dd6f9 ]

Calls to perf_env__insert_bpf_prog_info may fail as a sideband thread
may already have inserted the bpf_prog_info. Such failures may yield
info_linear being freed which then causes use-after-free issues with
the internal bpf_prog_info info struct. Make it so that
perf_env__insert_bpf_prog_info trigger early non-error paths and fix
the use-after-free in perf_event__synthesize_one_bpf_prog. Add proper
return error handling to perf_env__add_bpf_info (that calls
perf_env__insert_bpf_prog_info) and propagate the return value in its
callers.

Closes: https://lore.kernel.org/lkml/CAP-5=fWJQcmUOP7MuCA2ihKnDAHUCOBLkQFEkQES-1ZZTrgf8Q@mail.gmail.com/
Fixes: 03edb7020b ("perf bpf: Fix two memory leakages when calling perf_env__insert_bpf_prog_info()")
Reviewed-by: Namhyung Kim <namhyung@kernel.org>
Signed-off-by: Ian Rogers <irogers@google.com>
Link: https://lore.kernel.org/r/20250902181713.309797-2-irogers@google.com
Signed-off-by: Namhyung Kim <namhyung@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
Ian Rogers 2025-09-02 11:17:11 -07:00 committed by Greg Kroah-Hartman
parent 43167766ea
commit b01a706f9e

View File

@ -301,9 +301,15 @@ static int perf_event__synthesize_one_bpf_prog(struct perf_session *session,
info_node->info_linear = info_linear;
if (!perf_env__insert_bpf_prog_info(env, info_node)) {
free(info_linear);
/*
* Insert failed, likely because of a duplicate event
* made by the sideband thread. Ignore synthesizing the
* metadata.
*/
free(info_node);
goto out;
}
/* info_linear is now owned by info_node and shouldn't be freed below. */
info_linear = NULL;
/*
@ -459,18 +465,18 @@ int perf_event__synthesize_bpf_events(struct perf_session *session,
return err;
}
static void perf_env__add_bpf_info(struct perf_env *env, u32 id)
static int perf_env__add_bpf_info(struct perf_env *env, u32 id)
{
struct bpf_prog_info_linear *info_linear;
struct bpf_prog_info_node *info_node;
struct btf *btf = NULL;
u64 arrays;
u32 btf_id;
int fd;
int fd, err = 0;
fd = bpf_prog_get_fd_by_id(id);
if (fd < 0)
return;
return -EINVAL;
arrays = 1UL << BPF_PROG_INFO_JITED_KSYMS;
arrays |= 1UL << BPF_PROG_INFO_JITED_FUNC_LENS;
@ -483,6 +489,7 @@ static void perf_env__add_bpf_info(struct perf_env *env, u32 id)
info_linear = bpf_program__get_prog_info_linear(fd, arrays);
if (IS_ERR_OR_NULL(info_linear)) {
pr_debug("%s: failed to get BPF program info. aborting\n", __func__);
err = PTR_ERR(info_linear);
goto out;
}
@ -492,38 +499,46 @@ static void perf_env__add_bpf_info(struct perf_env *env, u32 id)
if (info_node) {
info_node->info_linear = info_linear;
if (!perf_env__insert_bpf_prog_info(env, info_node)) {
pr_debug("%s: duplicate add bpf info request for id %u\n",
__func__, btf_id);
free(info_linear);
free(info_node);
goto out;
}
} else
} else {
free(info_linear);
err = -ENOMEM;
goto out;
}
if (btf_id == 0)
goto out;
btf = btf__load_from_kernel_by_id(btf_id);
if (libbpf_get_error(btf)) {
pr_debug("%s: failed to get BTF of id %u, aborting\n",
__func__, btf_id);
goto out;
if (!btf) {
err = -errno;
pr_debug("%s: failed to get BTF of id %u %d\n", __func__, btf_id, err);
} else {
perf_env__fetch_btf(env, btf_id, btf);
}
perf_env__fetch_btf(env, btf_id, btf);
out:
btf__free(btf);
close(fd);
return err;
}
static int bpf_event__sb_cb(union perf_event *event, void *data)
{
struct perf_env *env = data;
int ret = 0;
if (event->header.type != PERF_RECORD_BPF_EVENT)
return -1;
switch (event->bpf.type) {
case PERF_BPF_EVENT_PROG_LOAD:
perf_env__add_bpf_info(env, event->bpf.id);
ret = perf_env__add_bpf_info(env, event->bpf.id);
case PERF_BPF_EVENT_PROG_UNLOAD:
/*
@ -537,7 +552,7 @@ static int bpf_event__sb_cb(union perf_event *event, void *data)
break;
}
return 0;
return ret;
}
int evlist__add_bpf_sb_event(struct evlist *evlist, struct perf_env *env)