Add a verifier_spill_fill case that passes an untouched byte next to a one-byte scalar spill to bpf_map_update_elem(). The full-capability load retains the existing uninitialized-stack behavior, while the CAP_BPF-only load must reject the STACK_INVALID helper read.
Signed-off-by: Yiyang Chen <[email protected]> --- .../selftests/bpf/progs/verifier_spill_fill.c | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/tools/testing/selftests/bpf/progs/verifier_spill_fill.c b/tools/testing/selftests/bpf/progs/verifier_spill_fill.c index 8b166c42c4e0e..e686f6d8d5656 100644 --- a/tools/testing/selftests/bpf/progs/verifier_spill_fill.c +++ b/tools/testing/selftests/bpf/progs/verifier_spill_fill.c @@ -11,6 +11,13 @@ struct { __uint(max_entries, 4096); } map_ringbuf SEC(".maps"); +struct { + __uint(type, BPF_MAP_TYPE_ARRAY); + __uint(max_entries, 1); + __type(key, __u32); + __type(value, __u8); +} map_partial_spill SEC(".maps"); + SEC("socket") __description("check valid spill/fill") __success __failure_unpriv __msg_unpriv("R0 leaks addr") @@ -1307,6 +1314,32 @@ __naked void stack_noperfmon_spill_32bit_onto_64bit_slot(void) : __clobber_all); } +SEC("socket") +__description("helper read must reject an invalid sibling of a narrow spill") +__success +__caps_unpriv(CAP_BPF) +__failure_unpriv __msg_unpriv("invalid read from stack R3 off -7+0 size 1") +__naked void helper_read_invalid_partial_spill_sibling(void) +{ + asm volatile ("r5 = 1;\n\t" + "*(u8 *)(r10 - 8) = r5;\n\t" + "r2 = 0;\n\t" + "*(u32 *)(r10 - 16) = r2;\n\t" + "r1 = %[map_partial_spill] ll;\n\t" + "r2 = r10;\n\t" + "r2 += -16;\n\t" + "r3 = r10;\n\t" + "r3 += -7;\n\t" + "r4 = 0;\n\t" + "call %[bpf_map_update_elem];\n\t" + "r0 = 0;\n\t" + "exit;\n\t" + : + : __imm(bpf_map_update_elem), + __imm_addr(map_partial_spill) + : __clobber_all); +} + /* * stacksafe(): check if 32-bit scalar spill in old state is considered * equivalent to STACK_MISC in cur state. -- 2.34.1

