Re: [PATCH bpf] bpf: Validate BTF repeated field counts before expansion

From: Hou Tao

Date: Mon Jun 08 2026 - 21:31:46 EST


Hi,

On 6/9/2026 3:59 AM, Eduard Zingerman wrote:
> On Sun, 2026-06-07 at 17:53 +0000, Paul Moses wrote:
>
> [...]
>
> The repro is legit.
> Here is a somewhat minimized version as a selftest:
>
> diff --git a/tools/testing/selftests/bpf/prog_tests/btf.c b/tools/testing/selftests/bpf/prog_tests/btf.c
> index a9de328a8697..212ca4472a89 100644
> --- a/tools/testing/selftests/bpf/prog_tests/btf.c
> +++ b/tools/testing/selftests/bpf/prog_tests/btf.c
> @@ -4258,6 +4258,44 @@ static struct btf_raw_test raw_tests[] = {
> .max_entries = 1,
> },
>
> +{
> +#define N 0x1999999aU
> + .descr = "repeat fields overflow",
> + .raw_types = {
> + /* int */ /* [1] */
> + BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),
> + /* struct target {} */ /* [2] */
> + BTF_TYPE_ENC(NAME_TBD, BTF_INFO_ENC(BTF_KIND_STRUCT, 0, 0), 1),
> + /* type_tag "kptr_untrusted" -> target */ /* [3] */
> + BTF_TYPE_TAG_ENC(NAME_TBD, 2),
> + /* target * (kptr) */ /* [4] */
> + BTF_PTR_ENC(3),
> + /* struct outer { target *kp; elem items[N]; } */ /* [5] */
> + BTF_TYPE_ENC(NAME_TBD, BTF_INFO_ENC(BTF_KIND_STRUCT, 0, 2), (N * 8u + 8u)),
> + BTF_MEMBER_ENC(NAME_TBD, 4, 0), /* kp */
> + BTF_MEMBER_ENC(NAME_TBD, 6, 64), /* items */
> + /* elem[N] */ /* [6] */
> + BTF_TYPE_ARRAY_ENC(7, 1, N),
> + /* struct elem { target *f0..f9; } */ /* [7] */
> + BTF_TYPE_ENC(NAME_TBD, BTF_INFO_ENC(BTF_KIND_STRUCT, 0, 10), 8),
> + BTF_MEMBER_ENC(NAME_TBD, 4, 0), /* f0 */
> + BTF_MEMBER_ENC(NAME_TBD, 4, 0), /* f1 */
> + BTF_MEMBER_ENC(NAME_TBD, 4, 0), /* f2 */
> + BTF_MEMBER_ENC(NAME_TBD, 4, 0), /* f3 */
> + BTF_MEMBER_ENC(NAME_TBD, 4, 0), /* f4 */
> + BTF_MEMBER_ENC(NAME_TBD, 4, 0), /* f5 */
> + BTF_MEMBER_ENC(NAME_TBD, 4, 0), /* f6 */
> + BTF_MEMBER_ENC(NAME_TBD, 4, 0), /* f7 */
> + BTF_MEMBER_ENC(NAME_TBD, 4, 0), /* f8 */
> + BTF_MEMBER_ENC(NAME_TBD, 4, 0), /* f9 */
> + BTF_END_RAW,
> + },
> + BTF_STR_SEC("\0target\0kptr_untrusted\0outer\0kp\0items\0elem"
> + "\0f0\0f1\0f2\0f3\0f4\0f5\0f6\0f7\0f8\0f9"),
> + .btf_load_err = true,
> +#undef N
> +},
> +
> }; /* struct btf_raw_test raw_tests[] */
>
> static const char *get_next_str(const char *start, const char *end)
>
> However, as far as I understand the repro hits an overflow only
> because `BTF_TYPE_ENC(NAME_TBD, BTF_INFO_ENC(BTF_KIND_STRUCT, 0, 10), 8)`
> lies about `struct elem` size. It is specified as 8, while in reality it is 80.

No exactly. Every field in the struct elem has the same offset (0), so
the size of struct elem is correct, but the field definition of struct
elem is incorrect.
> The size of 80 would make `struct outer` unrepresentable in BTF,
> because (N * 80 + 8) exceeds u32 range, and that's what btf_type->size uses.
> Given that btf_repeat_fields() only traverses structs/arrays but not unions,
> I suspect that overflow won't happen in `field_cnt * (repeat_cnt + 1)`
> if proper size checks were implemented in btf_struct_check_meta() / btf_struct_resolve().
> Even more, If I change "kptr_untrusted" to "kptr_untrusted11" to avoid fields parsing,
> the kernel accepts the bogus BTF.

btf_struct_check_meta has checked the validity of field offset. However
it seems the checking is loose:

                /*
                 * ">" instead of ">=" because the last member could be
                 * "char a[0];"
                 */
                if (last_offset > offset) {
                        btf_verifier_log_member(env, t, member,
                                                "Invalid member
bits_offset");
                        return -EINVAL;
                }

For struct elem, all fields have the offset 0, so the offset checking is
passed. From the code snippet above, it seems BTF tries to support the
following struct definition below, is it OK to only support the last
zero-sized field in struct definition ?

struct elem {
    int a;
    char b[0];
    int c;
    char d[0];
}


>
> Paul, could you please investigate why is this happening?
> .