selftests/bpf: validate fake register spill/fill precision backtracking logic
Add two tests validating that verifier's precision backtracking logic handles BPF_ST_MEM instructions that produce fake register spill into register slot. This is happening when non-zero constant is written directly to a slot, e.g., *(u64 *)(r10 -8) = 123. Add both full 64-bit register spill, as well as 32-bit "sub-spill". Signed-off-by: Andrii Nakryiko <andrii@kernel.org> Acked-by: Eduard Zingerman <eddyz87@gmail.com> Link: https://lore.kernel.org/r/20231209010958.66758-2-andrii@kernel.org Signed-off-by: Alexei Starovoitov <ast@kernel.org>
This commit is contained in:
parent
482d548d40
commit
7d8ed51bcb
@ -577,4 +577,158 @@ __naked void partial_stack_load_preserves_zeros(void)
|
||||
: __clobber_common);
|
||||
}
|
||||
|
||||
char two_byte_buf[2] SEC(".data.two_byte_buf");
|
||||
|
||||
SEC("raw_tp")
|
||||
__log_level(2) __flag(BPF_F_TEST_STATE_FREQ)
|
||||
__success
|
||||
/* make sure fp-8 is IMPRECISE fake register spill */
|
||||
__msg("3: (7a) *(u64 *)(r10 -8) = 1 ; R10=fp0 fp-8_w=1")
|
||||
/* and fp-16 is spilled IMPRECISE const reg */
|
||||
__msg("5: (7b) *(u64 *)(r10 -16) = r0 ; R0_w=1 R10=fp0 fp-16_w=1")
|
||||
/* validate load from fp-8, which was initialized using BPF_ST_MEM */
|
||||
__msg("8: (79) r2 = *(u64 *)(r10 -8) ; R2_w=1 R10=fp0 fp-8=1")
|
||||
__msg("9: (0f) r1 += r2")
|
||||
__msg("mark_precise: frame0: last_idx 9 first_idx 7 subseq_idx -1")
|
||||
__msg("mark_precise: frame0: regs=r2 stack= before 8: (79) r2 = *(u64 *)(r10 -8)")
|
||||
__msg("mark_precise: frame0: regs= stack=-8 before 7: (bf) r1 = r6")
|
||||
/* note, fp-8 is precise, fp-16 is not yet precise, we'll get there */
|
||||
__msg("mark_precise: frame0: parent state regs= stack=-8: R0_w=1 R1=ctx() R6_r=map_value(map=.data.two_byte_,ks=4,vs=2) R10=fp0 fp-8_rw=P1 fp-16_w=1")
|
||||
__msg("mark_precise: frame0: last_idx 6 first_idx 3 subseq_idx 7")
|
||||
__msg("mark_precise: frame0: regs= stack=-8 before 6: (05) goto pc+0")
|
||||
__msg("mark_precise: frame0: regs= stack=-8 before 5: (7b) *(u64 *)(r10 -16) = r0")
|
||||
__msg("mark_precise: frame0: regs= stack=-8 before 4: (b7) r0 = 1")
|
||||
__msg("mark_precise: frame0: regs= stack=-8 before 3: (7a) *(u64 *)(r10 -8) = 1")
|
||||
__msg("10: R1_w=map_value(map=.data.two_byte_,ks=4,vs=2,off=1) R2_w=1")
|
||||
/* validate load from fp-16, which was initialized using BPF_STX_MEM */
|
||||
__msg("12: (79) r2 = *(u64 *)(r10 -16) ; R2_w=1 R10=fp0 fp-16=1")
|
||||
__msg("13: (0f) r1 += r2")
|
||||
__msg("mark_precise: frame0: last_idx 13 first_idx 7 subseq_idx -1")
|
||||
__msg("mark_precise: frame0: regs=r2 stack= before 12: (79) r2 = *(u64 *)(r10 -16)")
|
||||
__msg("mark_precise: frame0: regs= stack=-16 before 11: (bf) r1 = r6")
|
||||
__msg("mark_precise: frame0: regs= stack=-16 before 10: (73) *(u8 *)(r1 +0) = r2")
|
||||
__msg("mark_precise: frame0: regs= stack=-16 before 9: (0f) r1 += r2")
|
||||
__msg("mark_precise: frame0: regs= stack=-16 before 8: (79) r2 = *(u64 *)(r10 -8)")
|
||||
__msg("mark_precise: frame0: regs= stack=-16 before 7: (bf) r1 = r6")
|
||||
/* now both fp-8 and fp-16 are precise, very good */
|
||||
__msg("mark_precise: frame0: parent state regs= stack=-16: R0_w=1 R1=ctx() R6_r=map_value(map=.data.two_byte_,ks=4,vs=2) R10=fp0 fp-8_rw=P1 fp-16_rw=P1")
|
||||
__msg("mark_precise: frame0: last_idx 6 first_idx 3 subseq_idx 7")
|
||||
__msg("mark_precise: frame0: regs= stack=-16 before 6: (05) goto pc+0")
|
||||
__msg("mark_precise: frame0: regs= stack=-16 before 5: (7b) *(u64 *)(r10 -16) = r0")
|
||||
__msg("mark_precise: frame0: regs=r0 stack= before 4: (b7) r0 = 1")
|
||||
__msg("14: R1_w=map_value(map=.data.two_byte_,ks=4,vs=2,off=1) R2_w=1")
|
||||
__naked void stack_load_preserves_const_precision(void)
|
||||
{
|
||||
asm volatile (
|
||||
/* establish checkpoint with state that has no stack slots;
|
||||
* if we bubble up to this state without finding desired stack
|
||||
* slot, then it's a bug and should be caught
|
||||
*/
|
||||
"goto +0;"
|
||||
|
||||
/* fp-8 is const 1 *fake* register */
|
||||
".8byte %[fp8_st_one];" /* LLVM-18+: *(u64 *)(r10 -8) = 1; */
|
||||
|
||||
/* fp-16 is const 1 register */
|
||||
"r0 = 1;"
|
||||
"*(u64 *)(r10 -16) = r0;"
|
||||
|
||||
/* force checkpoint to check precision marks preserved in parent states */
|
||||
"goto +0;"
|
||||
|
||||
/* load single U64 from aligned FAKE_REG=1 slot */
|
||||
"r1 = %[two_byte_buf];"
|
||||
"r2 = *(u64 *)(r10 -8);"
|
||||
"r1 += r2;"
|
||||
"*(u8 *)(r1 + 0) = r2;" /* this should be fine */
|
||||
|
||||
/* load single U64 from aligned REG=1 slot */
|
||||
"r1 = %[two_byte_buf];"
|
||||
"r2 = *(u64 *)(r10 -16);"
|
||||
"r1 += r2;"
|
||||
"*(u8 *)(r1 + 0) = r2;" /* this should be fine */
|
||||
|
||||
"r0 = 0;"
|
||||
"exit;"
|
||||
:
|
||||
: __imm_ptr(two_byte_buf),
|
||||
__imm_insn(fp8_st_one, BPF_ST_MEM(BPF_DW, BPF_REG_FP, -8, 1))
|
||||
: __clobber_common);
|
||||
}
|
||||
|
||||
SEC("raw_tp")
|
||||
__log_level(2) __flag(BPF_F_TEST_STATE_FREQ)
|
||||
__success
|
||||
/* make sure fp-8 is 32-bit FAKE subregister spill */
|
||||
__msg("3: (62) *(u32 *)(r10 -8) = 1 ; R10=fp0 fp-8=????1")
|
||||
/* but fp-16 is spilled IMPRECISE zero const reg */
|
||||
__msg("5: (63) *(u32 *)(r10 -16) = r0 ; R0_w=1 R10=fp0 fp-16=????1")
|
||||
/* validate load from fp-8, which was initialized using BPF_ST_MEM */
|
||||
__msg("8: (61) r2 = *(u32 *)(r10 -8) ; R2_w=1 R10=fp0 fp-8=????1")
|
||||
__msg("9: (0f) r1 += r2")
|
||||
__msg("mark_precise: frame0: last_idx 9 first_idx 7 subseq_idx -1")
|
||||
__msg("mark_precise: frame0: regs=r2 stack= before 8: (61) r2 = *(u32 *)(r10 -8)")
|
||||
__msg("mark_precise: frame0: regs= stack=-8 before 7: (bf) r1 = r6")
|
||||
__msg("mark_precise: frame0: parent state regs= stack=-8: R0_w=1 R1=ctx() R6_r=map_value(map=.data.two_byte_,ks=4,vs=2) R10=fp0 fp-8_r=????P1 fp-16=????1")
|
||||
__msg("mark_precise: frame0: last_idx 6 first_idx 3 subseq_idx 7")
|
||||
__msg("mark_precise: frame0: regs= stack=-8 before 6: (05) goto pc+0")
|
||||
__msg("mark_precise: frame0: regs= stack=-8 before 5: (63) *(u32 *)(r10 -16) = r0")
|
||||
__msg("mark_precise: frame0: regs= stack=-8 before 4: (b7) r0 = 1")
|
||||
__msg("mark_precise: frame0: regs= stack=-8 before 3: (62) *(u32 *)(r10 -8) = 1")
|
||||
__msg("10: R1_w=map_value(map=.data.two_byte_,ks=4,vs=2,off=1) R2_w=1")
|
||||
/* validate load from fp-16, which was initialized using BPF_STX_MEM */
|
||||
__msg("12: (61) r2 = *(u32 *)(r10 -16) ; R2_w=1 R10=fp0 fp-16=????1")
|
||||
__msg("13: (0f) r1 += r2")
|
||||
__msg("mark_precise: frame0: last_idx 13 first_idx 7 subseq_idx -1")
|
||||
__msg("mark_precise: frame0: regs=r2 stack= before 12: (61) r2 = *(u32 *)(r10 -16)")
|
||||
__msg("mark_precise: frame0: regs= stack=-16 before 11: (bf) r1 = r6")
|
||||
__msg("mark_precise: frame0: regs= stack=-16 before 10: (73) *(u8 *)(r1 +0) = r2")
|
||||
__msg("mark_precise: frame0: regs= stack=-16 before 9: (0f) r1 += r2")
|
||||
__msg("mark_precise: frame0: regs= stack=-16 before 8: (61) r2 = *(u32 *)(r10 -8)")
|
||||
__msg("mark_precise: frame0: regs= stack=-16 before 7: (bf) r1 = r6")
|
||||
__msg("mark_precise: frame0: parent state regs= stack=-16: R0_w=1 R1=ctx() R6_r=map_value(map=.data.two_byte_,ks=4,vs=2) R10=fp0 fp-8_r=????P1 fp-16_r=????P1")
|
||||
__msg("mark_precise: frame0: last_idx 6 first_idx 3 subseq_idx 7")
|
||||
__msg("mark_precise: frame0: regs= stack=-16 before 6: (05) goto pc+0")
|
||||
__msg("mark_precise: frame0: regs= stack=-16 before 5: (63) *(u32 *)(r10 -16) = r0")
|
||||
__msg("mark_precise: frame0: regs=r0 stack= before 4: (b7) r0 = 1")
|
||||
__msg("14: R1_w=map_value(map=.data.two_byte_,ks=4,vs=2,off=1) R2_w=1")
|
||||
__naked void stack_load_preserves_const_precision_subreg(void)
|
||||
{
|
||||
asm volatile (
|
||||
/* establish checkpoint with state that has no stack slots;
|
||||
* if we bubble up to this state without finding desired stack
|
||||
* slot, then it's a bug and should be caught
|
||||
*/
|
||||
"goto +0;"
|
||||
|
||||
/* fp-8 is const 1 *fake* SUB-register */
|
||||
".8byte %[fp8_st_one];" /* LLVM-18+: *(u32 *)(r10 -8) = 1; */
|
||||
|
||||
/* fp-16 is const 1 SUB-register */
|
||||
"r0 = 1;"
|
||||
"*(u32 *)(r10 -16) = r0;"
|
||||
|
||||
/* force checkpoint to check precision marks preserved in parent states */
|
||||
"goto +0;"
|
||||
|
||||
/* load single U32 from aligned FAKE_REG=1 slot */
|
||||
"r1 = %[two_byte_buf];"
|
||||
"r2 = *(u32 *)(r10 -8);"
|
||||
"r1 += r2;"
|
||||
"*(u8 *)(r1 + 0) = r2;" /* this should be fine */
|
||||
|
||||
/* load single U32 from aligned REG=1 slot */
|
||||
"r1 = %[two_byte_buf];"
|
||||
"r2 = *(u32 *)(r10 -16);"
|
||||
"r1 += r2;"
|
||||
"*(u8 *)(r1 + 0) = r2;" /* this should be fine */
|
||||
|
||||
"r0 = 0;"
|
||||
"exit;"
|
||||
:
|
||||
: __imm_ptr(two_byte_buf),
|
||||
__imm_insn(fp8_st_one, BPF_ST_MEM(BPF_W, BPF_REG_FP, -8, 1)) /* 32-bit spill */
|
||||
: __clobber_common);
|
||||
}
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
|
Loading…
Reference in New Issue
Block a user