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 'bpf-recognize-special-arithmetic-shift-in-the-verifier'

Puranjay Mohan says:

====================
bpf: Recognize special arithmetic shift in the verifier

v3: https://lore.kernel.org/all/20260103022310.935686-1-puranjay@kernel.org/
Changes in v3->v4:
- Fork verifier state while processing BPF_OR when src_reg has [-1,0]
range and 2nd operand is a constant. This is to detect the following pattern:
i32 X > -1 ? C1 : -1 --> (X >>s 31) | C1
- Add selftests for above.
- Remove __description("s>>=63") (Eduard in another patchset)

v2: https://lore.kernel.org/bpf/20251115022611.64898-1-alexei.starovoitov@gmail.com/
Changes in v2->v3:
- fork verifier state while processing BPF_AND when src_reg has [-1,0]
range and 2nd operand is a constant.

v1->v2:
Use __mark_reg32_known() or __mark_reg_known() for zero too.
Add comment to selftest.

v1:
https://lore.kernel.org/bpf/20251114031039.63852-1-alexei.starovoitov@gmail.com/
====================

Link: https://patch.msgid.link/20260112201424.816836-1-puranjay@kernel.org
Signed-off-by: Alexei Starovoitov <ast@kernel.org>

+124
+39
kernel/bpf/verifier.c
··· 15491 15491 } 15492 15492 } 15493 15493 15494 + static int maybe_fork_scalars(struct bpf_verifier_env *env, struct bpf_insn *insn, 15495 + struct bpf_reg_state *dst_reg) 15496 + { 15497 + struct bpf_verifier_state *branch; 15498 + struct bpf_reg_state *regs; 15499 + bool alu32; 15500 + 15501 + if (dst_reg->smin_value == -1 && dst_reg->smax_value == 0) 15502 + alu32 = false; 15503 + else if (dst_reg->s32_min_value == -1 && dst_reg->s32_max_value == 0) 15504 + alu32 = true; 15505 + else 15506 + return 0; 15507 + 15508 + branch = push_stack(env, env->insn_idx + 1, env->insn_idx, false); 15509 + if (IS_ERR(branch)) 15510 + return PTR_ERR(branch); 15511 + 15512 + regs = branch->frame[branch->curframe]->regs; 15513 + if (alu32) { 15514 + __mark_reg32_known(&regs[insn->dst_reg], 0); 15515 + __mark_reg32_known(dst_reg, -1ull); 15516 + } else { 15517 + __mark_reg_known(&regs[insn->dst_reg], 0); 15518 + __mark_reg_known(dst_reg, -1ull); 15519 + } 15520 + return 0; 15521 + } 15522 + 15494 15523 /* WARNING: This function does calculations on 64-bit values, but the actual 15495 15524 * execution may occur on 32-bit values. Therefore, things like bitshifts 15496 15525 * need extra checks in the 32-bit case. ··· 15582 15553 scalar_min_max_mul(dst_reg, &src_reg); 15583 15554 break; 15584 15555 case BPF_AND: 15556 + if (tnum_is_const(src_reg.var_off)) { 15557 + ret = maybe_fork_scalars(env, insn, dst_reg); 15558 + if (ret) 15559 + return ret; 15560 + } 15585 15561 dst_reg->var_off = tnum_and(dst_reg->var_off, src_reg.var_off); 15586 15562 scalar32_min_max_and(dst_reg, &src_reg); 15587 15563 scalar_min_max_and(dst_reg, &src_reg); 15588 15564 break; 15589 15565 case BPF_OR: 15566 + if (tnum_is_const(src_reg.var_off)) { 15567 + ret = maybe_fork_scalars(env, insn, dst_reg); 15568 + if (ret) 15569 + return ret; 15570 + } 15590 15571 dst_reg->var_off = tnum_or(dst_reg->var_off, src_reg.var_off); 15591 15572 scalar32_min_max_or(dst_reg, &src_reg); 15592 15573 scalar_min_max_or(dst_reg, &src_reg);
+85
tools/testing/selftests/bpf/progs/verifier_subreg.c
··· 738 738 : __clobber_all); 739 739 } 740 740 741 + SEC("socket") 742 + __success __success_unpriv __retval(0) 743 + __naked void arsh_31_and(void) 744 + { 745 + /* Below is what LLVM generates in cilium's bpf_wiregard.o */ 746 + asm volatile (" \ 747 + call %[bpf_get_prandom_u32]; \ 748 + w2 = w0; \ 749 + w2 s>>= 31; \ 750 + w2 &= -134; /* w2 becomes 0 or -134 */ \ 751 + if w2 s> -1 goto +2; \ 752 + /* Branch always taken because w2 = -134 */ \ 753 + if w2 != -136 goto +1; \ 754 + w0 /= 0; \ 755 + w0 = 0; \ 756 + exit; \ 757 + " : 758 + : __imm(bpf_get_prandom_u32) 759 + : __clobber_all); 760 + } 761 + 762 + SEC("socket") 763 + __success __success_unpriv __retval(0) 764 + __naked void arsh_63_and(void) 765 + { 766 + /* Copy of arsh_31 with s/w/r/ */ 767 + asm volatile (" \ 768 + call %[bpf_get_prandom_u32]; \ 769 + r2 = r0; \ 770 + r2 <<= 32; \ 771 + r2 s>>= 63; \ 772 + r2 &= -134; \ 773 + if r2 s> -1 goto +2; \ 774 + /* Branch always taken because w2 = -134 */ \ 775 + if r2 != -136 goto +1; \ 776 + r0 /= 0; \ 777 + r0 = 0; \ 778 + exit; \ 779 + " : 780 + : __imm(bpf_get_prandom_u32) 781 + : __clobber_all); 782 + } 783 + 784 + SEC("socket") 785 + __success __success_unpriv __retval(0) 786 + __naked void arsh_31_or(void) 787 + { 788 + asm volatile (" \ 789 + call %[bpf_get_prandom_u32]; \ 790 + w2 = w0; \ 791 + w2 s>>= 31; \ 792 + w2 |= 134; /* w2 becomes -1 or 134 */ \ 793 + if w2 s> -1 goto +2; \ 794 + /* Branch always taken because w2 = -1 */ \ 795 + if w2 == -1 goto +1; \ 796 + w0 /= 0; \ 797 + w0 = 0; \ 798 + exit; \ 799 + " : 800 + : __imm(bpf_get_prandom_u32) 801 + : __clobber_all); 802 + } 803 + 804 + SEC("socket") 805 + __success __success_unpriv __retval(0) 806 + __naked void arsh_63_or(void) 807 + { 808 + /* Copy of arsh_31 with s/w/r/ */ 809 + asm volatile (" \ 810 + call %[bpf_get_prandom_u32]; \ 811 + r2 = r0; \ 812 + r2 <<= 32; \ 813 + r2 s>>= 63; \ 814 + r2 |= 134; /* r2 becomes -1 or 134 */ \ 815 + if r2 s> -1 goto +2; \ 816 + /* Branch always taken because w2 = -1 */ \ 817 + if r2 == -1 goto +1; \ 818 + r0 /= 0; \ 819 + r0 = 0; \ 820 + exit; \ 821 + " : 822 + : __imm(bpf_get_prandom_u32) 823 + : __clobber_all); 824 + } 825 + 741 826 char _license[] SEC("license") = "GPL";