Linux Kernel Selftest development
 help / color / mirror / Atom feed
* [PATCH bpf-next] libbpf: Reject oversized unknown BTF type records
@ 2026-09-02  9:11 Mark Amirkan
  2026-09-03  0:40 ` Andrii Nakryiko
  0 siblings, 1 reply; 2+ messages in thread
From: Mark Amirkan @ 2026-09-02  9:11 UTC (permalink / raw)
  To: bpf
  Cc: andrii, eddyz87, ihor.solodrai, ast, daniel, memxor, martin.lau,
	song, yonghong.song, jolsa, emil, shuah, linux-kernel,
	linux-kselftest, Mark Amirkan

btf_type_size_unknown() returns an int, but calculates the type record
size using unsigned operands.  With a 24-bit vlen, an unknown kind whose
vlen is 0xffffff and whose info and element sizes are both 252 has a
record size of 0xfc00000c.  Converting this value to int produces
-67108852.

btf_parse_type_sec() returns the negative value, and btf_new() encodes it
with ERR_PTR().  Since the value is outside the error-pointer range,
libbpf_ptr() does not recognize it as an error.  On a 64-bit system, a
129-byte raw BTF input makes btf__new() return 0xfffffffffc00000c while
libbpf_get_error() returns zero and errno remains zero.  Calling
btf__type_cnt() or btf__free() on the result crashes.  The same input
makes bpftool's "btf dump file" command terminate with SIGSEGV.

Calculate the record size as size_t and reject values above INT_MAX
before converting it to int.  Add a regression test for the maximum-vlen
unknown kind.

Fixes: cacd6729c092 ("libbpf: Adjust btf_vlen() to return a __u32")
Assisted-by: Symbolic
Signed-off-by: Mark Amirkan <markdamirkan@gmail.com>
---
Testing:
- normal builds of libbpf and bpftool, plus an ASan/UBSan libbpf build;
- the reproducer through btf__new() and bpftool;
- the added btf_kind test and the existing encoding/decoding subtests;
- identical bpftool output before and after the fix for all 60 BTF blobs
  under /sys/kernel/btf on the test system.

The complete BPF selftest suite was not run locally because clang was not
available.

This fixes code present in Linux v7.2; please consider it for stable.

 tools/lib/bpf/btf.c                           | 11 +++++-
 .../selftests/bpf/prog_tests/btf_kind.c       | 39 +++++++++++++++++++
 2 files changed, 49 insertions(+), 1 deletion(-)

diff --git a/tools/lib/bpf/btf.c b/tools/lib/bpf/btf.c
index c783359977b46e521965c1af223515cfd8701b12..83a7f199f37dfdc2384107a45a8be5b98c4cd8f1 100644
--- a/tools/lib/bpf/btf.c
+++ b/tools/lib/bpf/btf.c
@@ -421,6 +421,7 @@ static int btf_type_size_unknown(const struct btf *btf, const struct btf_type *t
 {
 	__u32 l_cnt = btf->hdr.layout_len / sizeof(struct btf_layout);
 	struct btf_layout *l = btf->layout;
+	size_t type_size;
 	__u32 vlen = btf_vlen(t);
 	__u32 kind = btf_kind(t);
 
@@ -448,7 +449,15 @@ static int btf_type_size_unknown(const struct btf *btf, const struct btf_type *t
 		return -EINVAL;
 	}
 
-	return sizeof(struct btf_type) + l[kind].info_sz + vlen * l[kind].elem_sz;
+	type_size = sizeof(struct btf_type) + l[kind].info_sz +
+		    (size_t)vlen * l[kind].elem_sz;
+	if (type_size > INT_MAX) {
+		pr_debug("BTF type size %zu for kind %u is too large\n",
+			 type_size, kind);
+		return -E2BIG;
+	}
+
+	return type_size;
 }
 
 static int btf_type_size(const struct btf *btf, const struct btf_type *t)
diff --git a/tools/testing/selftests/bpf/prog_tests/btf_kind.c b/tools/testing/selftests/bpf/prog_tests/btf_kind.c
index f61afe6a79a51f86f22f62bd86c685b7db8b39d1..fc6a4db9993764536885a5c5f13980a4c3a6bd26 100644
--- a/tools/testing/selftests/bpf/prog_tests/btf_kind.c
+++ b/tools/testing/selftests/bpf/prog_tests/btf_kind.c
@@ -217,10 +217,49 @@ void test_btf_kind_decoding(void)
 	btf__free(btf);
 }
 
+static void test_btf_kind_size_overflow(void)
+{
+	/* Max vlen and aligned u8 layout sizes produce type size 0xfc00000c. */
+	struct {
+		struct btf_header hdr;
+		struct btf_type type;
+		struct btf_layout layouts[NR_BTF_KINDS + 1];
+		char strs[1];
+	} __packed raw_btf = {
+		.hdr = {
+			.magic = BTF_MAGIC,
+			.version = BTF_VERSION,
+			.hdr_len = sizeof(struct btf_header),
+			.type_len = sizeof(struct btf_type),
+			.layout_off = sizeof(struct btf_type),
+			.layout_len = sizeof(struct btf_layout) * (NR_BTF_KINDS + 1),
+			.str_off = sizeof(struct btf_type) +
+				   sizeof(struct btf_layout) * (NR_BTF_KINDS + 1),
+			.str_len = 1,
+		},
+		.type.info = (NR_BTF_KINDS << 24) | BTF_MAX_VLEN,
+		.layouts[NR_BTF_KINDS] = {
+			.info_sz = 252,
+			.elem_sz = 252,
+		},
+	};
+	struct btf *btf;
+	int err;
+
+	errno = 0;
+	btf = btf__new(&raw_btf, sizeof(raw_btf));
+	err = libbpf_get_error(btf);
+	if (!ASSERT_EQ(err, -E2BIG, "size_overflow_err"))
+		return;
+	ASSERT_NULL(btf, "size_overflow_btf");
+}
+
 void test_btf_kind(void)
 {
 	if (test__start_subtest("btf_kind_encoding"))
 		test_btf_kind_encoding();
 	if (test__start_subtest("btf_kind_decoding"))
 		test_btf_kind_decoding();
+	if (test__start_subtest("btf_kind_size_overflow"))
+		test_btf_kind_size_overflow();
 }

base-commit: d761934c9483ecde93fe99d8705282f716dfee50
-- 
2.39.3 (Apple Git-146)

^ permalink raw reply related	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2026-09-03  0:40 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-02  9:11 [PATCH bpf-next] libbpf: Reject oversized unknown BTF type records Mark Amirkan
2026-09-03  0:40 ` Andrii Nakryiko

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox