Improvements for BPF_ST tracking by verifier
Subsystems (4)
Eduard Zingerman
maintainerv2 cover Improvements for BPF_ST tracking by verifier
This patch-set is a part of preparation work for -mcpu=v4 option for BPF C compiler (discussed in [1]). Among other things -mcpu=v4 should enable generation of BPF_ST instruction by the compiler.
- Patches #1,2 adjust verifier to track values of constants written to stack using BPF_ST. Currently these are tracked imprecisely, unlike the writes using BPF_STX, e.g.:
fp[-8] = 42; currently verifier assumes that fp[-8]=mmmmmmmm
after such instruction, where m stands for "misc", just a note that something is written at fp[-8].
r1 = 42; verifier tracks r1=42 after this instruction.
fp[-8] = r1; verifier tracks fp[-8]=42 after this instruction.
This patch makes both cases equivalent.
- Patches #3,4 adjust verifier.c:check_stack_write_fixed_off() to preserve STACK_ZERO marks when BPF_ST writes zero. Currently these are replaced by STACK_MISC, unlike zero writes using BPF_STX, e.g.:
... stack range [X,Y] is marked as STACK_ZERO ... r0 = ... variable offset pointer to stack with range [X,Y] ...
fp[r0] = 0; currently verifier marks range [X,Y] as
STACK_MISC for such instructions.
r1 = 0;
fp[r0] = r1; verifier keeps STACK_ZERO marks for range [X,Y].
This patch makes both cases equivalent.
Motivating example for patch #1 could be found at [3].
Previous version of the patch-set is here [2], the changes are:
- Explicit initialization of fake register parent link is removed from verifier.c:check_stack_write_fixed_off() as parent links are now correctly handled by verifier.c:save_register_state().
- Original patch #1 is split in patches #1 & #3.
- Missing test case added for patch #3 verifier.c:check_stack_write_fixed_off() adjustment.
- Test cases are updated to use .prog_type = BPF_PROG_TYPE_SK_LOOKUP, which requires return value to be in the range [0,1] (original test cases assumed that such range is always required, which is not true).
- Original patch #3 with changes allowing BPF_ST writes to context is withheld for now, w/o compiler support for BPF_ST it requires some creative testing.
- Original patch #5 is removed from the patch-set. This patch contained adjustments to expected verifier error messages in some tests, necessary when C compiler generates BPF_ST instruction instead of BPF_STX (changes to expected instruction indices). These changes are not necessary yet.
[1] https://lore.kernel.org/bpf/[email protected]/
[2] https://lore.kernel.org/bpf/[email protected]/
[3] https://lore.kernel.org/bpf/[email protected]/
Eduard Zingerman (4):
bpf: track immediate values written to stack by BPF_ST instruction selftests/bpf: check if verifier tracks constants spilled by
BPF_ST_MEM
bpf: BPF_ST with variable offset should preserve STACK_ZERO marks selftests/bpf: check if BPF_ST with variable offset preserves
STACK_ZERO
kernel/bpf/verifier.c | 22 +++- .../bpf/verifier/bounds_mix_sign_unsign.c | 110 ++++++++++-------- .../selftests/bpf/verifier/bpf_st_mem.c | 67 +++++++++++ 3 files changed, 150 insertions(+), 49 deletions(-) create mode 100644 tools/testing/selftests/bpf/verifier/bpf_st_mem.c
Signature · 1 line
2.39.1
Eduard Zingerman
maintainerPATCH v2 1/4 bpf: track immediate values written to stack by BPF_ST instruction
For aligned stack writes using BPF_ST instruction track stored values in a same way BPF_STX is handled, e.g. make sure that the following commands produce similar verifier knowledge:
fp[-8] = 42; r1 = 42;
fp[-8] = r1;This covers two cases:
- non-null values written to stack are stored as spill of fake registers;
- null values written to stack are stored as STACK_ZERO marks.
Previously both cases above used STACK_MISC marks instead.
Some verifier test cases relied on the old logic to obtain STACK_MISC marks for some stack values. These test cases are updated in the same commit to avoid failures during bisect.
--- kernel/bpf/verifier.c | 18 ++- .../bpf/verifier/bounds_mix_sign_unsign.c | 110 ++++++++++-------- 2 files changed, 80 insertions(+), 48 deletions(-)
kernel/bpf/verifier.c +16 -2
@@ -3473,6 +3473,11 @@ static void save_register_state(struct bpf_func_state *state,scrub_spilled_slot (&state->stack[spi].slot_type[i -1 ]); } +static bool is_bpf_st_mem (struct bpf_insn *insn) +{ +return BPF_CLASS (insn->code) ==BPF_ST &&BPF_MODE (insn->code) ==BPF_MEM ; +} +/* check_stack_{read,write}_fixed_off functions track spill/fill of registers, * stack boundary and alignment are checked in check_mem_access() */
@@ -3484,8 +3489,9 @@ static int check_stack_write_fixed_off(struct bpf_verifier_env *env, {struct bpf_func_state *cur;/* state of the current function */ int i, slot = -off -1 , spi = slot /BPF_REG_SIZE , err; -u32 dst_reg = env->prog->insnsi[insn_idx].dst_reg; +struct bpf_insn *insn = &env->prog->insnsi[insn_idx];struct bpf_reg_state *reg =NULL ; +u32 dst_reg = insn->dst_reg; err =grow_stack_state (state,round_up (slot +1 ,BPF_REG_SIZE ));if (err)
@@ -3538,6 +3544,13 @@ static int check_stack_write_fixed_off(struct bpf_verifier_env *env,return err; }save_register_state (state, spi, reg, size); + }else if (!reg && !(off %BPF_REG_SIZE ) &&is_bpf_st_mem (insn) && + insn->imm !=0 && env->bpf_capable) { +struct bpf_reg_state fake_reg = {}; + +__mark_reg_known (&fake_reg, (u32 )insn->imm); + fake_reg.type =SCALAR_VALUE ; +save_register_state (state, spi, &fake_reg, size); }else if (reg &&is_spillable_regtype (reg->type)) {/* register containing pointer is being spilled into stack */ if (size !=BPF_REG_SIZE ) {
@@ -3572,7 +3585,8 @@ static int check_stack_write_fixed_off(struct bpf_verifier_env *env, state->stack[spi].spilled_ptr.live |=REG_LIVE_WRITTEN ;/* when we zero initialize stack slots mark them as such */ -if (reg &®ister_is_null (reg)) { +if ((reg &®ister_is_null (reg)) || + (!reg &&is_bpf_st_mem (insn) && insn->imm ==0 )) {/* backtracking doesn't work for STACK_ZERO yet. */ err =mark_chain_precision (env, value_regno);if (err)
tools/testing/selftests/bpf/verifier/bounds_mix_sign_unsign.c +64 -46
371 lines not on this page · raw message
Signature · 1 line
2.39.1
Eduard Zingerman
maintainerPATCH v2 2/4 selftests/bpf: check if verifier tracks constants spilled by BPF_ST_MEM
In reply to Eduard ZingermanCheck that verifier tracks the value of 'imm' spilled to stack by BPF_ST_MEM instruction. Cover the following cases:
- write of non-zero constant to stack;
- write of a zero constant to stack.
--- .../selftests/bpf/verifier/bpf_st_mem.c | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 tools/testing/selftests/bpf/verifier/bpf_st_mem.c
tools/testing/selftests/bpf/verifier/bpf_st_mem.c new file +37 -0
@@ -0,0 +1,37 @@ +{ + "BPF_ST_MEM stack imm non-zero", + .insns = { +BPF_ST_MEM (BPF_DW ,BPF_REG_10 ,-8 ,42 ), +BPF_LDX_MEM (BPF_DW ,BPF_REG_0 ,BPF_REG_10 ,-8 ), +BPF_ALU64_IMM (BPF_ADD ,BPF_REG_0 ,-42 ), +/* if value is tracked correctly R0 is zero */ + BPF_EXIT_INSN(), + }, + .result =ACCEPT , +/* Use prog type that requires return value in range [0, 1] */ + .prog_type = BPF_PROG_TYPE_SK_LOOKUP, + .expected_attach_type = BPF_SK_LOOKUP, + .runs = -1, +}, +{ +"BPF_ST_MEM stack imm zero" , + .insns = { +/* mark stack 0000 0000 */ +BPF_ST_MEM (BPF_DW ,BPF_REG_10 ,-8 ,0 ), +/* read and sum a few bytes */ +BPF_MOV64_IMM (BPF_REG_0 ,0 ), +BPF_LDX_MEM (BPF_B ,BPF_REG_1 ,BPF_REG_10 ,-8 ), +BPF_ALU64_REG (BPF_ADD ,BPF_REG_0 ,BPF_REG_1 ), +BPF_LDX_MEM (BPF_B ,BPF_REG_1 ,BPF_REG_10 ,-4 ), +BPF_ALU64_REG (BPF_ADD ,BPF_REG_0 ,BPF_REG_1 ), +BPF_LDX_MEM (BPF_B ,BPF_REG_1 ,BPF_REG_10 ,-1 ), +BPF_ALU64_REG (BPF_ADD ,BPF_REG_0 ,BPF_REG_1 ), +/* if value is tracked correctly R0 is zero */ + BPF_EXIT_INSN(), + }, + .result =ACCEPT , +/* Use prog type that requires return value in range [0, 1] */ + .prog_type = BPF_PROG_TYPE_SK_LOOKUP, + .expected_attach_type = BPF_SK_LOOKUP, + .runs = -1, +},
Signature · 1 line
2.39.1
Eduard Zingerman
maintainerPATCH v2 3/4 bpf: BPF_ST with variable offset should preserve STACK_ZERO marks
In reply to Eduard ZingermanBPF_STX instruction preserves STACK_ZERO marks for variable offset writes in situations like below:
*(u64*)(r10 - 8) = 0 ; STACK_ZERO marks for fp[-8] r0 = random(-7, -1) ; some random number in range of [-7, -1] r0 += r10 ; r0 is now a variable offset pointer to stack
r1 = 0
*(u8*)(r0) = r1 ; BPF_STX writing zero, STACK_ZERO mark for
; fp[-8] is preserved
This commit updates verifier.c:check_stack_write_var_off() to process BPF_ST in a similar manner, e.g. the following example:
*(u64*)(r10 - 8) = 0 ; STACK_ZERO marks for fp[-8] r0 = random(-7, -1) ; some random number in range of [-7, -1] r0 += r10 ; r0 is now variable offset pointer to stack *(u8*)(r0) = 0 ; BPF_ST writing zero, STACK_ZERO mark for
; fp[-8] is preserved
--- kernel/bpf/verifier.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
kernel/bpf/verifier.c +3 -1
@@ -3631,6 +3631,7 @@ static int check_stack_write_var_off(struct bpf_verifier_env *env,int min_off, max_off;int i, err;struct bpf_reg_state *ptr_reg =NULL , *value_reg =NULL ; +struct bpf_insn *insn = &env->prog->insnsi[insn_idx];bool writing_zero = false;/* set if the fact that we're writing a zero is used to let any * stack slots remain STACK_ZERO
@@ -3643,7 +3644,8 @@ static int check_stack_write_var_off(struct bpf_verifier_env *env, max_off = ptr_reg->smax_value + off + size;if (value_regno >=0 ) value_reg = &cur->regs[value_regno]; -if (value_reg &®ister_is_null (value_reg)) +if ((value_reg &®ister_is_null (value_reg)) || + (!value_reg &&is_bpf_st_mem (insn) && insn->imm ==0 )) writing_zero = true; err =grow_stack_state (state,round_up (-min_off,BPF_REG_SIZE ));
Signature · 1 line
2.39.1
Eduard Zingerman
maintainerPATCH v2 4/4 selftests/bpf: check if BPF_ST with variable offset preserves STACK_ZERO
In reply to Eduard ZingermanA test case to verify that variable offset BPF_ST instruction preserves STACK_ZERO marks when writes zeros, e.g. in the following situation:
*(u64*)(r10 - 8) = 0 ; STACK_ZERO marks for fp[-8] r0 = random(-7, -1) ; some random number in range of [-7, -1] r0 += r10 ; r0 is now variable offset pointer to stack *(u8*)(r0) = 0 ; BPF_ST writing zero, STACK_ZERO mark for
; fp[-8] should be preserved.
--- .../selftests/bpf/verifier/bpf_st_mem.c | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+)
tools/testing/selftests/bpf/verifier/bpf_st_mem.c +30 -0
@@ -35,3 +35,33 @@ .expected_attach_type = BPF_SK_LOOKUP, .runs = -1, }, +{ + "BPF_ST_MEM stack imm zero, variable offset", + .insns = { +/* set fp[-16], fp[-24] to zeros */ +BPF_ST_MEM (BPF_DW ,BPF_REG_10 ,-16 ,0 ), +BPF_ST_MEM (BPF_DW ,BPF_REG_10 ,-24 ,0 ), +/* r0 = random value in range [-32, -15] */ +BPF_EMIT_CALL (BPF_FUNC_get_prandom_u32), +BPF_JMP_IMM (BPF_JLE ,BPF_REG_0 ,16 ,2 ), +BPF_MOV64_IMM (BPF_REG_0 ,0 ), +BPF_EXIT_INSN (), +BPF_ALU64_IMM (BPF_SUB ,BPF_REG_0 ,32 ), +/* fp[r0] = 0, make a variable offset write of zero, +* this should preserve zero marks on stack. +*/ +BPF_ALU64_REG (BPF_ADD ,BPF_REG_0 ,BPF_REG_10 ), +BPF_ST_MEM (BPF_B ,BPF_REG_0 ,0 ,0 ), +/* r0 = fp[-20], if variable offset write was tracked correctly +* r0 would be a known zero. +*/ +BPF_LDX_MEM (BPF_B ,BPF_REG_0 ,BPF_REG_10 ,-20 ), +/* Would fail return code verification if r0 range is not tracked correctly. */ + BPF_EXIT_INSN(), + }, + .result =ACCEPT , +/* Use prog type that requires return value in range [0, 1] */ + .prog_type = BPF_PROG_TYPE_SK_LOOKUP, + .expected_attach_type = BPF_SK_LOOKUP, + .runs = -1, +},
Signature · 1 line
2.39.1
This series was applied to bpf/bpf-next.git (master) by Alexei Starovoitov <[email protected]>:
Hello:
This series was applied to bpf/bpf-next.git (master) by Alexei Starovoitov <[email protected]>:
Quoted 9 lines · On Wed, 15 Feb 2023 01:20:26 +0200 you wrote:
This patch-set is a part of preparation work for -mcpu=v4 option for BPF C compiler (discussed in [1]). Among other things -mcpu=v4 should enable generation of
BPF_STinstruction by the compiler.- Patches #1,2 adjust verifier to track values of constants written to stack using
BPF_ST. Currently these are tracked imprecisely, unlike the writes usingBPF_STX, e.g.:[...]
Here is the summary with links:
- [bpf-next,v2,1/4] bpf: track immediate values written to stack by BPF_ST instruction
https://git.kernel.org/bpf/bpf-next/c/ecdf985d7615 - [bpf-next,v2,2/4] selftests/bpf: check if verifier tracks constants spilled by BPF_ST_MEM https://git.kernel.org/bpf/bpf-next/c/1a24af65bb5f - [bpf-next,v2,3/4] bpf: BPF_ST with variable offset should preserve STACK_ZERO marks https://git.kernel.org/bpf/bpf-next/c/31ff2135121c - [bpf-next,v2,4/4] selftests/bpf: check if BPF_ST with variable offset preserves STACK_ZERO https://git.kernel.org/bpf/bpf-next/c/2a33c5a25ef4
You are awesome, thank you!
Signature · 2 lines
Deet-doot-dot, I am a bot. https://korg.docs.kernel.org/patchwork/pwbot.html