selftests/bpf: test case for callback_depth states pruning logic
authorEduard Zingerman <eddyz87@gmail.com>
Thu, 22 Feb 2024 15:41:21 +0000 (17:41 +0200)
committerAlexei Starovoitov <ast@kernel.org>
Wed, 6 Mar 2024 00:15:56 +0000 (16:15 -0800)
commit5c2bc5e2f81d3344095ae241032dde20a4ea2b48
tree9679deaf78e877789d754ee7f82f9d1b874e64fa
parente9a8e5a587ca55fec6c58e4881742705d45bee54
selftests/bpf: test case for callback_depth states pruning logic

The test case was minimized from mailing list discussion [0].
It is equivalent to the following C program:

    struct iter_limit_bug_ctx { __u64 a; __u64 b; __u64 c; };

    static __naked void iter_limit_bug_cb(void)
    {
     switch (bpf_get_prandom_u32()) {
     case 1:  ctx->a = 42; break;
     case 2:  ctx->b = 42; break;
     default: ctx->c = 42; break;
     }
    }

    int iter_limit_bug(struct __sk_buff *skb)
    {
     struct iter_limit_bug_ctx ctx = { 7, 7, 7 };

     bpf_loop(2, iter_limit_bug_cb, &ctx, 0);
     if (ctx.a == 42 && ctx.b == 42 && ctx.c == 7)
       asm volatile("r1 /= 0;":::"r1");
     return 0;
    }

The main idea is that each loop iteration changes one of the state
variables in a non-deterministic manner. Hence it is premature to
prune the states that have two iterations left comparing them to
states with one iteration left.
E.g. {{7,7,7}, callback_depth=0} can reach state {42,42,7},
while {{7,7,7}, callback_depth=1} can't.

[0] https://lore.kernel.org/bpf/9b251840-7cb8-4d17-bd23-1fc8071d8eef@linux.dev/

Acked-by: Yonghong Song <yonghong.song@linux.dev>
Signed-off-by: Eduard Zingerman <eddyz87@gmail.com>
Link: https://lore.kernel.org/r/20240222154121.6991-3-eddyz87@gmail.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
tools/testing/selftests/bpf/progs/verifier_iterating_callbacks.c