Commit d1d10cea authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge tag 'perf-tools-fixes-for-v6.17-2025-09-05' of...

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
parents d3e45016 ca81e74d
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -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,
+1 −1
Original line number Diff line number Diff line
@@ -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
+27 −12
Original line number Diff line number Diff line
@@ -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);
	}

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)
+39 −22
Original line number Diff line number Diff line
@@ -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);
+7 −3
Original line number Diff line number Diff line
@@ -873,13 +873,17 @@ static int elf_read_build_id(Elf *elf, void *bf, size_t size)

#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;