Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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/20251229-scc-for-callbacks-v1-0-ceadfe679900@gmail.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>

+84 -4
+9 -4
kernel/bpf/verifier.c
··· 19830 19830 } 19831 19831 } 19832 19832 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; 19834 19835 goto hit; 19836 + } 19835 19837 goto skip_inf_loop_check; 19836 19838 } 19837 19839 /* attempt to detect infinite loop to avoid unnecessary doomed work */ ··· 25073 25071 } 25074 25072 /* 25075 25073 * Assign SCC number only if component has two or more elements, 25076 - * or if component has a self reference. 25074 + * or if component has a self reference, or if instruction is a 25075 + * callback calling function (implicit loop). 25077 25076 */ 25078 - assign_scc = stack[stack_sz - 1] != w; 25079 - for (j = 0; j < succ->cnt; ++j) { 25077 + assign_scc = stack[stack_sz - 1] != w; /* two or more elements? */ 25078 + for (j = 0; j < succ->cnt; ++j) { /* self reference? */ 25080 25079 if (succ->items[j] == w) { 25081 25080 assign_scc = true; 25082 25081 break; 25083 25082 } 25084 25083 } 25084 + if (bpf_calls_callback(env, w)) /* implicit loop? */ 25085 + assign_scc = true; 25085 25086 /* Pop component elements from stack */ 25086 25087 do { 25087 25088 t = stack[--stack_sz];
+75
tools/testing/selftests/bpf/progs/iters.c
··· 1926 1926 ); 1927 1927 } 1928 1928 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 + 1929 2004 char _license[] SEC("license") = "GPL";