Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

bpf: Check the remaining info_cnt before repeating btf fields

When trying to repeat the btf fields for array of nested struct, it
doesn't check the remaining info_cnt. The following splat will be
reported when the value of ret * nelems is greater than BTF_FIELDS_MAX:

------------[ cut here ]------------
UBSAN: array-index-out-of-bounds in ../kernel/bpf/btf.c:3951:49
index 11 is out of range for type 'btf_field_info [11]'
CPU: 6 UID: 0 PID: 411 Comm: test_progs ...... 6.11.0-rc4+ #1
Tainted: [O]=OOT_MODULE
Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS ...
Call Trace:
<TASK>
dump_stack_lvl+0x57/0x70
dump_stack+0x10/0x20
ubsan_epilogue+0x9/0x40
__ubsan_handle_out_of_bounds+0x6f/0x80
? kallsyms_lookup_name+0x48/0xb0
btf_parse_fields+0x992/0xce0
map_create+0x591/0x770
__sys_bpf+0x229/0x2410
__x64_sys_bpf+0x1f/0x30
x64_sys_call+0x199/0x9f0
do_syscall_64+0x3b/0xc0
entry_SYSCALL_64_after_hwframe+0x4b/0x53
RIP: 0033:0x7fea56f2cc5d
......
</TASK>
---[ end trace ]---

Fix it by checking the remaining info_cnt in btf_repeat_fields() before
repeating the btf fields.

Fixes: 64e8ee814819 ("bpf: look into the types of the fields of a struct type recursively.")
Signed-off-by: Hou Tao <houtao1@huawei.com>
Acked-by: Eduard Zingerman <eddyz87@gmail.com>
Link: https://lore.kernel.org/r/20241008071114.3718177-2-houtao@huaweicloud.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>

authored by

Hou Tao and committed by
Alexei Starovoitov
797d73ee b24d7f0d

+10 -4
+10 -4
kernel/bpf/btf.c
··· 3523 3523 * (i + 1) * elem_size 3524 3524 * where i is the repeat index and elem_size is the size of an element. 3525 3525 */ 3526 - static int btf_repeat_fields(struct btf_field_info *info, 3526 + static int btf_repeat_fields(struct btf_field_info *info, int info_cnt, 3527 3527 u32 field_cnt, u32 repeat_cnt, u32 elem_size) 3528 3528 { 3529 3529 u32 i, j; ··· 3542 3542 return -EINVAL; 3543 3543 } 3544 3544 } 3545 + 3546 + /* The type of struct size or variable size is u32, 3547 + * so the multiplication will not overflow. 3548 + */ 3549 + if (field_cnt * (repeat_cnt + 1) > info_cnt) 3550 + return -E2BIG; 3545 3551 3546 3552 cur = field_cnt; 3547 3553 for (i = 0; i < repeat_cnt; i++) { ··· 3593 3587 info[i].off += off; 3594 3588 3595 3589 if (nelems > 1) { 3596 - err = btf_repeat_fields(info, ret, nelems - 1, t->size); 3590 + err = btf_repeat_fields(info, info_cnt, ret, nelems - 1, t->size); 3597 3591 if (err == 0) 3598 3592 ret *= nelems; 3599 3593 else ··· 3687 3681 3688 3682 if (ret == BTF_FIELD_IGNORE) 3689 3683 return 0; 3690 - if (nelems > info_cnt) 3684 + if (!info_cnt) 3691 3685 return -E2BIG; 3692 3686 if (nelems > 1) { 3693 - ret = btf_repeat_fields(info, 1, nelems - 1, sz); 3687 + ret = btf_repeat_fields(info, info_cnt, 1, nelems - 1, sz); 3694 3688 if (ret < 0) 3695 3689 return ret; 3696 3690 }