diff options
| author | Daniel Borkmann <daniel@iogearbox.net> | 2026-09-25 19:52:31 +0200 |
|---|---|---|
| committer | Alexei Starovoitov <ast@kernel.org> | 2026-09-25 21:02:31 +0000 |
| commit | 939bb6270ee32cfef0631b30ff07a9e0b8806e64 (patch) | |
| tree | 52752d776ee30e1dcc675b6f3a32cd4b451ce3da /kernel | |
| parent | 8608ec8e981974b18834e2d67ed39638a9ec2e63 (diff) | |
| download | linux-939bb6270ee32cfef0631b30ff07a9e0b8806e64.tar.gz linux-939bb6270ee32cfef0631b30ff07a9e0b8806e64.zip | |
bpf: Leave out the hidden exit edge of a subprogram without an exit
visit_abnormal_return_insn() gives tail_call and ld_{abs,ind} insns a
second successor, the exit_idx of their subprogram, so that the hidden
exit from the subprogram is part of the CFG. check_subprogs() only
assigns exit_idx when it walks over a BPF_EXIT, and a subprogram whose
last insn jumps back into itself never has one, so exit_idx stays zero
and the edge points at insn 0 of the program:
0: r0 = 0
1: r0 = *(u8 *)skb[0]
2: goto -2
For a subprogram other than main, update_insn() then reads the liveness
masks at a negative relative index. Such a subprogram can still load
when it leaves through a bpf_throw(), so mark exit_idx as unset in that
case and leave the hidden edge out, as there is no exit for it to reach.
Fixes: e40f5a6bf88a ("bpf: correct stack liveness for tail calls")
Fixes: ee861486e377 ("bpf: Fix ld_{abs,ind} failure path analysis in subprogs")
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Link: https://patch.msgid.link/20260925175244.1136329-5-daniel@iogearbox.net
Diffstat (limited to 'kernel')
| -rw-r--r-- | kernel/bpf/cfg.c | 10 | ||||
| -rw-r--r-- | kernel/bpf/verifier.c | 1 |
2 files changed, 8 insertions, 3 deletions
diff --git a/kernel/bpf/cfg.c b/kernel/bpf/cfg.c index 69e484b25b59..f3f545c53264 100644 --- a/kernel/bpf/cfg.c +++ b/kernel/bpf/cfg.c @@ -515,17 +515,21 @@ static int visit_abnormal_return_insn(struct bpf_verifier_env *env, int t) { struct bpf_subprog_info *subprog; struct bpf_iarray *jt; + u32 n; if (env->insn_aux_data[t].jt) return 0; - jt = bpf_iarray_realloc(NULL, 2); + /* A subprogram without an exit has no hidden exit edge to take. */ + subprog = bpf_find_containing_subprog(env, t); + n = subprog->exit_idx == U32_MAX ? 1 : 2; + jt = bpf_iarray_realloc(NULL, n); if (!jt) return -ENOMEM; - subprog = bpf_find_containing_subprog(env, t); jt->items[0] = t + 1; - jt->items[1] = subprog->exit_idx; + if (n == 2) + jt->items[1] = subprog->exit_idx; env->insn_aux_data[t].jt = jt; return 0; } diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index cf9ff37cfdf2..107456fa1845 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -2470,6 +2470,7 @@ static int add_subprog(struct bpf_verifier_env *env, int off) return -E2BIG; } /* determine subprog starts. The end is one before the next starts */ + env->subprog_info[env->subprog_cnt].exit_idx = U32_MAX; env->subprog_info[env->subprog_cnt++].start = off; sort(env->subprog_info, env->subprog_cnt, sizeof(env->subprog_info[0]), cmp_subprogs, NULL); |
