mirror of
https://github.com/torvalds/linux.git
synced 2026-09-22 04:34:03 +02:00
Merge branch 'bpf-skip-bounds-adjustment-for-conditional-jumps-on-same-scalar-register'
KaFai Wan says: ==================== bpf: Skip bounds adjustment for conditional jumps on same scalar register This small patchset is about avoid verifier bug warning when conditional jumps on same register when the register holds a scalar with range. v4: - make code better. (Alexei) v3: https://lore.kernel.org/bpf/20251031154107.403054-1-kafai.wan@linux.dev/ - Enhance is_scalar_branch_taken() to handle scalar case. (Eduard) - Update the selftest to cover all conditional jump opcodes. (Eduard) v2: https://lore.kernel.org/bpf/20251025053017.2308823-1-kafai.wan@linux.dev/ - Enhance is_branch_taken() and is_scalar_branch_taken() to handle branch direction computation for same register. (Eduard and Alexei) - Update the selftest. v1: https://lore.kernel.org/bpf/20251022164457.1203756-1-kafai.wan@linux.dev/ --- ==================== Link: https://patch.msgid.link/20251103063108.1111764-1-kafai.wan@linux.dev Signed-off-by: Alexei Starovoitov <ast@kernel.org>
This commit is contained in:
commit
11369e6e33
|
|
@ -15993,6 +15993,30 @@ static int is_scalar_branch_taken(struct bpf_reg_state *reg1, struct bpf_reg_sta
|
|||
s64 smin2 = is_jmp32 ? (s64)reg2->s32_min_value : reg2->smin_value;
|
||||
s64 smax2 = is_jmp32 ? (s64)reg2->s32_max_value : reg2->smax_value;
|
||||
|
||||
if (reg1 == reg2) {
|
||||
switch (opcode) {
|
||||
case BPF_JGE:
|
||||
case BPF_JLE:
|
||||
case BPF_JSGE:
|
||||
case BPF_JSLE:
|
||||
case BPF_JEQ:
|
||||
return 1;
|
||||
case BPF_JGT:
|
||||
case BPF_JLT:
|
||||
case BPF_JSGT:
|
||||
case BPF_JSLT:
|
||||
case BPF_JNE:
|
||||
return 0;
|
||||
case BPF_JSET:
|
||||
if (tnum_is_const(t1))
|
||||
return t1.value != 0;
|
||||
else
|
||||
return (smin1 <= 0 && smax1 >= 0) ? -1 : 1;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
switch (opcode) {
|
||||
case BPF_JEQ:
|
||||
/* constants, umin/umax and smin/smax checks would be
|
||||
|
|
@ -16439,6 +16463,13 @@ static int reg_set_min_max(struct bpf_verifier_env *env,
|
|||
if (false_reg1->type != SCALAR_VALUE || false_reg2->type != SCALAR_VALUE)
|
||||
return 0;
|
||||
|
||||
/* We compute branch direction for same SCALAR_VALUE registers in
|
||||
* is_scalar_branch_taken(). For unknown branch directions (e.g., BPF_JSET)
|
||||
* on the same registers, we don't need to adjust the min/max values.
|
||||
*/
|
||||
if (false_reg1 == false_reg2)
|
||||
return 0;
|
||||
|
||||
/* fallthrough (FALSE) branch */
|
||||
regs_refine_cond_op(false_reg1, false_reg2, rev_opcode(opcode), is_jmp32);
|
||||
reg_bounds_sync(false_reg1);
|
||||
|
|
|
|||
|
|
@ -1709,4 +1709,158 @@ __naked void jeq_disagreeing_tnums(void *ctx)
|
|||
: __clobber_all);
|
||||
}
|
||||
|
||||
SEC("socket")
|
||||
__description("conditional jump on same register, branch taken")
|
||||
__not_msg("20: (b7) r0 = 1 {{.*}} R0=1")
|
||||
__success __log_level(2)
|
||||
__retval(0) __flag(BPF_F_TEST_REG_INVARIANTS)
|
||||
__naked void condition_jump_on_same_register(void *ctx)
|
||||
{
|
||||
asm volatile(" \
|
||||
call %[bpf_get_prandom_u32]; \
|
||||
w8 = 0x80000000; \
|
||||
r0 &= r8; \
|
||||
if r0 == r0 goto +1; \
|
||||
goto l1_%=; \
|
||||
if r0 >= r0 goto +1; \
|
||||
goto l1_%=; \
|
||||
if r0 s>= r0 goto +1; \
|
||||
goto l1_%=; \
|
||||
if r0 <= r0 goto +1; \
|
||||
goto l1_%=; \
|
||||
if r0 s<= r0 goto +1; \
|
||||
goto l1_%=; \
|
||||
if r0 != r0 goto l1_%=; \
|
||||
if r0 > r0 goto l1_%=; \
|
||||
if r0 s> r0 goto l1_%=; \
|
||||
if r0 < r0 goto l1_%=; \
|
||||
if r0 s< r0 goto l1_%=; \
|
||||
l0_%=: r0 = 0; \
|
||||
exit; \
|
||||
l1_%=: r0 = 1; \
|
||||
exit; \
|
||||
" :
|
||||
: __imm(bpf_get_prandom_u32)
|
||||
: __clobber_all);
|
||||
}
|
||||
|
||||
SEC("socket")
|
||||
__description("jset on same register, constant value branch taken")
|
||||
__not_msg("7: (b7) r0 = 1 {{.*}} R0=1")
|
||||
__success __log_level(2)
|
||||
__retval(0) __flag(BPF_F_TEST_REG_INVARIANTS)
|
||||
__naked void jset_on_same_register_1(void *ctx)
|
||||
{
|
||||
asm volatile(" \
|
||||
r0 = 0; \
|
||||
if r0 & r0 goto l1_%=; \
|
||||
r0 = 1; \
|
||||
if r0 & r0 goto +1; \
|
||||
goto l1_%=; \
|
||||
l0_%=: r0 = 0; \
|
||||
exit; \
|
||||
l1_%=: r0 = 1; \
|
||||
exit; \
|
||||
" :
|
||||
: __imm(bpf_get_prandom_u32)
|
||||
: __clobber_all);
|
||||
}
|
||||
|
||||
SEC("socket")
|
||||
__description("jset on same register, scalar value branch taken")
|
||||
__not_msg("12: (b7) r0 = 1 {{.*}} R0=1")
|
||||
__success __log_level(2)
|
||||
__retval(0) __flag(BPF_F_TEST_REG_INVARIANTS)
|
||||
__naked void jset_on_same_register_2(void *ctx)
|
||||
{
|
||||
asm volatile(" \
|
||||
/* range [1;2] */ \
|
||||
call %[bpf_get_prandom_u32]; \
|
||||
r0 &= 0x1; \
|
||||
r0 += 1; \
|
||||
if r0 & r0 goto +1; \
|
||||
goto l1_%=; \
|
||||
/* range [-2;-1] */ \
|
||||
call %[bpf_get_prandom_u32]; \
|
||||
r0 &= 0x1; \
|
||||
r0 -= 2; \
|
||||
if r0 & r0 goto +1; \
|
||||
goto l1_%=; \
|
||||
l0_%=: r0 = 0; \
|
||||
exit; \
|
||||
l1_%=: r0 = 1; \
|
||||
exit; \
|
||||
" :
|
||||
: __imm(bpf_get_prandom_u32)
|
||||
: __clobber_all);
|
||||
}
|
||||
|
||||
SEC("socket")
|
||||
__description("jset on same register, scalar value unknown branch 1")
|
||||
__msg("3: (b7) r0 = 0 {{.*}} R0=0")
|
||||
__msg("5: (b7) r0 = 1 {{.*}} R0=1")
|
||||
__success __log_level(2)
|
||||
__flag(BPF_F_TEST_REG_INVARIANTS)
|
||||
__naked void jset_on_same_register_3(void *ctx)
|
||||
{
|
||||
asm volatile(" \
|
||||
/* range [0;1] */ \
|
||||
call %[bpf_get_prandom_u32]; \
|
||||
r0 &= 0x1; \
|
||||
if r0 & r0 goto l1_%=; \
|
||||
l0_%=: r0 = 0; \
|
||||
exit; \
|
||||
l1_%=: r0 = 1; \
|
||||
exit; \
|
||||
" :
|
||||
: __imm(bpf_get_prandom_u32)
|
||||
: __clobber_all);
|
||||
}
|
||||
|
||||
SEC("socket")
|
||||
__description("jset on same register, scalar value unknown branch 2")
|
||||
__msg("4: (b7) r0 = 0 {{.*}} R0=0")
|
||||
__msg("6: (b7) r0 = 1 {{.*}} R0=1")
|
||||
__success __log_level(2)
|
||||
__flag(BPF_F_TEST_REG_INVARIANTS)
|
||||
__naked void jset_on_same_register_4(void *ctx)
|
||||
{
|
||||
asm volatile(" \
|
||||
/* range [-1;0] */ \
|
||||
call %[bpf_get_prandom_u32]; \
|
||||
r0 &= 0x1; \
|
||||
r0 -= 1; \
|
||||
if r0 & r0 goto l1_%=; \
|
||||
l0_%=: r0 = 0; \
|
||||
exit; \
|
||||
l1_%=: r0 = 1; \
|
||||
exit; \
|
||||
" :
|
||||
: __imm(bpf_get_prandom_u32)
|
||||
: __clobber_all);
|
||||
}
|
||||
|
||||
SEC("socket")
|
||||
__description("jset on same register, scalar value unknown branch 3")
|
||||
__msg("4: (b7) r0 = 0 {{.*}} R0=0")
|
||||
__msg("6: (b7) r0 = 1 {{.*}} R0=1")
|
||||
__success __log_level(2)
|
||||
__flag(BPF_F_TEST_REG_INVARIANTS)
|
||||
__naked void jset_on_same_register_5(void *ctx)
|
||||
{
|
||||
asm volatile(" \
|
||||
/* range [-1;1] */ \
|
||||
call %[bpf_get_prandom_u32]; \
|
||||
r0 &= 0x2; \
|
||||
r0 -= 1; \
|
||||
if r0 & r0 goto l1_%=; \
|
||||
l0_%=: r0 = 0; \
|
||||
exit; \
|
||||
l1_%=: r0 = 1; \
|
||||
exit; \
|
||||
" :
|
||||
: __imm(bpf_get_prandom_u32)
|
||||
: __clobber_all);
|
||||
}
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user