mirror of
https://github.com/torvalds/linux.git
synced 2026-03-08 04:04:43 +01:00
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:
commit
d1d10cea08
5 changed files with 76 additions and 40 deletions
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue