[PATCH bpf-next v4 0/6] bpf: Scope callback arguments to their frame

From: Ihor Solodrai

Date: Fri Nov 13 2026 - 19:31:26 EST


A callback-calling helper can pass its callback a pointer that is only
valid for the callback invocation, and the verifier has no way to
express that lifetime.

Two helpers need fixing:

* bpf_user_ringbuf_drain() passes a CONST_PTR_TO_DYNPTR over a
sample it releases as soon as the callback returns. Saving this
pointer in callback_ctx leaves it pointing into reused kernel
stack and allows arbitrary kernel read and write since v7.3.

* bpf_for_each_map_elem() over an array or per-CPU array map passes
the address of a u32 held in bpf_for_each_array_elem()'s own frame,
leaking four bytes of kernel stack.

Give the callback frame an id and make it both the id and the
parent_id of such an argument, so that slices and clones descend from
it. Invalidate the id and its descendants when the frame is popped.

---

v3->v4:
* Rebase on bpf-next and include the callback setter cleanups [1] as
patches 1-2 (Alexei)
* Pair frame_id in join_func(), and add a selftest for it
* Drop the Fixes: tags and Cc: stable
v3: https://lore.kernel.org/r/20261106231157.2840635-1-ihor.solodrai@xxxxxxxxx

v2->v3:
* Replace REF_TYPE_FRAME with a frame_id in bpf_func_state and drop
the preparation and refactoring patches (Alexei)
* Rebase on bpf, and add Fixes: tags
v2: https://lore.kernel.org/r/20261009202759.50520-1-ihor.solodrai@xxxxxxxxx

v1->v2:
* Add the refactoring patches: release frame references in one scan,
and fold diagnostics into the REF_TYPE_FRAME introduction
* Replace temporary argument mask and deferred register scan with
reference acquisition directly in the owning verifier state during
callback setup (Eduard)
* Reuse callback_park_dynptr() for the nested drain test
v1: https://lore.kernel.org/r/20260922010333.1226537-1-ihor.solodrai@xxxxxxxxx

[1] https://lore.kernel.org/r/20261106232110.2867412-1-ihor.solodrai@xxxxxxxxx

---

Ihor Solodrai (6):
bpf: Share map-backed callback register setup
bpf: Rely on callback frame initialization defaults
bpf: Scope the bpf_user_ringbuf_drain() dynptr to its callback frame
selftests/bpf: Cover the user ringbuf callback dynptr lifetime
bpf: Scope the bpf_for_each_map_elem() array key to the callback frame
selftests/bpf: Cover callback-frame map key lifetime

include/linux/bpf.h | 2 +
include/linux/bpf_verifier.h | 2 +
kernel/bpf/arraymap.c | 19 ++-
kernel/bpf/join.c | 3 +
kernel/bpf/states.c | 3 +
kernel/bpf/verifier.c | 128 ++++----------
.../selftests/bpf/progs/user_ringbuf_fail.c | 161 ++++++++++++++++++
.../bpf/progs/verifier_iterating_callbacks.c | 91 ++++++++++
8 files changed, 316 insertions(+), 93 deletions(-)


base-commit: 0021f17128de5dc0b19f1a523f448df2440d8088
--
2.56.0