perf-tools fixes for v6.17-rc5

This series contains fixes for use-after-free resulted in segfaults
 after merging the bpf tree.  Also a couple of build and test fixes.
 
 Signed-off-by: Namhyung Kim <namhyung@kernel.org>
 -----BEGIN PGP SIGNATURE-----
 
 iHUEABYKAB0WIQSo2x5BnqMqsoHtzsmMstVUGiXMgwUCaLtFZAAKCRCMstVUGiXM
 g7OFAP9F6M4Cpri9cxmbDyr188w85n+l6QC/Rjabardgn6NoEAEAovtNCO/0jEQ0
 pUpYgMX3OCn+ARZGOR6vuJTSIlzj8gg=
 =G7Fh
 -----END PGP SIGNATURE-----

Merge tag 'perf-tools-fixes-for-v6.17-2025-09-05' of git://git.kernel.org/pub/scm/linux/kernel/git/perf/perf-tools

Pull perf tools fixes from Namhyung Kim:
 "Fixes for use-after-free that resulted in segfaults after merging the
  bpf tree.

  Also a couple of build and test fixes"

* tag 'perf-tools-fixes-for-v6.17-2025-09-05' of git://git.kernel.org/pub/scm/linux/kernel/git/perf/perf-tools:
  perf symbol-elf: Add support for the block argument for libbfd
  perf test: Checking BPF metadata collection fails on version string
  perf tests: Fix "PE file support" test build
  perf bpf-utils: Harden get_bpf_prog_info_linear
  perf bpf-utils: Constify bpil_array_desc
  perf bpf-event: Fix use-after-free in synthesis
This commit is contained in:
Linus Torvalds 2025-09-05 15:01:46 -07:00
commit d1d10cea08
5 changed files with 76 additions and 40 deletions

View file

@ -37,7 +37,7 @@ static int run_dir(const char *d)
size_t idx;
scnprintf(filename, PATH_MAX, "%s/pe-file.exe", d);
ret = filename__read_build_id(filename, &bid);
ret = filename__read_build_id(filename, &bid, /*block=*/true);
TEST_ASSERT_VAL("Failed to read build_id",
ret == sizeof(expect_build_id));
TEST_ASSERT_VAL("Wrong build_id", !memcmp(bid.data, expect_build_id,
@ -49,7 +49,7 @@ static int run_dir(const char *d)
!strcmp(debuglink, expect_debuglink));
scnprintf(debugfile, PATH_MAX, "%s/%s", d, debuglink);
ret = filename__read_build_id(debugfile, &bid);
ret = filename__read_build_id(debugfile, &bid, /*block=*/true);
TEST_ASSERT_VAL("Failed to read debug file build_id",
ret == sizeof(expect_build_id));
TEST_ASSERT_VAL("Wrong build_id", !memcmp(bid.data, expect_build_id,

View file

@ -61,7 +61,7 @@ test_bpf_metadata() {
/perf_version/ {
if (entry) print $NF;
}
' | egrep "$VERS" > /dev/null
' | grep -qF "$VERS"
then
echo "Basic BPF metadata test [Failed invalid output]"
err=1

View file

@ -657,9 +657,15 @@ static int perf_event__synthesize_one_bpf_prog(struct perf_session *session,
info_node->info_linear = info_linear;
info_node->metadata = NULL;
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;
/*
@ -827,18 +833,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_node *info_node;
struct perf_bpil *info_linear;
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 << PERF_BPIL_JITED_KSYMS;
arrays |= 1UL << PERF_BPIL_JITED_FUNC_LENS;
@ -852,6 +858,7 @@ static void perf_env__add_bpf_info(struct perf_env *env, u32 id)
info_linear = get_bpf_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;
}
@ -862,38 +869,46 @@ static void perf_env__add_bpf_info(struct perf_env *env, u32 id)
info_node->info_linear = info_linear;
info_node->metadata = bpf_metadata_create(&info_linear->info);
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:
/*
@ -907,7 +922,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)

View file

@ -20,7 +20,7 @@ struct bpil_array_desc {
*/
};
static struct bpil_array_desc bpil_array_desc[] = {
static const struct bpil_array_desc bpil_array_desc[] = {
[PERF_BPIL_JITED_INSNS] = {
offsetof(struct bpf_prog_info, jited_prog_insns),
offsetof(struct bpf_prog_info, jited_prog_len),
@ -115,7 +115,7 @@ get_bpf_prog_info_linear(int fd, __u64 arrays)
__u32 info_len = sizeof(info);
__u32 data_len = 0;
int i, err;
void *ptr;
__u8 *ptr;
if (arrays >> PERF_BPIL_LAST_ARRAY)
return ERR_PTR(-EINVAL);
@ -126,15 +126,15 @@ get_bpf_prog_info_linear(int fd, __u64 arrays)
pr_debug("can't get prog info: %s", strerror(errno));
return ERR_PTR(-EFAULT);
}
if (info.type >= __MAX_BPF_PROG_TYPE)
pr_debug("%s:%d: unexpected program type %u\n", __func__, __LINE__, info.type);
/* step 2: calculate total size of all arrays */
for (i = PERF_BPIL_FIRST_ARRAY; i < PERF_BPIL_LAST_ARRAY; ++i) {
const struct bpil_array_desc *desc = &bpil_array_desc[i];
bool include_array = (arrays & (1UL << i)) > 0;
struct bpil_array_desc *desc;
__u32 count, size;
desc = bpil_array_desc + i;
/* kernel is too old to support this field */
if (info_len < desc->array_offset + sizeof(__u32) ||
info_len < desc->count_offset + sizeof(__u32) ||
@ -163,19 +163,20 @@ get_bpf_prog_info_linear(int fd, __u64 arrays)
ptr = info_linear->data;
for (i = PERF_BPIL_FIRST_ARRAY; i < PERF_BPIL_LAST_ARRAY; ++i) {
struct bpil_array_desc *desc;
const struct bpil_array_desc *desc = &bpil_array_desc[i];
__u32 count, size;
if ((arrays & (1UL << i)) == 0)
continue;
desc = bpil_array_desc + i;
count = bpf_prog_info_read_offset_u32(&info, desc->count_offset);
size = bpf_prog_info_read_offset_u32(&info, desc->size_offset);
bpf_prog_info_set_offset_u32(&info_linear->info,
desc->count_offset, count);
bpf_prog_info_set_offset_u32(&info_linear->info,
desc->size_offset, size);
assert(ptr >= info_linear->data);
assert(ptr < &info_linear->data[data_len]);
bpf_prog_info_set_offset_u64(&info_linear->info,
desc->array_offset,
ptr_to_u64(ptr));
@ -189,27 +190,45 @@ get_bpf_prog_info_linear(int fd, __u64 arrays)
free(info_linear);
return ERR_PTR(-EFAULT);
}
if (info_linear->info.type >= __MAX_BPF_PROG_TYPE) {
pr_debug("%s:%d: unexpected program type %u\n",
__func__, __LINE__, info_linear->info.type);
}
/* step 6: verify the data */
ptr = info_linear->data;
for (i = PERF_BPIL_FIRST_ARRAY; i < PERF_BPIL_LAST_ARRAY; ++i) {
struct bpil_array_desc *desc;
__u32 v1, v2;
const struct bpil_array_desc *desc = &bpil_array_desc[i];
__u32 count1, count2, size1, size2;
__u64 ptr2;
if ((arrays & (1UL << i)) == 0)
continue;
desc = bpil_array_desc + i;
v1 = bpf_prog_info_read_offset_u32(&info, desc->count_offset);
v2 = bpf_prog_info_read_offset_u32(&info_linear->info,
count1 = bpf_prog_info_read_offset_u32(&info, desc->count_offset);
count2 = bpf_prog_info_read_offset_u32(&info_linear->info,
desc->count_offset);
if (v1 != v2)
pr_warning("%s: mismatch in element count\n", __func__);
if (count1 != count2) {
pr_warning("%s: mismatch in element count %u vs %u\n", __func__, count1, count2);
free(info_linear);
return ERR_PTR(-ERANGE);
}
v1 = bpf_prog_info_read_offset_u32(&info, desc->size_offset);
v2 = bpf_prog_info_read_offset_u32(&info_linear->info,
size1 = bpf_prog_info_read_offset_u32(&info, desc->size_offset);
size2 = bpf_prog_info_read_offset_u32(&info_linear->info,
desc->size_offset);
if (v1 != v2)
pr_warning("%s: mismatch in rec size\n", __func__);
if (size1 != size2) {
pr_warning("%s: mismatch in rec size %u vs %u\n", __func__, size1, size2);
free(info_linear);
return ERR_PTR(-ERANGE);
}
ptr2 = bpf_prog_info_read_offset_u64(&info_linear->info, desc->array_offset);
if (ptr_to_u64(ptr) != ptr2) {
pr_warning("%s: mismatch in array %p vs %llx\n", __func__, ptr, ptr2);
free(info_linear);
return ERR_PTR(-ERANGE);
}
ptr += roundup(count1 * size1, sizeof(__u64));
}
/* step 7: update info_len and data_len */
@ -224,13 +243,12 @@ void bpil_addr_to_offs(struct perf_bpil *info_linear)
int i;
for (i = PERF_BPIL_FIRST_ARRAY; i < PERF_BPIL_LAST_ARRAY; ++i) {
struct bpil_array_desc *desc;
const struct bpil_array_desc *desc = &bpil_array_desc[i];
__u64 addr, offs;
if ((info_linear->arrays & (1UL << i)) == 0)
continue;
desc = bpil_array_desc + i;
addr = bpf_prog_info_read_offset_u64(&info_linear->info,
desc->array_offset);
offs = addr - ptr_to_u64(info_linear->data);
@ -244,13 +262,12 @@ void bpil_offs_to_addr(struct perf_bpil *info_linear)
int i;
for (i = PERF_BPIL_FIRST_ARRAY; i < PERF_BPIL_LAST_ARRAY; ++i) {
struct bpil_array_desc *desc;
const struct bpil_array_desc *desc = &bpil_array_desc[i];
__u64 addr, offs;
if ((info_linear->arrays & (1UL << i)) == 0)
continue;
desc = bpil_array_desc + i;
offs = bpf_prog_info_read_offset_u64(&info_linear->info,
desc->array_offset);
addr = offs + ptr_to_u64(info_linear->data);

View file

@ -873,13 +873,17 @@ out:
#ifdef HAVE_LIBBFD_BUILDID_SUPPORT
static int read_build_id(const char *filename, struct build_id *bid)
static int read_build_id(const char *filename, struct build_id *bid, bool block)
{
size_t size = sizeof(bid->data);
int err = -1;
int err = -1, fd;
bfd *abfd;
abfd = bfd_openr(filename, NULL);
fd = open(filename, block ? O_RDONLY : (O_RDONLY | O_NONBLOCK));
if (fd < 0)
return -1;
abfd = bfd_fdopenr(filename, /*target=*/NULL, fd);
if (!abfd)
return -1;