Message ID | 20230406164455.1045294-1-yhs@fb.com (mailing list archive) |
---|---|
State | Accepted |
Commit | 13fbcee55706db45ce047a7cea14811d68f94ee3 |
Delegated to: | BPF |
Headers | show |
Series | bpf: Improve verifier for cond_op and spilled loop index variables | expand |
On 4/6/23 12:44 PM, Yonghong Song wrote: > Currently, for BPF_JEQ/BPF_JNE insn, verifier determines > whether the branch is taken or not only if both operands > are constants. Therefore, for the following code snippet, > 0: (85) call bpf_ktime_get_ns#5 ; R0_w=scalar() > 1: (a5) if r0 < 0x3 goto pc+2 ; R0_w=scalar(umin=3) > 2: (b7) r2 = 2 ; R2_w=2 > 3: (1d) if r0 == r2 goto pc+2 6 > > At insn 3, since r0 is not a constant, verifier assumes both branch > can be taken which may lead inproper verification failure. > > Add comparing umin/umax value and the constant. If the umin value > is greater than the constant, or umax value is smaller than the constant, > for JEQ the branch must be not-taken, and for JNE the branch must be taken. > The jmp32 mode JEQ/JNE branch taken checking is also handled similarly. > > The following lists the veristat result w.r.t. changed number > of processes insns during verification: > > File Program Insns (A) Insns (B) Insns (DIFF) > ----------------------------------------------------- ---------------------------------------------------- --------- --------- --------------- > test_cls_redirect.bpf.linked3.o cls_redirect 64980 73472 +8492 (+13.07%) > test_seg6_loop.bpf.linked3.o __add_egr_x 12425 12423 -2 (-0.02%) > test_tcp_hdr_options.bpf.linked3.o estab 2634 2558 -76 (-2.89%) > test_parse_tcp_hdr_opt.bpf.linked3.o xdp_ingress_v6 1421 1420 -1 (-0.07%) > test_parse_tcp_hdr_opt_dynptr.bpf.linked3.o xdp_ingress_v6 1238 1237 -1 (-0.08%) > test_tc_dtime.bpf.linked3.o egress_fwdns_prio100 414 411 -3 (-0.72%) > > Mostly a small improvement but test_cls_redirect.bpf.linked3.o has a 13% regression. > I checked with verifier log and found it this is due to pruning. > For some JEQ/JNE branches impacted by this patch, > one branch is explored and the other has state equivalence and > pruned. Can you elaborate a bit on this insn increase caused by pruning? My naive reading of this: there was some state exploration that was previously pruned due to is_branch_taken returning indeterminate value (-1), and now that it can concretely say branch is/isn't taken, states which would've been considered equivalent no longer are. Is that accurate? > > Signed-off-by: Yonghong Song <yhs@fb.com> > --- Regardless, Acked-by: Dave Marchevsky <davemarchevsky@fb.com>
On Thu, Apr 6, 2023 at 10:49 AM Dave Marchevsky <davemarchevsky@meta.com> wrote: > > > > On 4/6/23 12:44 PM, Yonghong Song wrote: > > Currently, for BPF_JEQ/BPF_JNE insn, verifier determines > > whether the branch is taken or not only if both operands > > are constants. Therefore, for the following code snippet, > > 0: (85) call bpf_ktime_get_ns#5 ; R0_w=scalar() > > 1: (a5) if r0 < 0x3 goto pc+2 ; R0_w=scalar(umin=3) > > 2: (b7) r2 = 2 ; R2_w=2 > > 3: (1d) if r0 == r2 goto pc+2 6 > > > > At insn 3, since r0 is not a constant, verifier assumes both branch > > can be taken which may lead inproper verification failure. > > > > Add comparing umin/umax value and the constant. If the umin value > > is greater than the constant, or umax value is smaller than the constant, > > for JEQ the branch must be not-taken, and for JNE the branch must be taken. > > The jmp32 mode JEQ/JNE branch taken checking is also handled similarly. > > > > The following lists the veristat result w.r.t. changed number > > of processes insns during verification: > > > > File Program Insns (A) Insns (B) Insns (DIFF) > > ----------------------------------------------------- ---------------------------------------------------- --------- --------- --------------- > > test_cls_redirect.bpf.linked3.o cls_redirect 64980 73472 +8492 (+13.07%) > > test_seg6_loop.bpf.linked3.o __add_egr_x 12425 12423 -2 (-0.02%) > > test_tcp_hdr_options.bpf.linked3.o estab 2634 2558 -76 (-2.89%) > > test_parse_tcp_hdr_opt.bpf.linked3.o xdp_ingress_v6 1421 1420 -1 (-0.07%) > > test_parse_tcp_hdr_opt_dynptr.bpf.linked3.o xdp_ingress_v6 1238 1237 -1 (-0.08%) > > test_tc_dtime.bpf.linked3.o egress_fwdns_prio100 414 411 -3 (-0.72%) > > > > Mostly a small improvement but test_cls_redirect.bpf.linked3.o has a 13% regression. > > I checked with verifier log and found it this is due to pruning. > > For some JEQ/JNE branches impacted by this patch, > > one branch is explored and the other has state equivalence and > > pruned. > > Can you elaborate a bit on this insn increase caused by pruning? > > My naive reading of this: there was some state exploration that was > previously pruned due to is_branch_taken returning indeterminate > value (-1), and now that it can concretely say branch is/isn't taken, > states which would've been considered equivalent no longer are. > Is that accurate? Pretty much. It's because when is_branch_taken() is certain on the direction of the branch it marks register as precise and it hurts state equivalence later. > > > > > Signed-off-by: Yonghong Song <yhs@fb.com> > > --- > > Regardless, > > Acked-by: Dave Marchevsky <davemarchevsky@fb.com>
On Thu, Apr 6, 2023 at 9:45 AM Yonghong Song <yhs@fb.com> wrote: > > Currently, for BPF_JEQ/BPF_JNE insn, verifier determines > whether the branch is taken or not only if both operands > are constants. Therefore, for the following code snippet, > 0: (85) call bpf_ktime_get_ns#5 ; R0_w=scalar() > 1: (a5) if r0 < 0x3 goto pc+2 ; R0_w=scalar(umin=3) > 2: (b7) r2 = 2 ; R2_w=2 > 3: (1d) if r0 == r2 goto pc+2 6 > > At insn 3, since r0 is not a constant, verifier assumes both branch > can be taken which may lead inproper verification failure. > > Add comparing umin/umax value and the constant. If the umin value > is greater than the constant, or umax value is smaller than the constant, > for JEQ the branch must be not-taken, and for JNE the branch must be taken. > The jmp32 mode JEQ/JNE branch taken checking is also handled similarly. > > The following lists the veristat result w.r.t. changed number > of processes insns during verification: > > File Program Insns (A) Insns (B) Insns (DIFF) > ----------------------------------------------------- ---------------------------------------------------- --------- --------- --------------- > test_cls_redirect.bpf.linked3.o cls_redirect 64980 73472 +8492 (+13.07%) > test_seg6_loop.bpf.linked3.o __add_egr_x 12425 12423 -2 (-0.02%) > test_tcp_hdr_options.bpf.linked3.o estab 2634 2558 -76 (-2.89%) > test_parse_tcp_hdr_opt.bpf.linked3.o xdp_ingress_v6 1421 1420 -1 (-0.07%) > test_parse_tcp_hdr_opt_dynptr.bpf.linked3.o xdp_ingress_v6 1238 1237 -1 (-0.08%) > test_tc_dtime.bpf.linked3.o egress_fwdns_prio100 414 411 -3 (-0.72%) > > Mostly a small improvement but test_cls_redirect.bpf.linked3.o has a 13% regression. > I checked with verifier log and found it this is due to pruning. > For some JEQ/JNE branches impacted by this patch, > one branch is explored and the other has state equivalence and > pruned. > > Signed-off-by: Yonghong Song <yhs@fb.com> > --- LGTM. Acked-by: Andrii Nakryiko <andrii@kernel.org> > kernel/bpf/verifier.c | 8 ++++++++ > 1 file changed, 8 insertions(+) > > diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c > index 56f569811f70..5c6b90e384a5 100644 > --- a/kernel/bpf/verifier.c > +++ b/kernel/bpf/verifier.c > @@ -12651,10 +12651,14 @@ static int is_branch32_taken(struct bpf_reg_state *reg, u32 val, u8 opcode) > case BPF_JEQ: > if (tnum_is_const(subreg)) > return !!tnum_equals_const(subreg, val); > + else if (val < reg->u32_min_value || val > reg->u32_max_value) > + return 0; > break; > case BPF_JNE: > if (tnum_is_const(subreg)) > return !tnum_equals_const(subreg, val); > + else if (val < reg->u32_min_value || val > reg->u32_max_value) > + return 1; > break; > case BPF_JSET: > if ((~subreg.mask & subreg.value) & val) > @@ -12724,10 +12728,14 @@ static int is_branch64_taken(struct bpf_reg_state *reg, u64 val, u8 opcode) > case BPF_JEQ: > if (tnum_is_const(reg->var_off)) > return !!tnum_equals_const(reg->var_off, val); > + else if (val < reg->umin_value || val > reg->umax_value) > + return 0; > break; > case BPF_JNE: > if (tnum_is_const(reg->var_off)) > return !tnum_equals_const(reg->var_off, val); > + else if (val < reg->umin_value || val > reg->umax_value) > + return 1; > break; > case BPF_JSET: > if ((~reg->var_off.mask & reg->var_off.value) & val) > -- > 2.34.1 >
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 56f569811f70..5c6b90e384a5 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -12651,10 +12651,14 @@ static int is_branch32_taken(struct bpf_reg_state *reg, u32 val, u8 opcode) case BPF_JEQ: if (tnum_is_const(subreg)) return !!tnum_equals_const(subreg, val); + else if (val < reg->u32_min_value || val > reg->u32_max_value) + return 0; break; case BPF_JNE: if (tnum_is_const(subreg)) return !tnum_equals_const(subreg, val); + else if (val < reg->u32_min_value || val > reg->u32_max_value) + return 1; break; case BPF_JSET: if ((~subreg.mask & subreg.value) & val) @@ -12724,10 +12728,14 @@ static int is_branch64_taken(struct bpf_reg_state *reg, u64 val, u8 opcode) case BPF_JEQ: if (tnum_is_const(reg->var_off)) return !!tnum_equals_const(reg->var_off, val); + else if (val < reg->umin_value || val > reg->umax_value) + return 0; break; case BPF_JNE: if (tnum_is_const(reg->var_off)) return !tnum_equals_const(reg->var_off, val); + else if (val < reg->umin_value || val > reg->umax_value) + return 1; break; case BPF_JSET: if ((~reg->var_off.mask & reg->var_off.value) & val)
Currently, for BPF_JEQ/BPF_JNE insn, verifier determines whether the branch is taken or not only if both operands are constants. Therefore, for the following code snippet, 0: (85) call bpf_ktime_get_ns#5 ; R0_w=scalar() 1: (a5) if r0 < 0x3 goto pc+2 ; R0_w=scalar(umin=3) 2: (b7) r2 = 2 ; R2_w=2 3: (1d) if r0 == r2 goto pc+2 6 At insn 3, since r0 is not a constant, verifier assumes both branch can be taken which may lead inproper verification failure. Add comparing umin/umax value and the constant. If the umin value is greater than the constant, or umax value is smaller than the constant, for JEQ the branch must be not-taken, and for JNE the branch must be taken. The jmp32 mode JEQ/JNE branch taken checking is also handled similarly. The following lists the veristat result w.r.t. changed number of processes insns during verification: File Program Insns (A) Insns (B) Insns (DIFF) ----------------------------------------------------- ---------------------------------------------------- --------- --------- --------------- test_cls_redirect.bpf.linked3.o cls_redirect 64980 73472 +8492 (+13.07%) test_seg6_loop.bpf.linked3.o __add_egr_x 12425 12423 -2 (-0.02%) test_tcp_hdr_options.bpf.linked3.o estab 2634 2558 -76 (-2.89%) test_parse_tcp_hdr_opt.bpf.linked3.o xdp_ingress_v6 1421 1420 -1 (-0.07%) test_parse_tcp_hdr_opt_dynptr.bpf.linked3.o xdp_ingress_v6 1238 1237 -1 (-0.08%) test_tc_dtime.bpf.linked3.o egress_fwdns_prio100 414 411 -3 (-0.72%) Mostly a small improvement but test_cls_redirect.bpf.linked3.o has a 13% regression. I checked with verifier log and found it this is due to pruning. For some JEQ/JNE branches impacted by this patch, one branch is explored and the other has state equivalence and pruned. Signed-off-by: Yonghong Song <yhs@fb.com> --- kernel/bpf/verifier.c | 8 ++++++++ 1 file changed, 8 insertions(+)