>From 0ee0da46079d4e9453afe2b28acdaadc0f011042 Mon Sep 17 00:00:00 2001
From: Darren Carreras <[email protected]>
Date: Mon, 10 Aug 2026 11:20:13 -0400
Subject: [PATCH] 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]>
---
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 8417de92d..744f33e2a 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 7d1b478c9..77db87dab 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();
}
-- 
2.50.1 (Apple Git-155)

Reply via email to