Resending v2 because Gmail mangled the inline v1 and Patchwork reported "Patch is empty." The complete git-format-patch output is attached as a text/plain part; its decoded bytes apply cleanly to current bpf-next d114bb989367.
The attachment includes the v2 changelog and authorized Signed-off-by line.
From b00cc136eecc130e3081faace5340b25ca6fc94e Mon Sep 17 00:00:00 2001 From: Darren Carreras <[email protected]> Date: Mon, 10 Aug 2026 15:10:57 -0400 Subject: [PATCH bpf-next v2] libbpf: avoid overflow in BTF.ext bounds check An invalid BTF.ext subsection length can wrap the pointer addition used by btf_ext_parse_sec_info() on 32-bit builds. The wrapped pointer passes the bounds check and parsing then reads beyond the copied BTF.ext data. Validate the offset and length with subtraction before forming the section pointer. Add a selftest using an overflowing func_info_len. Fixes: cf579164e9ea ("libbpf: Support BTF.ext loading and output in either endianness") Closes: https://issues.oss-fuzz.com/issues/477315119 Signed-off-by: Darren Carreras <[email protected]> --- Changes in v2: - Resend as plain text because Gmail mangled the v1 diff and Patchwork reported "Patch is empty." - Include the authorized DCO Signed-off-by line. tools/lib/bpf/btf.c | 13 +++++----- .../selftests/bpf/prog_tests/test_btf_ext.c | 24 +++++++++++++++++++ 2 files changed, 31 insertions(+), 6 deletions(-) diff --git a/tools/lib/bpf/btf.c b/tools/lib/bpf/btf.c index 8417de9..744f33e 100644 --- a/tools/lib/bpf/btf.c +++ b/tools/lib/bpf/btf.c @@ -3364,7 +3364,7 @@ static int btf_ext_parse_sec_info(struct btf_ext *btf_ext, { const struct btf_ext_info_sec *sinfo; struct btf_ext_info *ext_info; - __u32 info_left, record_size; + __u32 data_left, info_left, record_size; size_t sec_cnt = 0; void *info; @@ -3377,16 +3377,17 @@ static int btf_ext_parse_sec_info(struct btf_ext *btf_ext, return -EINVAL; } - /* The start of the info sec (including the __u32 record_size). */ - info = btf_ext->data + btf_ext->hdr->hdr_len + ext_sec->off; - info_left = ext_sec->len; - - if (btf_ext->data + btf_ext->data_size < info + ext_sec->len) { + data_left = btf_ext->data_size - btf_ext->hdr->hdr_len; + if (ext_sec->off > data_left || ext_sec->len > data_left - ext_sec->off) { pr_debug("%s section (off:%u len:%u) is beyond the end of the ELF section .BTF.ext\n", ext_sec->desc, ext_sec->off, ext_sec->len); return -EINVAL; } + /* The start of the info sec (including the __u32 record_size). */ + info = btf_ext->data + btf_ext->hdr->hdr_len + ext_sec->off; + info_left = ext_sec->len; + /* At least a record size */ if (info_left < sizeof(__u32)) { pr_debug(".BTF.ext %s record size not found\n", ext_sec->desc); diff --git a/tools/testing/selftests/bpf/prog_tests/test_btf_ext.c b/tools/testing/selftests/bpf/prog_tests/test_btf_ext.c index 7d1b478..77db87d 100644 --- a/tools/testing/selftests/bpf/prog_tests/test_btf_ext.c +++ b/tools/testing/selftests/bpf/prog_tests/test_btf_ext.c @@ -1,9 +1,31 @@ // SPDX-License-Identifier: GPL-2.0 /* Copyright (c) 2025 Meta Platforms Inc. */ #include <test_progs.h> +#include "bpf/libbpf_internal.h" #include "test_btf_ext.skel.h" #include "btf_helpers.h" +static void subtest_invalid_info_len(void) +{ + struct { + struct btf_ext_header hdr; + __u32 record_size; + } raw = { + .hdr = { + .magic = BTF_MAGIC, + .version = BTF_VERSION, + .hdr_len = sizeof(raw.hdr), + .func_info_len = UINT32_MAX - 7, + }, + .record_size = sizeof(struct bpf_func_info_min), + }; + struct btf_ext *btf_ext; + + btf_ext = btf_ext__new((void *)&raw, sizeof(raw)); + if (!ASSERT_ERR_PTR(btf_ext, "invalid_info_len")) + btf_ext__free(btf_ext); +} + static void subtest_line_func_info(void) { struct test_btf_ext *skel; @@ -59,6 +81,8 @@ static void subtest_line_func_info(void) void test_btf_ext(void) { + if (test__start_subtest("invalid_info_len")) + subtest_invalid_info_len(); if (test__start_subtest("line_func_info")) subtest_line_func_info(); }

