CVE-2022-49658 Information
Description
In the Linux kernel the following vulnerability has been resolved:
bpf: Fix insufficient bounds propagation from adjust_scalar_min_max_vals
Kuee reported a corner case where the tnum becomes constant after the call to __reg_bound_offset() but the register’s bounds are not that is its min bounds are still not equal to the register’s max bounds.
This in turn allows to leak pointers through turning a pointer register as is into an unknown scalar via adjust_ptr_min_max_vals().
Before:
func0 @0 0: R1=ctx(off=0imm=0umax=0var_off=(0x0; 0x0)) R10=fp(off=0imm=0umax=0var_off=(0x0; 0x0)) 0: (b7) r0 = 1 ; R0_w=scalar(imm=1umin=1umax=1var_off=(0x1; 0x0)) 1: (b7) r3 = 0 ; R3_w=scalar(imm=0umax=0var_off=(0x0; 0x0)) 2: (87) r3 = -r3 ; R3_w=scalar() 3: (87) r3 = -r3 ; R3_w=scalar() 4: (47) r3 |= 32767 ; R3_w=scalar(smin=-9223372036854743041umin=32767var_off=(0x7fff; 0xffffffffffff8000)s32_min=-2147450881) 5: (75) if r3 s>= 0x0 goto pc+1 ; R3_w=scalar(umin=9223372036854808575var_off=(0x8000000000007fff; 0x7fffffffffff8000)s32_min=-2147450881u32_min=32767) 6: (95) exit
from 5 to 7: R0=scalar(imm=1umin=1umax=1var_off=(0x1; 0x0)) R1=ctx(off=0imm=0umax=0var_off=(0x0; 0x0)) R3=scalar(umin=32767umax=9223372036854775807var_off=(0x7fff; 0x7fffffffffff8000)s32_min=-2147450881) R10=fp(off=0imm=0umax=0var_off=(0x0; 0x0)) 7: (d5) if r3 s<= 0x8000 goto pc+1 ; R3=scalar(umin=32769umax=9223372036854775807var_off=(0x7fff; 0x7fffffffffff8000)s32_min=-2147450881u32_min=32767) 8: (95) exit
from 7 to 9: R0=scalar(imm=1umin=1umax=1var_off=(0x1; 0x0)) R1=ctx(off=0imm=0umax=0var_off=(0x0; 0x0)) R3=scalar(umin=32767umax=32768var_off=(0x7fff; 0x8000)) R10=fp(off=0imm=0umax=0var_off=(0x0; 0x0)) 9: (07) r3 += -32767 ; R3_w=scalar(imm=0umax=1var_off=(0x0; 0x0)) <— [] 10: (95) exit
What can be seen here is that R3=scalar(umin=32767umax=32768var_off=(0x7fff; 0x8000)) after the operation R3 += -32767 results in a ‘malformed’ constant that is R3_w=scalar(imm=0umax=1var_off=(0x0; 0x0)). Intersecting with var_off has not been done at that point via __update_reg_bounds() which would have improved the umax to be equal to umin.
Refactor the tnum <> min/max bounds information flow into a reg_bounds_sync() helper and use it consistently everywhere. After the fix bounds have been corrected to R3_w=scalar(imm=0umax=0var_off=(0x0; 0x0)) and thus the register is regarded as a ‘proper’ constant scalar of 0.
After:
func0 @0 0: R1=ctx(off=0imm=0umax=0var_off=(0x0; 0x0)) R10=fp(off=0imm=0umax=0var_off=(0x0; 0x0)) 0: (b7) r0 = 1 ; R0_w=scalar(imm=1umin=1umax=1var_off=(0x1; 0x0)) 1: (b7) r3 = 0 ; R3_w=scalar(imm=0umax=0var_off=(0x0; 0x0)) 2: (87) r3 = -r3 ; R3_w=scalar() 3: (87) r3 = -r3 ; R3_w=scalar() 4: (47) r3 |= 32767 ; R3_w=scalar(smin=-9223372036854743041umin=32767var_off=(0x7fff; 0xffffffffffff8000)s32_min=-2147450881) 5: (75) if r3 s>= 0x0 goto pc+1 ; R3_w=scalar(umin=9223372036854808575var_off=(0x8000000000007fff; 0x7fffffffffff8000)s32_min=-2147450881u32_min=32767) 6: (95) exit
from 5 to 7: R0=scalar(imm=1umin=1umax=1var_off=(0x1; 0x0)) R1=ctx(off=0imm=0umax=0var_off=(0x0; 0x0)) R3=scalar(umin=32767umax=9223372036854775807var_off=(0x7fff; 0x7fffffffffff8000)s32_min=-2147450881) R10=fp(off=0imm=0umax=0var_off=(0x0; 0x0)) 7: (d5) if r3 s<= 0x8000 goto pc+1 ; R3=scalar(umin=32769umax=9223372036854775807var_off=(0x7fff; 0x7fffffffffff8000)s32_min=-2147450881u32_min=32767) 8: (95) exit
from 7 to 9: R0=scalar(imm=1umin=1umax=1var_off=(0x1; 0x0)) R1=ctx(off=0imm=0umax=0var_off=(0x0; 0x0)) R3=scalar(umin=32767umax=32768var_off=(0x7fff; 0x8000)) R10=fp(off=0
truncated—
Reference
https://git.kernel.org/stable/c/3844d153a41adea718202c10ae91dc96b37453b5 https://git.kernel.org/stable/c/a7de8d436db92bab8b1f44624297c2554a6ac36b https://git.kernel.org/stable/c/b2a28bb36664c94375926cbbb91976242847699d https://git.kernel.org/stable/c/e917be1f83ea14a68b3cf64d3da9968eaf991dae
Share on: