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

Configure Feed

Select the types of activity you want to include in your feed.

Merge branch 'fix-invariant-violations-and-improve-branch-detection'

Paul Chaignon says:

====================
Fix invariant violations and improve branch detection

This patchset fixes invariant violations on register bounds. These
invariant violations cause a warning and happen when reg_bounds_sync is
trying to refine register bounds while walking an impossible branch.

This patchset takes this situation as an opportunity to improve
verification performance. That is, the verifier will use the invariant
violations as a signal that a branch cannot be taken and process it as
dead code.

This patchset implements this approach and covers it in selftests with
a new invariant violation case. Some of the logic in reg_bounds_sync
likely acts as a duplicate with logic from is_scalar_branch_taken. This
patchset does not attempt to remove superfluous logic from
is_scalar_branch_taken and leaves it to a future patchset (ex. once
syzbot has confirmed that all invariant violations are fixed).

In the future, there is also a potential opportunity to simplify
existing logic by merging reg_bounds_sync and range_bounds_violation
(have reg_bounds_sync error out on invariant violation). That is
however not needed to fix invariant violation, which we focus on in
this patchset.

Changes in v3:
- Rename and refactor the helper functions checking for tnum-related
invariant violations (Mykyta).
- Small changes to comment style in verifier changes and new selftest
(Mykyta).
- Rebased.
Changes in v2:
- Moved tmp registers to env in preparatory commit (Eduard).
- Updated reg_bounds_sync to bail out in case of ill-formed
registers, thus avoiding one set of invariant violation checks in
simulate_both_branches_taken (Eduard).
- Drop the Fixes tag to avoid misleading backporters (Shung-Hsi).
- Improve wording of commit descriptions (Shung-Hsi, Hari).
- Fix error in code comments (AI bot).
- Rebased.
====================

Link: https://patch.msgid.link/cover.1775142354.git.paul.chaignon@gmail.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>

+148 -108
+3 -1
include/linux/bpf_verifier.h
··· 837 837 u64 scratched_stack_slots; 838 838 u64 prev_log_pos, prev_insn_print_pos; 839 839 /* buffer used to temporary hold constants as scalar registers */ 840 - struct bpf_reg_state fake_reg[2]; 840 + struct bpf_reg_state fake_reg[1]; 841 + /* buffers used to save updated reg states while simulating branches */ 842 + struct bpf_reg_state true_reg1, true_reg2, false_reg1, false_reg2; 841 843 /* buffer used to generate temporary string representations, 842 844 * e.g., in reg_type_str() to generate reg_type string 843 845 */
+110 -91
kernel/bpf/verifier.c
··· 2788 2788 reg->var_off = tnum_or(tnum_clear_subreg(var64_off), var32_off); 2789 2789 } 2790 2790 2791 + static bool range_bounds_violation(struct bpf_reg_state *reg); 2792 + 2791 2793 static void reg_bounds_sync(struct bpf_reg_state *reg) 2792 2794 { 2795 + /* If the input reg_state is invalid, we can exit early */ 2796 + if (range_bounds_violation(reg)) 2797 + return; 2793 2798 /* We might have learned new bounds from the var_off. */ 2794 2799 __update_reg_bounds(reg); 2795 2800 /* We might have learned something about the sign bit. */ ··· 2809 2804 __update_reg_bounds(reg); 2810 2805 } 2811 2806 2807 + static bool range_bounds_violation(struct bpf_reg_state *reg) 2808 + { 2809 + return (reg->umin_value > reg->umax_value || reg->smin_value > reg->smax_value || 2810 + reg->u32_min_value > reg->u32_max_value || 2811 + reg->s32_min_value > reg->s32_max_value); 2812 + } 2813 + 2814 + static bool const_tnum_range_mismatch(struct bpf_reg_state *reg) 2815 + { 2816 + u64 uval = reg->var_off.value; 2817 + s64 sval = (s64)uval; 2818 + 2819 + if (!tnum_is_const(reg->var_off)) 2820 + return false; 2821 + 2822 + return reg->umin_value != uval || reg->umax_value != uval || 2823 + reg->smin_value != sval || reg->smax_value != sval; 2824 + } 2825 + 2826 + static bool const_tnum_range_mismatch_32(struct bpf_reg_state *reg) 2827 + { 2828 + u32 uval32 = tnum_subreg(reg->var_off).value; 2829 + s32 sval32 = (s32)uval32; 2830 + 2831 + if (!tnum_subreg_is_const(reg->var_off)) 2832 + return false; 2833 + 2834 + return reg->u32_min_value != uval32 || reg->u32_max_value != uval32 || 2835 + reg->s32_min_value != sval32 || reg->s32_max_value != sval32; 2836 + } 2837 + 2812 2838 static int reg_bounds_sanity_check(struct bpf_verifier_env *env, 2813 2839 struct bpf_reg_state *reg, const char *ctx) 2814 2840 { 2815 2841 const char *msg; 2816 2842 2817 - if (reg->umin_value > reg->umax_value || 2818 - reg->smin_value > reg->smax_value || 2819 - reg->u32_min_value > reg->u32_max_value || 2820 - reg->s32_min_value > reg->s32_max_value) { 2821 - msg = "range bounds violation"; 2822 - goto out; 2843 + if (range_bounds_violation(reg)) { 2844 + msg = "range bounds violation"; 2845 + goto out; 2823 2846 } 2824 2847 2825 - if (tnum_is_const(reg->var_off)) { 2826 - u64 uval = reg->var_off.value; 2827 - s64 sval = (s64)uval; 2828 - 2829 - if (reg->umin_value != uval || reg->umax_value != uval || 2830 - reg->smin_value != sval || reg->smax_value != sval) { 2831 - msg = "const tnum out of sync with range bounds"; 2832 - goto out; 2833 - } 2848 + if (const_tnum_range_mismatch(reg)) { 2849 + msg = "const tnum out of sync with range bounds"; 2850 + goto out; 2834 2851 } 2835 2852 2836 - if (tnum_subreg_is_const(reg->var_off)) { 2837 - u32 uval32 = tnum_subreg(reg->var_off).value; 2838 - s32 sval32 = (s32)uval32; 2839 - 2840 - if (reg->u32_min_value != uval32 || reg->u32_max_value != uval32 || 2841 - reg->s32_min_value != sval32 || reg->s32_max_value != sval32) { 2842 - msg = "const subreg tnum out of sync with range bounds"; 2843 - goto out; 2844 - } 2853 + if (const_tnum_range_mismatch_32(reg)) { 2854 + msg = "const subreg tnum out of sync with range bounds"; 2855 + goto out; 2845 2856 } 2846 2857 2847 2858 return 0; ··· 16786 16765 })); 16787 16766 } 16788 16767 16768 + static void regs_refine_cond_op(struct bpf_reg_state *reg1, struct bpf_reg_state *reg2, 16769 + u8 opcode, bool is_jmp32); 16770 + static u8 rev_opcode(u8 opcode); 16771 + 16772 + /* 16773 + * Learn more information about live branches by simulating refinement on both branches. 16774 + * regs_refine_cond_op() is sound, so producing ill-formed register bounds for the branch means 16775 + * that branch is dead. 16776 + */ 16777 + static int simulate_both_branches_taken(struct bpf_verifier_env *env, u8 opcode, bool is_jmp32) 16778 + { 16779 + /* Fallthrough (FALSE) branch */ 16780 + regs_refine_cond_op(&env->false_reg1, &env->false_reg2, rev_opcode(opcode), is_jmp32); 16781 + reg_bounds_sync(&env->false_reg1); 16782 + reg_bounds_sync(&env->false_reg2); 16783 + /* 16784 + * If there is a range bounds violation in *any* of the abstract values in either 16785 + * reg_states in the FALSE branch (i.e. reg1, reg2), the FALSE branch must be dead. Only 16786 + * TRUE branch will be taken. 16787 + */ 16788 + if (range_bounds_violation(&env->false_reg1) || range_bounds_violation(&env->false_reg2)) 16789 + return 1; 16790 + 16791 + /* Jump (TRUE) branch */ 16792 + regs_refine_cond_op(&env->true_reg1, &env->true_reg2, opcode, is_jmp32); 16793 + reg_bounds_sync(&env->true_reg1); 16794 + reg_bounds_sync(&env->true_reg2); 16795 + /* 16796 + * If there is a range bounds violation in *any* of the abstract values in either 16797 + * reg_states in the TRUE branch (i.e. true_reg1, true_reg2), the TRUE branch must be dead. 16798 + * Only FALSE branch will be taken. 16799 + */ 16800 + if (range_bounds_violation(&env->true_reg1) || range_bounds_violation(&env->true_reg2)) 16801 + return 0; 16802 + 16803 + /* Both branches are possible, we can't determine which one will be taken. */ 16804 + return -1; 16805 + } 16806 + 16789 16807 /* 16790 16808 * <reg1> <op> <reg2>, currently assuming reg2 is a constant 16791 16809 */ 16792 - static int is_scalar_branch_taken(struct bpf_reg_state *reg1, struct bpf_reg_state *reg2, 16793 - u8 opcode, bool is_jmp32) 16810 + static int is_scalar_branch_taken(struct bpf_verifier_env *env, struct bpf_reg_state *reg1, 16811 + struct bpf_reg_state *reg2, u8 opcode, bool is_jmp32) 16794 16812 { 16795 16813 struct tnum t1 = is_jmp32 ? tnum_subreg(reg1->var_off) : reg1->var_off; 16796 16814 struct tnum t2 = is_jmp32 ? tnum_subreg(reg2->var_off) : reg2->var_off; ··· 16981 16921 break; 16982 16922 } 16983 16923 16984 - return -1; 16924 + return simulate_both_branches_taken(env, opcode, is_jmp32); 16985 16925 } 16986 16926 16987 16927 static int flip_opcode(u32 opcode) ··· 17052 16992 * -1 - unknown. Example: "if (reg1 < 5)" is unknown when register value 17053 16993 * range [0,10] 17054 16994 */ 17055 - static int is_branch_taken(struct bpf_reg_state *reg1, struct bpf_reg_state *reg2, 17056 - u8 opcode, bool is_jmp32) 16995 + static int is_branch_taken(struct bpf_verifier_env *env, struct bpf_reg_state *reg1, 16996 + struct bpf_reg_state *reg2, u8 opcode, bool is_jmp32) 17057 16997 { 17058 16998 if (reg_is_pkt_pointer_any(reg1) && reg_is_pkt_pointer_any(reg2) && !is_jmp32) 17059 16999 return is_pkt_ptr_branch_taken(reg1, reg2, opcode); ··· 17091 17031 } 17092 17032 17093 17033 /* now deal with two scalars, but not necessarily constants */ 17094 - return is_scalar_branch_taken(reg1, reg2, opcode, is_jmp32); 17034 + return is_scalar_branch_taken(env, reg1, reg2, opcode, is_jmp32); 17095 17035 } 17096 17036 17097 17037 /* Opcode that corresponds to a *false* branch condition. ··· 17182 17122 /* u32_min_value is not equal to 0xffffffff at this point, 17183 17123 * because otherwise u32_max_value is 0xffffffff as well, 17184 17124 * in such a case both reg1 and reg2 would be constants, 17185 - * jump would be predicted and reg_set_min_max() won't 17186 - * be called. 17125 + * jump would be predicted and regs_refine_cond_op() 17126 + * wouldn't be called. 17187 17127 * 17188 17128 * Same reasoning works for all {u,s}{min,max}{32,64} cases 17189 17129 * below. ··· 17290 17230 } 17291 17231 } 17292 17232 17293 - /* Adjusts the register min/max values in the case that the dst_reg and 17294 - * src_reg are both SCALAR_VALUE registers (or we are simply doing a BPF_K 17295 - * check, in which case we have a fake SCALAR_VALUE representing insn->imm). 17296 - * Technically we can do similar adjustments for pointers to the same object, 17297 - * but we don't support that right now. 17298 - */ 17299 - static int reg_set_min_max(struct bpf_verifier_env *env, 17300 - struct bpf_reg_state *true_reg1, 17301 - struct bpf_reg_state *true_reg2, 17302 - struct bpf_reg_state *false_reg1, 17303 - struct bpf_reg_state *false_reg2, 17304 - u8 opcode, bool is_jmp32) 17233 + /* Check for invariant violations on the registers for both branches of a condition */ 17234 + static int regs_bounds_sanity_check_branches(struct bpf_verifier_env *env) 17305 17235 { 17306 17236 int err; 17307 17237 17308 - /* If either register is a pointer, we can't learn anything about its 17309 - * variable offset from the compare (unless they were a pointer into 17310 - * the same object, but we don't bother with that). 17311 - */ 17312 - if (false_reg1->type != SCALAR_VALUE || false_reg2->type != SCALAR_VALUE) 17313 - return 0; 17314 - 17315 - /* We compute branch direction for same SCALAR_VALUE registers in 17316 - * is_scalar_branch_taken(). For unknown branch directions (e.g., BPF_JSET) 17317 - * on the same registers, we don't need to adjust the min/max values. 17318 - */ 17319 - if (false_reg1 == false_reg2) 17320 - return 0; 17321 - 17322 - /* fallthrough (FALSE) branch */ 17323 - regs_refine_cond_op(false_reg1, false_reg2, rev_opcode(opcode), is_jmp32); 17324 - reg_bounds_sync(false_reg1); 17325 - reg_bounds_sync(false_reg2); 17326 - 17327 - /* jump (TRUE) branch */ 17328 - regs_refine_cond_op(true_reg1, true_reg2, opcode, is_jmp32); 17329 - reg_bounds_sync(true_reg1); 17330 - reg_bounds_sync(true_reg2); 17331 - 17332 - err = reg_bounds_sanity_check(env, true_reg1, "true_reg1"); 17333 - err = err ?: reg_bounds_sanity_check(env, true_reg2, "true_reg2"); 17334 - err = err ?: reg_bounds_sanity_check(env, false_reg1, "false_reg1"); 17335 - err = err ?: reg_bounds_sanity_check(env, false_reg2, "false_reg2"); 17238 + err = reg_bounds_sanity_check(env, &env->true_reg1, "true_reg1"); 17239 + err = err ?: reg_bounds_sanity_check(env, &env->true_reg2, "true_reg2"); 17240 + err = err ?: reg_bounds_sanity_check(env, &env->false_reg1, "false_reg1"); 17241 + err = err ?: reg_bounds_sanity_check(env, &env->false_reg2, "false_reg2"); 17336 17242 return err; 17337 17243 } 17338 17244 ··· 17682 17656 } 17683 17657 17684 17658 is_jmp32 = BPF_CLASS(insn->code) == BPF_JMP32; 17685 - pred = is_branch_taken(dst_reg, src_reg, opcode, is_jmp32); 17659 + copy_register_state(&env->false_reg1, dst_reg); 17660 + copy_register_state(&env->false_reg2, src_reg); 17661 + copy_register_state(&env->true_reg1, dst_reg); 17662 + copy_register_state(&env->true_reg2, src_reg); 17663 + pred = is_branch_taken(env, dst_reg, src_reg, opcode, is_jmp32); 17686 17664 if (pred >= 0) { 17687 17665 /* If we get here with a dst_reg pointer type it is because 17688 17666 * above is_branch_taken() special cased the 0 comparison. ··· 17750 17720 return PTR_ERR(other_branch); 17751 17721 other_branch_regs = other_branch->frame[other_branch->curframe]->regs; 17752 17722 17753 - if (BPF_SRC(insn->code) == BPF_X) { 17754 - err = reg_set_min_max(env, 17755 - &other_branch_regs[insn->dst_reg], 17756 - &other_branch_regs[insn->src_reg], 17757 - dst_reg, src_reg, opcode, is_jmp32); 17758 - } else /* BPF_SRC(insn->code) == BPF_K */ { 17759 - /* reg_set_min_max() can mangle the fake_reg. Make a copy 17760 - * so that these are two different memory locations. The 17761 - * src_reg is not used beyond here in context of K. 17762 - */ 17763 - memcpy(&env->fake_reg[1], &env->fake_reg[0], 17764 - sizeof(env->fake_reg[0])); 17765 - err = reg_set_min_max(env, 17766 - &other_branch_regs[insn->dst_reg], 17767 - &env->fake_reg[0], 17768 - dst_reg, &env->fake_reg[1], 17769 - opcode, is_jmp32); 17770 - } 17723 + err = regs_bounds_sanity_check_branches(env); 17771 17724 if (err) 17772 17725 return err; 17726 + 17727 + copy_register_state(dst_reg, &env->false_reg1); 17728 + copy_register_state(src_reg, &env->false_reg2); 17729 + copy_register_state(&other_branch_regs[insn->dst_reg], &env->true_reg1); 17730 + if (BPF_SRC(insn->code) == BPF_X) 17731 + copy_register_state(&other_branch_regs[insn->src_reg], &env->true_reg2); 17773 17732 17774 17733 if (BPF_SRC(insn->code) == BPF_X && 17775 17734 src_reg->type == SCALAR_VALUE && src_reg->id &&
+35 -16
tools/testing/selftests/bpf/progs/verifier_bounds.c
··· 1066 1066 SEC("xdp") 1067 1067 __description("bound check with JMP_JSLT for crossing 64-bit signed boundary") 1068 1068 __success __retval(0) 1069 - __flag(BPF_F_TEST_REG_INVARIANTS) 1070 1069 __naked void crossing_64_bit_signed_boundary_2(void) 1071 1070 { 1072 1071 asm volatile (" \ ··· 1147 1148 SEC("xdp") 1148 1149 __description("bound check with JMP32_JSLT for crossing 32-bit signed boundary") 1149 1150 __success __retval(0) 1150 - __flag(BPF_F_TEST_REG_INVARIANTS) 1151 1151 __naked void crossing_32_bit_signed_boundary_2(void) 1152 1152 { 1153 1153 asm volatile (" \ ··· 1534 1536 SEC("socket") 1535 1537 __description("dead branch on jset, does not result in invariants violation error") 1536 1538 __success __log_level(2) 1537 - __retval(0) __flag(BPF_F_TEST_REG_INVARIANTS) 1539 + __retval(0) 1538 1540 __naked void jset_range_analysis(void) 1539 1541 { 1540 1542 asm volatile (" \ ··· 1570 1572 */ 1571 1573 SEC("socket") 1572 1574 __description("bounds deduction cross sign boundary, negative overlap") 1573 - __success __log_level(2) __flag(BPF_F_TEST_REG_INVARIANTS) 1575 + __success __log_level(2) 1574 1576 __msg("7: (1f) r0 -= r6 {{.*}} R0=scalar(smin=smin32=-655,smax=smax32=-146,umin=0xfffffffffffffd71,umax=0xffffffffffffff6e,umin32=0xfffffd71,umax32=0xffffff6e,var_off=(0xfffffffffffffc00; 0x3ff))") 1575 1577 __retval(0) 1576 1578 __naked void bounds_deduct_negative_overlap(void) ··· 1614 1616 */ 1615 1617 SEC("socket") 1616 1618 __description("bounds deduction cross sign boundary, positive overlap") 1617 - __success __log_level(2) __flag(BPF_F_TEST_REG_INVARIANTS) 1619 + __success __log_level(2) 1618 1620 __msg("3: (2d) if r0 > r1 {{.*}} R0=scalar(smin=smin32=0,smax=umax=smax32=umax32=127,var_off=(0x0; 0x7f))") 1619 1621 __retval(0) 1620 1622 __naked void bounds_deduct_positive_overlap(void) ··· 1647 1649 */ 1648 1650 SEC("socket") 1649 1651 __description("bounds deduction cross sign boundary, two overlaps") 1650 - __failure __flag(BPF_F_TEST_REG_INVARIANTS) 1652 + __failure 1651 1653 __msg("3: (2d) if r0 > r1 {{.*}} R0=scalar(smin=smin32=-128,smax=smax32=127,umax=0xffffffffffffff80)") 1652 1654 __msg("frame pointer is read only") 1653 1655 __naked void bounds_deduct_two_overlaps(void) ··· 1711 1713 __description("conditional jump on same register, branch taken") 1712 1714 __not_msg("20: (b7) r0 = 1 {{.*}} R0=1") 1713 1715 __success __log_level(2) 1714 - __retval(0) __flag(BPF_F_TEST_REG_INVARIANTS) 1716 + __retval(0) 1715 1717 __naked void condition_jump_on_same_register(void *ctx) 1716 1718 { 1717 1719 asm volatile(" \ ··· 1746 1748 __description("jset on same register, constant value branch taken") 1747 1749 __not_msg("7: (b7) r0 = 1 {{.*}} R0=1") 1748 1750 __success __log_level(2) 1749 - __retval(0) __flag(BPF_F_TEST_REG_INVARIANTS) 1751 + __retval(0) 1750 1752 __naked void jset_on_same_register_1(void *ctx) 1751 1753 { 1752 1754 asm volatile(" \ ··· 1768 1770 __description("jset on same register, scalar value branch taken") 1769 1771 __not_msg("12: (b7) r0 = 1 {{.*}} R0=1") 1770 1772 __success __log_level(2) 1771 - __retval(0) __flag(BPF_F_TEST_REG_INVARIANTS) 1773 + __retval(0) 1772 1774 __naked void jset_on_same_register_2(void *ctx) 1773 1775 { 1774 1776 asm volatile(" \ ··· 1798 1800 __msg("3: (b7) r0 = 0 {{.*}} R0=0") 1799 1801 __msg("5: (b7) r0 = 1 {{.*}} R0=1") 1800 1802 __success __log_level(2) 1801 - __flag(BPF_F_TEST_REG_INVARIANTS) 1802 1803 __naked void jset_on_same_register_3(void *ctx) 1803 1804 { 1804 1805 asm volatile(" \ ··· 1819 1822 __msg("4: (b7) r0 = 0 {{.*}} R0=0") 1820 1823 __msg("6: (b7) r0 = 1 {{.*}} R0=1") 1821 1824 __success __log_level(2) 1822 - __flag(BPF_F_TEST_REG_INVARIANTS) 1823 1825 __naked void jset_on_same_register_4(void *ctx) 1824 1826 { 1825 1827 asm volatile(" \ ··· 1841 1845 __msg("4: (b7) r0 = 0 {{.*}} R0=0") 1842 1846 __msg("6: (b7) r0 = 1 {{.*}} R0=1") 1843 1847 __success __log_level(2) 1844 - __flag(BPF_F_TEST_REG_INVARIANTS) 1845 1848 __naked void jset_on_same_register_5(void *ctx) 1846 1849 { 1847 1850 asm volatile(" \ ··· 1872 1877 __description("bounds refinement with single-value tnum on umax") 1873 1878 __msg("3: (15) if r0 == 0xe0 {{.*}} R0=240") 1874 1879 __success __log_level(2) 1875 - __flag(BPF_F_TEST_REG_INVARIANTS) 1876 1880 __naked void bounds_refinement_tnum_umax(void *ctx) 1877 1881 { 1878 1882 asm volatile(" \ ··· 1901 1907 __description("bounds refinement with single-value tnum on umin") 1902 1908 __msg("3: (15) if r0 == 0xf0 {{.*}} R0=224") 1903 1909 __success __log_level(2) 1904 - __flag(BPF_F_TEST_REG_INVARIANTS) 1905 1910 __naked void bounds_refinement_tnum_umin(void *ctx) 1906 1911 { 1907 1912 asm volatile(" \ ··· 1995 2002 1996 2003 SEC("socket") 1997 2004 __success 1998 - __flag(BPF_F_TEST_REG_INVARIANTS) 1999 2005 __naked void signed_unsigned_intersection32_case1(void *ctx) 2000 2006 { 2001 2007 asm volatile(" \ ··· 2012 2020 2013 2021 SEC("socket") 2014 2022 __success 2015 - __flag(BPF_F_TEST_REG_INVARIANTS) 2016 2023 __naked void signed_unsigned_intersection32_case2(void *ctx) 2017 2024 { 2018 2025 asm volatile(" \ ··· 2153 2162 " : 2154 2163 : __imm(bpf_map_lookup_elem), 2155 2164 __imm_addr(map_hash_8b) 2165 + : __clobber_all); 2166 + } 2167 + 2168 + /* 2169 + * Last jump can be detected as always taken because the intersection of R5 and 2170 + * R7 32bit tnums produces a constant that isn't within R7's s32 bounds. 2171 + */ 2172 + SEC("socket") 2173 + __description("dead branch: tnums give impossible constant if equal") 2174 + __success 2175 + __naked void tnums_equal_impossible_constant(void *ctx) 2176 + { 2177 + asm volatile(" \ 2178 + call %[bpf_get_prandom_u32]; \ 2179 + r5 = r0; \ 2180 + /* Set r5's var_off32 to (0; 0xfffffffc) */ \ 2181 + r5 &= 0xfffffffffffffffc; \ 2182 + r7 = r0; \ 2183 + /* Set r7's var_off32 to (0x0; 0x1) */ \ 2184 + r7 &= 0x1; \ 2185 + /* Now, s32=[-43; -42], var_off32=(0xffffffd4; 0x3) */ \ 2186 + r7 += -43; \ 2187 + /* On fallthrough, var_off32=-44, not in s32 */ \ 2188 + if w5 != w7 goto +1; \ 2189 + r10 = 0; \ 2190 + exit; \ 2191 + " : 2192 + : __imm(bpf_get_prandom_u32) 2156 2193 : __clobber_all); 2157 2194 } 2158 2195