Re: [PATCH v3 bpf-next 2/6] libbpf: Adjust btf_vlen() to return a __u32

Alan Maguire <[email protected]>
Newsgroups dev.linux.lists.sashiko,org.kernel.vger.bpf
Message-ID <[email protected]>
On 17/04/2026 18:07, [email protected] 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 <[email protected]>
> 
> 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().
> 
>> 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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.