bpf: clear zext_dst of dead insns
Subsystems (4)
cover bpf: clear zext_dst of dead insns
Fix the "verifier bug. zext_dst is set, but no reg is defined" failure in the "access skb fields ok" test on s390.
Patch 1 is the fix, patch 2 adds a test.
v1: https://lore.kernel.org/bpf/[email protected]/
v1 -> v2: Rebase to bpf branch, add Fixes:, add a test (Daniel).
Ilya Leoshkevich (2):
bpf: clear zext_dst of dead insns
selftests: bpf: test that dead ldx_w insns are accepted
kernel/bpf/verifier.c | 1 + tools/testing/selftests/bpf/verifier/dead_code.c | 13 +++++++++++++ 2 files changed, 14 insertions(+)
Signature · 1 line
2.31.1
PATCH 1/2 bpf: clear zext_dst of dead insns
"access skb fields ok" verifier test fails on s390 with the "verifier bug. zext_dst is set, but no reg is defined" message. The first insns of the test prog are:
0: 61 01 00 00 00 00 00 00 ldxw %r0,[%r1+0]
8: 35 00 00 01 00 00 00 00 jge %r0,0,1
10: 61 01 00 08 00 00 00 00 ldxw %r0,[%r1+8]
and the 3rd one is dead (this does not look intentional to me, but this is a separate topic).
sanitize_dead_code() converts dead insns into "ja -1", but keeps zext_dst. When opt_subreg_zext_lo32_rnd_hi32() tries to parse such an insn, it sees this discrepancy and bails. This problem can be seen only with JITs whose bpf_jit_needs_zext() returns true.
Fix by clearning dead insns' zext_dst.
The commits that contributed to this problem are:
1. commit 5aa5bd14c5f8 ("bpf: add initial suite for selftests"), which introduced the test with the dead code.
2. commit 5327ed3d44b7 ("bpf: verifier: mark verified-insn with sub-register zext flag"), which introduced the zext_dst flag.
3. commit 83a2881903f3 ("bpf: Account for BPF_FETCH in insn_has_def32()"), which introduced the sanity check.
4. commit 9183671af6db ("bpf: Fix leakage under speculation on mispredicted branches"), which bisect points to.
It's best to fix this on stable branches that contain the second one, since that's the point where the inconsistency was introduced.
--- kernel/bpf/verifier.c | 1 + 1 file changed, 1 insertion(+)
kernel/bpf/verifier.c +1 -0
@@ -11663,6 +11663,7 @@ static void sanitize_dead_code(struct bpf_verifier_env *env)if (aux_data[i].seen)continue ;memcpy (insn + i, &trap,sizeof (trap)); + aux_data[i].zext_dst = false; } }
Signature · 1 line
2.31.1
PATCH 2/2 selftests: bpf: test that dead ldx_w insns are accepted
In reply to Ilya LeoshkevichPrevent regressions related to zero-extension metadata handling during dead code sanitization.
--- tools/testing/selftests/bpf/verifier/dead_code.c | 13 +++++++++++++ 1 file changed, 13 insertions(+)
tools/testing/selftests/bpf/verifier/dead_code.c +13 -0
@@ -159,3 +159,16 @@ .result = ACCEPT, .retval = 2, }, +{ + "dead code: zero extension", + .insns = { +BPF_MOV64_IMM (BPF_REG_0 ,0 ), +BPF_JMP_IMM (BPF_JGE ,BPF_REG_0 ,0 ,1 ), +BPF_LDX_MEM (BPF_W ,BPF_REG_0 ,BPF_REG_10 ,0 ), + BPF_EXIT_INSN(), + }, + .errstr_unpriv ="invalid read from stack R10 off=0 size=4" , + .result_unpriv = REJECT, + .result = ACCEPT, + .retval = 0, +},
Signature · 1 line
2.31.1
Re: [PATCH bpf 2/2] selftests: bpf: test that dead ldx_w insns are accepted
Quoted 20 lines · Ilya Leoshkevich
Prevent regressions related to zero-extension metadata handling during dead code sanitization.
Signed-off-by Ilya Leoshkevich <[email protected]>---
tools/testing/selftests/bpf/verifier/dead_code.c | 13 +++++++++++++1 file changed, 13 insertions(+)tools/testing/selftests/bpf/verifier/dead_code.c +3
-0@@ -159,3 +159,16 @@ .result = ACCEPT, .retval = 2, }, +{ + "dead code: zero extension", + .insns = {
tools/testing/selftests/bpf/verifier/dead_code.c line 174
Quoted 10 lines
tools/testing/selftests/bpf/verifier/dead_code.c +10
-0+ BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_JMP_IMM(BPF_JGE, BPF_REG_0, 0, 1), + BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_10, 0), + BPF_EXIT_INSN(), + }, + .errstr_unpriv = "invalid read from stack R10 off=0 size=4", + .result_unpriv = REJECT, + .result = ACCEPT, + .retval = 0, +},
Please disregard this patch: the test does not fail in absence of the fix. What rather fails is:
BPF_MOV64_IMM(BPF_REG_0, 0), BPF_STX_MEM(BPF_W, BPF_REG_10, BPF_REG_0, -4), BPF_JMP_IMM(BPF_JGE, BPF_REG_0, 0, 1), BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_10, -4), BPF_EXIT_INSN(),
The difference is that here the dead ldx_w is actually safe. I will send a v3 shortly (I also realized I forgot to tag this series with v2).