diff mbox series

[bpf,RESEND,1/2] bpf: Check the remaining info_cnt before repeating btf fields

Message ID 20241008071114.3718177-2-houtao@huaweicloud.com (mailing list archive)
State Accepted
Commit 797d73ee232dd1833dec4824bc53a22032e97c1c
Delegated to: BPF
Headers show
Series Check the remaining info_cnt before repeating btf fields | expand

Checks

Context Check Description
bpf/vmtest-bpf-PR success PR summary
netdev/series_format success Posting correctly formatted
netdev/tree_selection success Clearly marked for bpf
netdev/ynl success Generated files up to date; no warnings/errors; no diff in generated;
netdev/fixes_present success Fixes tag present in non-next series
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 9 this patch: 9
netdev/build_tools success No tools touched, skip
netdev/cc_maintainers success CCed 13 of 14 maintainers
netdev/build_clang success Errors and warnings before: 7 this patch: 7
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/deprecated_api success None detected
netdev/check_selftest success No net selftest shell script
netdev/verify_fixes success Fixes tag looks correct
netdev/build_allmodconfig_warn success Errors and warnings before: 9 this patch: 9
netdev/checkpatch warning WARNING: line length of 82 exceeds 80 columns
netdev/build_clang_rust success No Rust files in patch. Skipping build
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline success Was 0 now: 0
bpf/vmtest-bpf-VM_Test-2 success Logs for Unittests
bpf/vmtest-bpf-VM_Test-1 success Logs for ShellCheck
bpf/vmtest-bpf-VM_Test-5 success Logs for aarch64-gcc / build-release
bpf/vmtest-bpf-VM_Test-3 success Logs for Validate matrix.py
bpf/vmtest-bpf-VM_Test-0 success Logs for Lint
bpf/vmtest-bpf-VM_Test-10 success Logs for aarch64-gcc / veristat
bpf/vmtest-bpf-VM_Test-4 success Logs for aarch64-gcc / build / build for aarch64 with gcc
bpf/vmtest-bpf-VM_Test-12 success Logs for s390x-gcc / build-release
bpf/vmtest-bpf-VM_Test-9 success Logs for aarch64-gcc / test (test_verifier, false, 360) / test_verifier on aarch64 with gcc
bpf/vmtest-bpf-VM_Test-8 success Logs for aarch64-gcc / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on aarch64 with gcc
bpf/vmtest-bpf-VM_Test-6 success Logs for aarch64-gcc / test (test_maps, false, 360) / test_maps on aarch64 with gcc
bpf/vmtest-bpf-VM_Test-7 success Logs for aarch64-gcc / test (test_progs, false, 360) / test_progs on aarch64 with gcc
bpf/vmtest-bpf-VM_Test-11 success Logs for s390x-gcc / build / build for s390x with gcc
bpf/vmtest-bpf-VM_Test-17 success Logs for set-matrix
bpf/vmtest-bpf-VM_Test-16 success Logs for s390x-gcc / veristat
bpf/vmtest-bpf-VM_Test-19 success Logs for x86_64-gcc / build-release
bpf/vmtest-bpf-VM_Test-18 success Logs for x86_64-gcc / build / build for x86_64 with gcc
bpf/vmtest-bpf-VM_Test-27 success Logs for x86_64-llvm-17 / build / build for x86_64 with llvm-17
bpf/vmtest-bpf-VM_Test-33 success Logs for x86_64-llvm-17 / veristat
bpf/vmtest-bpf-VM_Test-15 success Logs for s390x-gcc / test (test_verifier, false, 360) / test_verifier on s390x with gcc
bpf/vmtest-bpf-VM_Test-28 success Logs for x86_64-llvm-17 / build-release / build for x86_64 with llvm-17-O2
bpf/vmtest-bpf-VM_Test-34 success Logs for x86_64-llvm-18 / build / build for x86_64 with llvm-18
bpf/vmtest-bpf-VM_Test-35 success Logs for x86_64-llvm-18 / build-release / build for x86_64 with llvm-18-O2
bpf/vmtest-bpf-VM_Test-41 success Logs for x86_64-llvm-18 / veristat
bpf/vmtest-bpf-VM_Test-14 success Logs for s390x-gcc / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on s390x with gcc
bpf/vmtest-bpf-VM_Test-13 success Logs for s390x-gcc / test (test_progs, false, 360) / test_progs on s390x with gcc
bpf/vmtest-bpf-VM_Test-22 success Logs for x86_64-gcc / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on x86_64 with gcc
bpf/vmtest-bpf-VM_Test-20 success Logs for x86_64-gcc / test (test_maps, false, 360) / test_maps on x86_64 with gcc
bpf/vmtest-bpf-VM_Test-23 success Logs for x86_64-gcc / test (test_progs_no_alu32_parallel, true, 30) / test_progs_no_alu32_parallel on x86_64 with gcc
bpf/vmtest-bpf-VM_Test-25 success Logs for x86_64-gcc / test (test_verifier, false, 360) / test_verifier on x86_64 with gcc
bpf/vmtest-bpf-VM_Test-24 success Logs for x86_64-gcc / test (test_progs_parallel, true, 30) / test_progs_parallel on x86_64 with gcc
bpf/vmtest-bpf-VM_Test-21 success Logs for x86_64-gcc / test (test_progs, false, 360) / test_progs on x86_64 with gcc
bpf/vmtest-bpf-VM_Test-30 success Logs for x86_64-llvm-17 / test (test_progs, false, 360) / test_progs on x86_64 with llvm-17
bpf/vmtest-bpf-VM_Test-26 success Logs for x86_64-gcc / veristat / veristat on x86_64 with gcc
bpf/vmtest-bpf-VM_Test-32 success Logs for x86_64-llvm-17 / test (test_verifier, false, 360) / test_verifier on x86_64 with llvm-17
bpf/vmtest-bpf-VM_Test-31 success Logs for x86_64-llvm-17 / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on x86_64 with llvm-17
bpf/vmtest-bpf-VM_Test-29 success Logs for x86_64-llvm-17 / test (test_maps, false, 360) / test_maps on x86_64 with llvm-17
bpf/vmtest-bpf-VM_Test-40 success Logs for x86_64-llvm-18 / test (test_verifier, false, 360) / test_verifier on x86_64 with llvm-18
bpf/vmtest-bpf-VM_Test-36 success Logs for x86_64-llvm-18 / test (test_maps, false, 360) / test_maps on x86_64 with llvm-18
bpf/vmtest-bpf-VM_Test-39 success Logs for x86_64-llvm-18 / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on x86_64 with llvm-18
bpf/vmtest-bpf-VM_Test-38 success Logs for x86_64-llvm-18 / test (test_progs_cpuv4, false, 360) / test_progs_cpuv4 on x86_64 with llvm-18
bpf/vmtest-bpf-VM_Test-37 success Logs for x86_64-llvm-18 / test (test_progs, false, 360) / test_progs on x86_64 with llvm-18

Commit Message

Hou Tao Oct. 8, 2024, 7:11 a.m. UTC
From: Hou Tao <houtao1@huawei.com>

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>
---
 kernel/bpf/btf.c | 14 ++++++++++----
 1 file changed, 10 insertions(+), 4 deletions(-)

Comments

Eduard Zingerman Oct. 9, 2024, 6:42 a.m. UTC | #1
On Tue, 2024-10-08 at 15:11 +0800, Hou Tao wrote:
> From: Hou Tao <houtao1@huawei.com>
> 
> 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:

[...]

> 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>

> @@ -3681,10 +3687,10 @@ static int btf_find_field_one(const struct btf *btf,
>  
>  	if (ret == BTF_FIELD_IGNORE)
>  		return 0;
> -	if (nelems > info_cnt)
> +	if (!info_cnt)
>  		return -E2BIG;
>  	if (nelems > 1) {
> -		ret = btf_repeat_fields(info, 1, nelems - 1, sz);
> +		ret = btf_repeat_fields(info, info_cnt, 1, nelems - 1, sz);
>  		if (ret < 0)
>  			return ret;
>  	}


I think the change like below (on top of yours) would work the same
(because nelems is >= 1 at this point):

-       if (!info_cnt)
-               return -E2BIG;
-       if (nelems > 1) {
-               ret = btf_repeat_fields(info, info_cnt, 1, nelems - 1, sz);
-               if (ret < 0)
-                       return ret;
-       }
+
+       ret = btf_repeat_fields(info, info_cnt, 1, nelems - 1, sz);
+       if (ret < 0)
+               return ret;

wdyt?
Hou Tao Oct. 9, 2024, 7:12 a.m. UTC | #2
Hi,

On 10/9/2024 2:42 PM, Eduard Zingerman wrote:
> On Tue, 2024-10-08 at 15:11 +0800, Hou Tao wrote:
>> From: Hou Tao <houtao1@huawei.com>
>>
>> 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:
> [...]
>
>> 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>

Thanks for the ack.
>
>> @@ -3681,10 +3687,10 @@ static int btf_find_field_one(const struct btf *btf,
>>  
>>  	if (ret == BTF_FIELD_IGNORE)
>>  		return 0;
>> -	if (nelems > info_cnt)
>> +	if (!info_cnt)
>>  		return -E2BIG;
>>  	if (nelems > 1) {
>> -		ret = btf_repeat_fields(info, 1, nelems - 1, sz);
>> +		ret = btf_repeat_fields(info, info_cnt, 1, nelems - 1, sz);
>>  		if (ret < 0)
>>  			return ret;
>>  	}
>
> I think the change like below (on top of yours) would work the same
> (because nelems is >= 1 at this point):
>
> -       if (!info_cnt)
> -               return -E2BIG;
> -       if (nelems > 1) {
> -               ret = btf_repeat_fields(info, info_cnt, 1, nelems - 1, sz);
> -               if (ret < 0)
> -                       return ret;
> -       }
> +
> +       ret = btf_repeat_fields(info, info_cnt, 1, nelems - 1, sz);
> +       if (ret < 0)
> +               return ret;
>
> wdyt?

I don't think they are the same. The main reason is due to the check in
the beginning of btf_repeat_field():

        /* Ensure not repeating fields that should not be repeated. */
        for (i = 0; i < field_cnt; i++) {
                switch (info[i].type) {

There are two cases here:
1) info_cnt == 0
Because info_cnt is 0, the found record isn't saved in info[0], the
check will be incorrect

2) nelements ==1 && info_cnt > 0
If the found record is bpf_timer or similar, btf_repeat_fields() will
return -EINVAL instead of 0.
Eduard Zingerman Oct. 9, 2024, 7:26 a.m. UTC | #3
On Wed, 2024-10-09 at 15:12 +0800, Hou Tao wrote:

[...]

> I don't think they are the same. The main reason is due to the check in
> the beginning of btf_repeat_field():
> 
>         /* Ensure not repeating fields that should not be repeated. */
>         for (i = 0; i < field_cnt; i++) {
>                 switch (info[i].type) {
> 
> There are two cases here:
> 1) info_cnt == 0
> Because info_cnt is 0, the found record isn't saved in info[0], the
> check will be incorrect
> 
> 2) nelements ==1 && info_cnt > 0
> If the found record is bpf_timer or similar, btf_repeat_fields() will
> return -EINVAL instead of 0.

Oh, right, there is a loop accessing 'info' at the start.
Sorry for the noise.
diff mbox series

Patch

diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c
index 75e4fe83c509..9cf0ec2e7cb6 100644
--- a/kernel/bpf/btf.c
+++ b/kernel/bpf/btf.c
@@ -3523,7 +3523,7 @@  static int btf_get_field_type(const struct btf *btf, const struct btf_type *var_
  *   (i + 1) * elem_size
  * where i is the repeat index and elem_size is the size of an element.
  */
-static int btf_repeat_fields(struct btf_field_info *info,
+static int btf_repeat_fields(struct btf_field_info *info, int info_cnt,
 			     u32 field_cnt, u32 repeat_cnt, u32 elem_size)
 {
 	u32 i, j;
@@ -3543,6 +3543,12 @@  static int btf_repeat_fields(struct btf_field_info *info,
 		}
 	}
 
+	/* The type of struct size or variable size is u32,
+	 * so the multiplication will not overflow.
+	 */
+	if (field_cnt * (repeat_cnt + 1) > info_cnt)
+		return -E2BIG;
+
 	cur = field_cnt;
 	for (i = 0; i < repeat_cnt; i++) {
 		memcpy(&info[cur], &info[0], field_cnt * sizeof(info[0]));
@@ -3587,7 +3593,7 @@  static int btf_find_nested_struct(const struct btf *btf, const struct btf_type *
 		info[i].off += off;
 
 	if (nelems > 1) {
-		err = btf_repeat_fields(info, ret, nelems - 1, t->size);
+		err = btf_repeat_fields(info, info_cnt, ret, nelems - 1, t->size);
 		if (err == 0)
 			ret *= nelems;
 		else
@@ -3681,10 +3687,10 @@  static int btf_find_field_one(const struct btf *btf,
 
 	if (ret == BTF_FIELD_IGNORE)
 		return 0;
-	if (nelems > info_cnt)
+	if (!info_cnt)
 		return -E2BIG;
 	if (nelems > 1) {
-		ret = btf_repeat_fields(info, 1, nelems - 1, sz);
+		ret = btf_repeat_fields(info, info_cnt, 1, nelems - 1, sz);
 		if (ret < 0)
 			return ret;
 	}