Re: [PATCH v3 bpf-next 2/6] libbpf: Adjust btf_vlen() to return a __u32
From: Alan Maguire <hidden>
Date: 2026-04-20 08:03:06
Also in:
bpf
Subsystem:
bpf [general] (safe dynamic programs and tools), bpf [library] (libbpf), the rest · Maintainers:
Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko, Eduard Zingerman, Kumar Kartikeya Dwivedi, Linus Torvalds
On 17/04/2026 18:07, sashiko-bot@kernel.org wrote:
Sashiko AI review found 1 potential issue(s): - [High] On 32-bit systems, `btf_parse_type_sec()` is vulnerable to pointer arithmetic wrap-around due to missing strict bounds checking, allowing an attacker-crafted BTF layout to bypass safety checks and cause out-of-bounds memory access. -- commit 0d6d9306686384cb94334ae8845f954db44a9f74 Author: Alan Maguire [off-list ref] libbpf: Adjust btf_vlen() to return a __u32 Now that vlen is 24 bits, btf_vlen() must return a __u32. Adjust use cases in libbpf accordingly. Also add error handling to avoid vlen overflow in btf_type_inc_vlen().quoted
diff --git a/tools/lib/bpf/btf.c b/tools/lib/bpf/btf.c index ceb57b46a8782..267904939098c 100644 --- a/tools/lib/bpf/btf.c +++ b/tools/lib/bpf/btf.c@@ -421,7 +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; - __u16 vlen = btf_vlen(t); + __u32 vlen = btf_vlen(t); __u32 kind = btf_kind(t);Can this lead to a pointer arithmetic wrap-around regression on 32-bit systems? The size is calculated later in this function using unsigned 32-bit arithmetic and returned as a signed int: return sizeof(struct btf_type) + l[kind].info_sz + vlen * l[kind].elem_sz; An attacker could provide a custom layout with an artificially large elem_sz (e.g., 2,000,000,000). For a type with vlen = 1, the calculated size fits within a positive signed 32-bit int and is returned as a valid positive type_size. When btf_parse_type_sec() checks if next_type + type_size > end_type, it evaluates this using 32-bit pointer arithmetic. If next_type is located high in the address space, adding 2,000,000,000 wraps around the 32-bit boundary to a numerically smaller low address, incorrectly passing the bounds check. This causes next_type to advance to the wrapped address, allowing subsequent loop iterations to read arbitrary memory and potentially cause out-of-bounds accesses or crashes. While this parser logic issue predates this patch, should we address it here by safely checking the offset instead? For example: if ((size_t)type_size > (size_t)((char *)end_type - (char *)next_type))
Ok so I think the suggestion is the following in btf_parse_type_sec() is the problem:
type_size = btf_type_size(btf, next_type);
if (type_size < 0)
return type_size;
if (next_type + type_size > end_type) {
...specifically the addition of next_type to type_size in the last line above.
A sufficiently high type_size will cause an address wrap on 32-bit systems and
we end up not triggering the bounds check. That seems like an existing issue to
me; even the example cited describes a vlen of 1 with a large elem_sz from the
layout section.
The proposed solution seems to be to check if type_size falls outside of the
remaining space in the types section. It might make sense to do that check locally
in btf_type_size_unknown() instead for clarity and safety. Something like the
following:
diff --git a/tools/lib/bpf/btf.c b/tools/lib/bpf/btf.c
index 267904939098..7d0dfb2810e9 100644
--- a/tools/lib/bpf/btf.c
+++ b/tools/lib/bpf/btf.c@@ -420,9 +420,11 @@ static int btf_parse_layout_sec(struct btf *btf) 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); + const void *end_type = btf->types_data + btf->hdr.type_len; struct btf_layout *l = btf->layout; __u32 vlen = btf_vlen(t); __u32 kind = btf_kind(t); + int type_size; /* Fall back to base BTF if needed as they share layout information */ if (!l) {
@@ -448,7 +450,13 @@ 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 + vlen * l[kind].elem_sz; + if ((size_t)type_size > (size_t)(end_type - (const void *)t)) { + pr_debug("Overflow in type size %d for kind %u\n", + type_size, kind); + return -EINVAL; + } + return type_size; }
Given that this problem exists separate from the extended vlen series, might make more sense to send a separate patch targeting the bpf tree for this issue. Alan