linux-kselftest.vger.kernel.org archive mirror
 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

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

On Wed, Sep 2, 2026 at 2:11 AM Mark Amirkan <markdamirkan@gmail.com> wrote:
>
> 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.

that's ok, I hope AI wasn't too stressed about this unfortunate state
of things...

we are not going to harden libbpf against any possible malicious
corruption of BTF contents. If you don't trust the source of data,
don't pass it to libbpf (or do it in a VM or some such).

pw-bot: cr

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

Do you use libbpf in practice? and where do you get its source code from, if so?

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

[...]

^ permalink raw reply	[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;
as well as URLs for NNTP newsgroup(s).