Thread (3 messages) 3 messages, 2 authors, 2026-04-20

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
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help