mirror of
https://github.com/torvalds/linux.git
synced 2024-12-02 00:51:44 +00:00
selftests/bpf: test if state loops are detected in a tricky case
A convoluted test case for iterators convergence logic that demonstrates that states with branch count equal to 0 might still be a part of not completely explored loop. E.g. consider the following state diagram: initial Here state 'succ' was processed first, | it was eventually tracked to produce a V state identical to 'hdr'. .---------> hdr All branches from 'succ' had been explored | | and thus 'succ' has its .branches == 0. | V | .------... Suppose states 'cur' and 'succ' correspond | | | to the same instruction + callsites. | V V In such case it is necessary to check | ... ... whether 'succ' and 'cur' are identical. | | | If 'succ' and 'cur' are a part of the same loop | V V they have to be compared exactly. | succ <- cur | | | V | ... | | '----' Signed-off-by: Eduard Zingerman <eddyz87@gmail.com> Link: https://lore.kernel.org/r/20231024000917.12153-7-eddyz87@gmail.com Signed-off-by: Alexei Starovoitov <ast@kernel.org>
This commit is contained in:
parent
2a0992829e
commit
64870feebe
@ -998,6 +998,183 @@ __naked int loop_state_deps1(void)
|
||||
);
|
||||
}
|
||||
|
||||
SEC("?raw_tp")
|
||||
__failure
|
||||
__msg("math between fp pointer and register with unbounded")
|
||||
__flag(BPF_F_TEST_STATE_FREQ)
|
||||
__naked int loop_state_deps2(void)
|
||||
{
|
||||
/* This is equivalent to C program below.
|
||||
*
|
||||
* The case turns out to be tricky in a sense that:
|
||||
* - states with read+precise mark on c are explored only on a second
|
||||
* iteration of the first inner loop and in a state which is pushed to
|
||||
* states stack first.
|
||||
* - states with c=-25 are explored only on a second iteration of the
|
||||
* second inner loop and in a state which is pushed to states stack
|
||||
* first.
|
||||
*
|
||||
* Depending on the details of iterator convergence logic
|
||||
* verifier might stop states traversal too early and miss
|
||||
* unsafe c=-25 memory access.
|
||||
*
|
||||
* j = iter_new(); // fp[-16]
|
||||
* a = 0; // r6
|
||||
* b = 0; // r7
|
||||
* c = -24; // r8
|
||||
* while (iter_next(j)) {
|
||||
* i = iter_new(); // fp[-8]
|
||||
* a = 0; // r6
|
||||
* b = 0; // r7
|
||||
* while (iter_next(i)) {
|
||||
* if (a == 1) {
|
||||
* a = 0;
|
||||
* b = 1;
|
||||
* } else if (a == 0) {
|
||||
* a = 1;
|
||||
* if (random() == 42)
|
||||
* continue;
|
||||
* if (b == 1) {
|
||||
* *(r10 + c) = 7; // this is not safe
|
||||
* iter_destroy(i);
|
||||
* iter_destroy(j);
|
||||
* return;
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* iter_destroy(i);
|
||||
* i = iter_new(); // fp[-8]
|
||||
* a = 0; // r6
|
||||
* b = 0; // r7
|
||||
* while (iter_next(i)) {
|
||||
* if (a == 1) {
|
||||
* a = 0;
|
||||
* b = 1;
|
||||
* } else if (a == 0) {
|
||||
* a = 1;
|
||||
* if (random() == 42)
|
||||
* continue;
|
||||
* if (b == 1) {
|
||||
* a = 0;
|
||||
* c = -25;
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* iter_destroy(i);
|
||||
* }
|
||||
* iter_destroy(j);
|
||||
* return;
|
||||
*/
|
||||
asm volatile (
|
||||
"r1 = r10;"
|
||||
"r1 += -16;"
|
||||
"r2 = 0;"
|
||||
"r3 = 10;"
|
||||
"call %[bpf_iter_num_new];"
|
||||
"r6 = 0;"
|
||||
"r7 = 0;"
|
||||
"r8 = -24;"
|
||||
"j_loop_%=:"
|
||||
"r1 = r10;"
|
||||
"r1 += -16;"
|
||||
"call %[bpf_iter_num_next];"
|
||||
"if r0 == 0 goto j_loop_end_%=;"
|
||||
|
||||
/* first inner loop */
|
||||
"r1 = r10;"
|
||||
"r1 += -8;"
|
||||
"r2 = 0;"
|
||||
"r3 = 10;"
|
||||
"call %[bpf_iter_num_new];"
|
||||
"r6 = 0;"
|
||||
"r7 = 0;"
|
||||
"i_loop_%=:"
|
||||
"r1 = r10;"
|
||||
"r1 += -8;"
|
||||
"call %[bpf_iter_num_next];"
|
||||
"if r0 == 0 goto i_loop_end_%=;"
|
||||
"check_one_r6_%=:"
|
||||
"if r6 != 1 goto check_zero_r6_%=;"
|
||||
"r6 = 0;"
|
||||
"r7 = 1;"
|
||||
"goto i_loop_%=;"
|
||||
"check_zero_r6_%=:"
|
||||
"if r6 != 0 goto i_loop_%=;"
|
||||
"r6 = 1;"
|
||||
"call %[bpf_get_prandom_u32];"
|
||||
"if r0 != 42 goto check_one_r7_%=;"
|
||||
"goto i_loop_%=;"
|
||||
"check_one_r7_%=:"
|
||||
"if r7 != 1 goto i_loop_%=;"
|
||||
"r0 = r10;"
|
||||
"r0 += r8;"
|
||||
"r1 = 7;"
|
||||
"*(u64 *)(r0 + 0) = r1;"
|
||||
"r1 = r10;"
|
||||
"r1 += -8;"
|
||||
"call %[bpf_iter_num_destroy];"
|
||||
"r1 = r10;"
|
||||
"r1 += -16;"
|
||||
"call %[bpf_iter_num_destroy];"
|
||||
"r0 = 0;"
|
||||
"exit;"
|
||||
"i_loop_end_%=:"
|
||||
"r1 = r10;"
|
||||
"r1 += -8;"
|
||||
"call %[bpf_iter_num_destroy];"
|
||||
|
||||
/* second inner loop */
|
||||
"r1 = r10;"
|
||||
"r1 += -8;"
|
||||
"r2 = 0;"
|
||||
"r3 = 10;"
|
||||
"call %[bpf_iter_num_new];"
|
||||
"r6 = 0;"
|
||||
"r7 = 0;"
|
||||
"i2_loop_%=:"
|
||||
"r1 = r10;"
|
||||
"r1 += -8;"
|
||||
"call %[bpf_iter_num_next];"
|
||||
"if r0 == 0 goto i2_loop_end_%=;"
|
||||
"check2_one_r6_%=:"
|
||||
"if r6 != 1 goto check2_zero_r6_%=;"
|
||||
"r6 = 0;"
|
||||
"r7 = 1;"
|
||||
"goto i2_loop_%=;"
|
||||
"check2_zero_r6_%=:"
|
||||
"if r6 != 0 goto i2_loop_%=;"
|
||||
"r6 = 1;"
|
||||
"call %[bpf_get_prandom_u32];"
|
||||
"if r0 != 42 goto check2_one_r7_%=;"
|
||||
"goto i2_loop_%=;"
|
||||
"check2_one_r7_%=:"
|
||||
"if r7 != 1 goto i2_loop_%=;"
|
||||
"r6 = 0;"
|
||||
"r8 = -25;"
|
||||
"goto i2_loop_%=;"
|
||||
"i2_loop_end_%=:"
|
||||
"r1 = r10;"
|
||||
"r1 += -8;"
|
||||
"call %[bpf_iter_num_destroy];"
|
||||
|
||||
"r6 = 0;"
|
||||
"r7 = 0;"
|
||||
"goto j_loop_%=;"
|
||||
"j_loop_end_%=:"
|
||||
"r1 = r10;"
|
||||
"r1 += -16;"
|
||||
"call %[bpf_iter_num_destroy];"
|
||||
"r0 = 0;"
|
||||
"exit;"
|
||||
:
|
||||
: __imm(bpf_get_prandom_u32),
|
||||
__imm(bpf_iter_num_new),
|
||||
__imm(bpf_iter_num_next),
|
||||
__imm(bpf_iter_num_destroy)
|
||||
: __clobber_all
|
||||
);
|
||||
}
|
||||
|
||||
SEC("?raw_tp")
|
||||
__success
|
||||
__naked int triple_continue(void)
|
||||
|
Loading…
Reference in New Issue
Block a user