Skip to content

Commit ccaa6d2

Browse files
author
Alexei Starovoitov
committed
Merge branch 'bpf-calls-to-bpf_loop-should-have-an-scc-and-accumulate-backedges'
Eduard Zingerman says: ==================== bpf: calls to bpf_loop() should have an SCC and accumulate backedges This is a correctness fix for the verification of BPF programs that work with callback-calling functions. The problem is the same as the issue fixed by series [1] for iterator-based loops: some of the states created while processing the callback function body might have incomplete read or precision marks. An example of an unsafe program that is accepted without this fix can be found in patch #2. There is some impact on verification performance: File Program Insns (A) Insns (B) Insns (DIFF) ------------------------------- -------------------- --------- --------- ----------------- pyperf600_bpf_loop.bpf.o on_event 4247 9985 +5738 (+135.11%) setget_sockopt.bpf.o skops_sockopt 5719 7446 +1727 (+30.20%) setget_sockopt.bpf.o socket_post_create 1253 1603 +350 (+27.93%) strobemeta_bpf_loop.bpf.o on_event 3424 7224 +3800 (+110.98%) test_tcp_custom_syncookie.bpf.o tcp_custom_syncookie 11929 38307 +26378 (+221.12%) xdp_synproxy_kern.bpf.o syncookie_tc 13986 23035 +9049 (+64.70%) xdp_synproxy_kern.bpf.o syncookie_xdp 13881 21022 +7141 (+51.44%) Total progs: 4172 Old success: 2520 New success: 2520 total_insns diff min: 0.00% total_insns diff max: 221.12% 0 -> value: 0 value -> 0: 0 total_insns abs max old: 837,487 total_insns abs max new: 837,487 0 .. 5 %: 4163 5 .. 15 %: 2 25 .. 35 %: 2 50 .. 60 %: 1 60 .. 70 %: 1 110 .. 120 %: 1 135 .. 145 %: 1 220 .. 225 %: 1 [1] https://lore.kernel.org/bpf/174968344350.3524559.14906547029551737094.git-patchwork-notify@kernel.org/ --- ==================== Link: https://patch.msgid.link/[email protected] Signed-off-by: Alexei Starovoitov <[email protected]>
2 parents 317a5df + e6f2612 commit ccaa6d2

File tree

2 files changed

+84
-4
lines changed

2 files changed

+84
-4
lines changed

kernel/bpf/verifier.c

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19830,8 +19830,10 @@ static int is_state_visited(struct bpf_verifier_env *env, int insn_idx)
1983019830
}
1983119831
}
1983219832
if (bpf_calls_callback(env, insn_idx)) {
19833-
if (states_equal(env, &sl->state, cur, RANGE_WITHIN))
19833+
if (states_equal(env, &sl->state, cur, RANGE_WITHIN)) {
19834+
loop = true;
1983419835
goto hit;
19836+
}
1983519837
goto skip_inf_loop_check;
1983619838
}
1983719839
/* attempt to detect infinite loop to avoid unnecessary doomed work */
@@ -25071,15 +25073,18 @@ static int compute_scc(struct bpf_verifier_env *env)
2507125073
}
2507225074
/*
2507325075
* Assign SCC number only if component has two or more elements,
25074-
* or if component has a self reference.
25076+
* or if component has a self reference, or if instruction is a
25077+
* callback calling function (implicit loop).
2507525078
*/
25076-
assign_scc = stack[stack_sz - 1] != w;
25077-
for (j = 0; j < succ->cnt; ++j) {
25079+
assign_scc = stack[stack_sz - 1] != w; /* two or more elements? */
25080+
for (j = 0; j < succ->cnt; ++j) { /* self reference? */
2507825081
if (succ->items[j] == w) {
2507925082
assign_scc = true;
2508025083
break;
2508125084
}
2508225085
}
25086+
if (bpf_calls_callback(env, w)) /* implicit loop? */
25087+
assign_scc = true;
2508325088
/* Pop component elements from stack */
2508425089
do {
2508525090
t = stack[--stack_sz];

tools/testing/selftests/bpf/progs/iters.c

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1926,4 +1926,79 @@ static int loop1_wrapper(void)
19261926
);
19271927
}
19281928

1929+
/*
1930+
* This is similar to a test case absent_mark_in_the_middle_state(),
1931+
* but adapted for use with bpf_loop().
1932+
*/
1933+
SEC("raw_tp")
1934+
__flag(BPF_F_TEST_STATE_FREQ)
1935+
__failure __msg("math between fp pointer and register with unbounded min value is not allowed")
1936+
__naked void absent_mark_in_the_middle_state4(void)
1937+
{
1938+
/*
1939+
* Equivalent to a C program below:
1940+
*
1941+
* int main(void) {
1942+
* fp[-8] = bpf_get_prandom_u32();
1943+
* fp[-16] = -32; // used in a memory access below
1944+
* bpf_loop(7, loop_cb4, fp, 0);
1945+
* return 0;
1946+
* }
1947+
*
1948+
* int loop_cb4(int i, void *ctx) {
1949+
* if (unlikely(ctx[-8] > bpf_get_prandom_u32()))
1950+
* *(u64 *)(fp + ctx[-16]) = 42; // aligned access expected
1951+
* if (unlikely(fp[-8] > bpf_get_prandom_u32()))
1952+
* ctx[-16] = -31; // makes said access unaligned
1953+
* return 0;
1954+
* }
1955+
*/
1956+
asm volatile (
1957+
"call %[bpf_get_prandom_u32];"
1958+
"r8 = r0;"
1959+
"*(u64 *)(r10 - 8) = r0;"
1960+
"*(u64 *)(r10 - 16) = -32;"
1961+
"r1 = 7;"
1962+
"r2 = loop_cb4 ll;"
1963+
"r3 = r10;"
1964+
"r4 = 0;"
1965+
"call %[bpf_loop];"
1966+
"r0 = 0;"
1967+
"exit;"
1968+
:
1969+
: __imm(bpf_loop),
1970+
__imm(bpf_get_prandom_u32)
1971+
: __clobber_all
1972+
);
1973+
}
1974+
1975+
__used __naked
1976+
static void loop_cb4(void)
1977+
{
1978+
asm volatile (
1979+
"r9 = r2;"
1980+
"r8 = *(u64 *)(r9 - 8);"
1981+
"r6 = *(u64 *)(r9 - 16);"
1982+
"call %[bpf_get_prandom_u32];"
1983+
"if r0 > r8 goto use_fp16_%=;"
1984+
"1:"
1985+
"call %[bpf_get_prandom_u32];"
1986+
"if r0 > r8 goto update_fp16_%=;"
1987+
"2:"
1988+
"r0 = 0;"
1989+
"exit;"
1990+
"use_fp16_%=:"
1991+
"r1 = r10;"
1992+
"r1 += r6;"
1993+
"*(u64 *)(r1 + 0) = 42;"
1994+
"goto 1b;"
1995+
"update_fp16_%=:"
1996+
"*(u64 *)(r9 - 16) = -31;"
1997+
"goto 2b;"
1998+
:
1999+
: __imm(bpf_get_prandom_u32)
2000+
: __clobber_all
2001+
);
2002+
}
2003+
19292004
char _license[] SEC("license") = "GPL";

0 commit comments

Comments
 (0)