mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Paul Moses <p@1g4.org>
To: martin.lau@linux.dev, ast@kernel.org, daniel@iogearbox.net,
	andrii@kernel.org, eddyz87@gmail.com, memxor@gmail.com,
	bpf@vger.kernel.org
Cc: song@kernel.org, yonghong.song@linux.dev, jolsa@kernel.org,
	houtao1@huawei.com, linux-kernel@vger.kernel.org,
	Paul Moses <p@1g4.org>,
	stable@vger.kernel.org
Subject: [PATCH bpf v2] bpf: Validate BTF repeated field counts before expansion
Date: Wed, 10 Jun 2026 08:14:47 +0000	[thread overview]
Message-ID: <20260610081434.2141515-1-p@1g4.org> (raw)

btf_parse_struct_metas() walks user supplied BTF during BPF_BTF_LOAD,
and btf_repeat_fields() expands repeatable fields from array elements
into the fixed BTF_FIELDS_MAX scratch array used by btf_parse_fields().

The remaining-capacity check performs the expanded field count calculation
in u32. A malformed BTF can wrap that calculation, causing the check to
pass even when the expanded field count exceeds the scratch array
capacity. The following memcpy() can then write past the end of the
array.

Use checked multiplication before copying repeated fields and reject 
impossible counts.

Add a raw BTF test that exercises repeated special-field expansion with a
large array count. The compact element layout keeps the array byte size
representable while the repeated field count overflows the old u32 capacity
calculation in btf_repeat_fields().

Fixes: 797d73ee232d ("bpf: Check the remaining info_cnt before repeating btf fields")
Cc: stable@vger.kernel.org
Signed-off-by: Paul Moses <p@1g4.org>

---
v1..v2
1. Combine fix and test
2. Drop check_add_overflow
---
 kernel/bpf/btf.c                             |  8 ++---
 tools/testing/selftests/bpf/prog_tests/btf.c | 37 ++++++++++++++++++++
 2 files changed, 40 insertions(+), 5 deletions(-)

diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c
index a62d78581207..7a28886f1307 100644
--- a/kernel/bpf/btf.c
+++ b/kernel/bpf/btf.c
@@ -3668,7 +3668,7 @@ static int btf_get_field_type(const struct btf *btf, const struct btf_type *var_
 static int btf_repeat_fields(struct btf_field_info *info, int info_cnt,
 			     u32 field_cnt, u32 repeat_cnt, u32 elem_size)
 {
-	u32 i, j;
+	u32 i, j, total_cnt;
 	u32 cur;
 
 	/* Ensure not repeating fields that should not be repeated. */
@@ -3686,10 +3686,8 @@ static int btf_repeat_fields(struct btf_field_info *info, int info_cnt,
 		}
 	}
 
-	/* The type of struct size or variable size is u32,
-	 * so the multiplication will not overflow.
-	 */
-	if (field_cnt * (repeat_cnt + 1) > info_cnt)
+	if (check_mul_overflow(field_cnt, repeat_cnt + 1, &total_cnt) ||
+	    total_cnt > (u32)info_cnt)
 		return -E2BIG;
 
 	cur = field_cnt;
diff --git a/tools/testing/selftests/bpf/prog_tests/btf.c b/tools/testing/selftests/bpf/prog_tests/btf.c
index 054ecb6b1e9f..9fcbc554e351 100644
--- a/tools/testing/selftests/bpf/prog_tests/btf.c
+++ b/tools/testing/selftests/bpf/prog_tests/btf.c
@@ -4258,6 +4258,43 @@ static struct btf_raw_test raw_tests[] = {
 	.max_entries = 1,
 },
 
+{
+	.descr = "struct test repeated fields count overflow",
+	.raw_types = {
+		BTF_TYPE_INT_ENC(NAME_TBD, BTF_INT_SIGNED, 0, 32, 4),	/* [1] */
+		BTF_STRUCT_ENC(NAME_TBD, 0, 0),				/* [2] */
+		BTF_TYPE_TAG_ENC(NAME_TBD, 2),				/* [3] */
+		BTF_PTR_ENC(3),						/* [4] */
+		BTF_TYPE_ARRAY_ENC(4, 1, 1),				/* [5] */
+		BTF_STRUCT_ENC(NAME_TBD, 10, 8),			/* [6] */
+		BTF_MEMBER_ENC(NAME_TBD, 5, 0),
+		BTF_MEMBER_ENC(NAME_TBD, 5, 0),
+		BTF_MEMBER_ENC(NAME_TBD, 5, 0),
+		BTF_MEMBER_ENC(NAME_TBD, 5, 0),
+		BTF_MEMBER_ENC(NAME_TBD, 5, 0),
+		BTF_MEMBER_ENC(NAME_TBD, 5, 0),
+		BTF_MEMBER_ENC(NAME_TBD, 5, 0),
+		BTF_MEMBER_ENC(NAME_TBD, 5, 0),
+		BTF_MEMBER_ENC(NAME_TBD, 5, 0),
+		BTF_MEMBER_ENC(NAME_TBD, 5, 0),
+		BTF_TYPE_ARRAY_ENC(6, 1, 0x1999999aU),			/* [7] */
+		BTF_STRUCT_ENC(NAME_TBD, 2, 8 + 8 * 0x1999999aU),	/* [8] */
+		BTF_MEMBER_ENC(NAME_TBD, 4, 0),
+		BTF_MEMBER_ENC(NAME_TBD, 7, 64),
+		BTF_END_RAW,
+	},
+	BTF_STR_SEC("\0int\0prog_test_ref_kfunc\0kptr_untrusted\0elem"
+		    "\0p0\0p1\0p2\0p3\0p4\0p5\0p6\0p7\0p8\0p9"
+		    "\0outer\0trigger\0elems"),
+	.map_type = BPF_MAP_TYPE_ARRAY,
+	.map_name = "repeat_fields",
+	.key_size = sizeof(int),
+	.value_size = 8 + 8 * 0x1999999aU,
+	.key_type_id = 1,
+	.value_type_id = 8,
+	.max_entries = 1,
+	.btf_load_err = true,
+},
 }; /* struct btf_raw_test raw_tests[] */
 
 static const char *get_next_str(const char *start, const char *end)
-- 
2.54.0



             reply	other threads:[~2026-06-10  8:15 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-06-10  8:14 Paul Moses [this message]
2026-06-10  8:18 ` Kumar Kartikeya Dwivedi

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260610081434.2141515-1-p@1g4.org \
    --to=p@1g4.org \
    --cc=andrii@kernel.org \
    --cc=ast@kernel.org \
    --cc=bpf@vger.kernel.org \
    --cc=daniel@iogearbox.net \
    --cc=eddyz87@gmail.com \
    --cc=houtao1@huawei.com \
    --cc=jolsa@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=martin.lau@linux.dev \
    --cc=memxor@gmail.com \
    --cc=song@kernel.org \
    --cc=stable@vger.kernel.org \
    --cc=yonghong.song@linux.dev \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®