On Fri, Nov 17, 2023 at 5:34 PM Eduard Zingerman <eddyz87@xxxxxxxxx> wrote: > + > +SEC("?raw_tp") > +__success __log_level(2) > +/* Check that path visiting every callback function once had been > + * reached by verifier. Variable 'i' below (stored as r2) serves > + * as a flag, with each decimal digit corresponding to a callback > + * visit marker. > + */ > +__msg("(73) *(u8 *)(r1 +0) = r2 ; R1_w=map_value(off=0,ks=4,vs=2,imm=0) R2_w=111111") > +int bpf_loop_iter_limit_nested(void *unused) > +{ > + struct num_context ctx1 = { .i = 0 }; > + struct num_context ctx2 = { .i = 0 }; > + /* Set registers for 'i' and 'p' to get guaranteed asm > + * instruction shape for __msg matching. > + */ > + register unsigned i asm("r2"); > + register __u8 *p asm("r1"); I suspect this is fragile. The compiler will use r2 for 'i' if 'i' is actually there, but if it can optimize 'i' and 'p' away the r1 and r2 may be used for something else. The "register" keyword is not mandatory. Unlike "volatile". > + unsigned a, b; > + > + bpf_loop(1, iter_limit_level1_cb, &ctx1, 0); > + bpf_loop(1, iter_limit_level1_cb, &ctx2, 0); > + a = ctx1.i; > + b = ctx2.i; > + i = a * 1000 + b; > + /* Force 'ctx1.i' and 'ctx2.i' precise. */ > + p = &choice_arr[(a % 2 + b % 2) % 2]; > + /* Make sure that verifier does not visit 'impossible' states: > + * enumerate all possible callback visit masks. > + */ > + if (a != 0 && a != 1 && a != 11 && a != 101 && a != 111 && > + b != 0 && b != 1 && b != 11 && b != 101 && b != 111) > + asm volatile ("r0 /= 0;" ::: "r0"); > + /* Instruction for match in __msg spec. */ > + asm volatile ("*(u8 *)(r1 + 0) = r2;" :: "r"(p), "r"(i) : "memory"); Feels even more fragile. Not sure what gcc will do. Can 'i' be checked as run-time value ? If it passes the verifier and after bpf_prog_run the 'i' is equal to expected value we're good, no?