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

bpf: Fix tnum constraints for 32-bit comparisons

The BPF verifier tried to track values based on 32-bit comparisons by
(ab)using the tnum state via 581738a681b6 ("bpf: Provide better register
bounds after jmp32 instructions"). The idea is that after a check like
this:

if ((u32)r0 > 3)
exit

We can't meaningfully constrain the arithmetic-range-based tracking, but
we can update the tnum state to (value=0,mask=0xffff'ffff'0000'0003).
However, the implementation from 581738a681b6 didn't compute the tnum
constraint based on the fixed operand, but instead derives it from the
arithmetic-range-based tracking. This means that after the following
sequence of operations:

if (r0 >= 0x1'0000'0001)
exit
if ((u32)r0 > 7)
exit

The verifier assumed that the lower half of r0 is in the range (0, 0)
and apply the tnum constraint (value=0,mask=0xffff'ffff'0000'0000) thus
causing the overall tnum to be (value=0,mask=0x1'0000'0000), which was
incorrect. Provide a fixed implementation.

Fixes: 581738a681b6 ("bpf: Provide better register bounds after jmp32 instructions")
Signed-off-by: Jann Horn <jannh@google.com>
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Link: https://lore.kernel.org/bpf/20200330160324.15259-3-daniel@iogearbox.net

authored by

Jann Horn and committed by
Alexei Starovoitov
604dca5e f2d67fec

+72 -36
+72 -36
kernel/bpf/verifier.c
··· 5678 5678 reg->smax_value <= 0 && reg->smin_value >= S32_MIN); 5679 5679 } 5680 5680 5681 + /* Constrain the possible values of @reg with unsigned upper bound @bound. 5682 + * If @is_exclusive, @bound is an exclusive limit, otherwise it is inclusive. 5683 + * If @is_jmp32, @bound is a 32-bit value that only constrains the low 32 bits 5684 + * of @reg. 5685 + */ 5686 + static void set_upper_bound(struct bpf_reg_state *reg, u64 bound, bool is_jmp32, 5687 + bool is_exclusive) 5688 + { 5689 + if (is_exclusive) { 5690 + /* There are no values for `reg` that make `reg<0` true. */ 5691 + if (bound == 0) 5692 + return; 5693 + bound--; 5694 + } 5695 + if (is_jmp32) { 5696 + /* Constrain the register's value in the tnum representation. 5697 + * For 64-bit comparisons this happens later in 5698 + * __reg_bound_offset(), but for 32-bit comparisons, we can be 5699 + * more precise than what can be derived from the updated 5700 + * numeric bounds. 5701 + */ 5702 + struct tnum t = tnum_range(0, bound); 5703 + 5704 + t.mask |= ~0xffffffffULL; /* upper half is unknown */ 5705 + reg->var_off = tnum_intersect(reg->var_off, t); 5706 + 5707 + /* Compute the 64-bit bound from the 32-bit bound. */ 5708 + bound += gen_hi_max(reg->var_off); 5709 + } 5710 + reg->umax_value = min(reg->umax_value, bound); 5711 + } 5712 + 5713 + /* Constrain the possible values of @reg with unsigned lower bound @bound. 5714 + * If @is_exclusive, @bound is an exclusive limit, otherwise it is inclusive. 5715 + * If @is_jmp32, @bound is a 32-bit value that only constrains the low 32 bits 5716 + * of @reg. 5717 + */ 5718 + static void set_lower_bound(struct bpf_reg_state *reg, u64 bound, bool is_jmp32, 5719 + bool is_exclusive) 5720 + { 5721 + if (is_exclusive) { 5722 + /* There are no values for `reg` that make `reg>MAX` true. */ 5723 + if (bound == (is_jmp32 ? U32_MAX : U64_MAX)) 5724 + return; 5725 + bound++; 5726 + } 5727 + if (is_jmp32) { 5728 + /* Constrain the register's value in the tnum representation. 5729 + * For 64-bit comparisons this happens later in 5730 + * __reg_bound_offset(), but for 32-bit comparisons, we can be 5731 + * more precise than what can be derived from the updated 5732 + * numeric bounds. 5733 + */ 5734 + struct tnum t = tnum_range(bound, U32_MAX); 5735 + 5736 + t.mask |= ~0xffffffffULL; /* upper half is unknown */ 5737 + reg->var_off = tnum_intersect(reg->var_off, t); 5738 + 5739 + /* Compute the 64-bit bound from the 32-bit bound. */ 5740 + bound += gen_hi_min(reg->var_off); 5741 + } 5742 + reg->umin_value = max(reg->umin_value, bound); 5743 + } 5744 + 5681 5745 /* Adjusts the register min/max values in the case that the dst_reg is the 5682 5746 * variable register that we are working on, and src_reg is a constant or we're 5683 5747 * simply doing a BPF_K check. ··· 5797 5733 case BPF_JGE: 5798 5734 case BPF_JGT: 5799 5735 { 5800 - u64 false_umax = opcode == BPF_JGT ? val : val - 1; 5801 - u64 true_umin = opcode == BPF_JGT ? val + 1 : val; 5802 - 5803 - if (is_jmp32) { 5804 - false_umax += gen_hi_max(false_reg->var_off); 5805 - true_umin += gen_hi_min(true_reg->var_off); 5806 - } 5807 - false_reg->umax_value = min(false_reg->umax_value, false_umax); 5808 - true_reg->umin_value = max(true_reg->umin_value, true_umin); 5736 + set_upper_bound(false_reg, val, is_jmp32, opcode == BPF_JGE); 5737 + set_lower_bound(true_reg, val, is_jmp32, opcode == BPF_JGT); 5809 5738 break; 5810 5739 } 5811 5740 case BPF_JSGE: ··· 5819 5762 case BPF_JLE: 5820 5763 case BPF_JLT: 5821 5764 { 5822 - u64 false_umin = opcode == BPF_JLT ? val : val + 1; 5823 - u64 true_umax = opcode == BPF_JLT ? val - 1 : val; 5824 - 5825 - if (is_jmp32) { 5826 - false_umin += gen_hi_min(false_reg->var_off); 5827 - true_umax += gen_hi_max(true_reg->var_off); 5828 - } 5829 - false_reg->umin_value = max(false_reg->umin_value, false_umin); 5830 - true_reg->umax_value = min(true_reg->umax_value, true_umax); 5765 + set_lower_bound(false_reg, val, is_jmp32, opcode == BPF_JLE); 5766 + set_upper_bound(true_reg, val, is_jmp32, opcode == BPF_JLT); 5831 5767 break; 5832 5768 } 5833 5769 case BPF_JSLE: ··· 5895 5845 case BPF_JGE: 5896 5846 case BPF_JGT: 5897 5847 { 5898 - u64 false_umin = opcode == BPF_JGT ? val : val + 1; 5899 - u64 true_umax = opcode == BPF_JGT ? val - 1 : val; 5900 - 5901 - if (is_jmp32) { 5902 - false_umin += gen_hi_min(false_reg->var_off); 5903 - true_umax += gen_hi_max(true_reg->var_off); 5904 - } 5905 - false_reg->umin_value = max(false_reg->umin_value, false_umin); 5906 - true_reg->umax_value = min(true_reg->umax_value, true_umax); 5848 + set_lower_bound(false_reg, val, is_jmp32, opcode == BPF_JGE); 5849 + set_upper_bound(true_reg, val, is_jmp32, opcode == BPF_JGT); 5907 5850 break; 5908 5851 } 5909 5852 case BPF_JSGE: ··· 5914 5871 case BPF_JLE: 5915 5872 case BPF_JLT: 5916 5873 { 5917 - u64 false_umax = opcode == BPF_JLT ? val : val - 1; 5918 - u64 true_umin = opcode == BPF_JLT ? val + 1 : val; 5919 - 5920 - if (is_jmp32) { 5921 - false_umax += gen_hi_max(false_reg->var_off); 5922 - true_umin += gen_hi_min(true_reg->var_off); 5923 - } 5924 - false_reg->umax_value = min(false_reg->umax_value, false_umax); 5925 - true_reg->umin_value = max(true_reg->umin_value, true_umin); 5874 + set_upper_bound(false_reg, val, is_jmp32, opcode == BPF_JLE); 5875 + set_lower_bound(true_reg, val, is_jmp32, opcode == BPF_JLT); 5926 5876 break; 5927 5877 } 5928 5878 case BPF_JSLE: