* [PATCH bpf-next v6 0/2] bpf: Destroy callee-local dynptrs on subprog return
@ 2026-09-27 12:04 Xu Yunxiang
2026-09-27 12:04 ` [PATCH bpf-next v6 1/2] " Xu Yunxiang
` (2 more replies)
0 siblings, 3 replies; 9+ messages in thread
From: Xu Yunxiang @ 2026-09-27 12:04 UTC (permalink / raw)
To: bpf; +Cc: ast, daniel, andrii, eddyz87, memxor, ameryhung, sashiko-bot
A dynptr data slice is valid only while the specific dynptr that produced
it remains valid. Apply normal dynptr stack-slot teardown before a callee
frame disappears, so its descendants are invalidated and its last
release-capable referenced dynptr cannot be lost.
Patch 1 adds a helper to destroy dynptrs in an inclusive range of stack
slots and reuses it for initialization, variable-offset writes and
subprogram returns. The callback diagnostic expectation changes in that
same commit. Patch 2 tests callee-local slices, last-holder rejection and
caller-owned slice compatibility.
Changes in v6, following Amery's review:
- Replace destroy_dynptrs_on_func_exit() with the generic
destroy_dynptrs_in_stack_slots() and use it at all three call sites.
- Add the requested blank line before return-time teardown.
- Clarify callee-local clone comments and rename the rejection test to
callee_dynptr_slice_invalid_after_return().
- Rebase onto bpf-next ea9358e1270ab.
v5:
https://lore.kernel.org/r/20260920210438.345847-1-xyx2021@mail.ustc.edu.cn
Review:
https://lore.kernel.org/r/CAMB2axMmrTb+UZ84UD48MwMtXbk1s9bWtreU3AOfjzU0fPT0BA@mail.gmail.com
https://lore.kernel.org/r/CAMB2axOEZjRV1K4Yxb4=1f2jvPHiD2rVy-+byXbvs3G0qat63g@mail.gmail.com
Validation on the rebased candidate with a matching bpf_testmod:
- W=1 verifier, full kernel and modules builds passed.
- Changed BPF objects and required fixtures compiled strictly.
- dynptr: 2/137 passed; 0 skipped.
- file_reader: 1/8 passed; 0 skipped.
- ns_bpf_qdisc/invalid_dynptr_returned_slice: 1/1 passed; 0 skipped.
- global_func: 3/29 passed; 3 skipped.
- cb_refs: 1/4 passed; 0 skipped.
- subprogs: 5/34 passed; 0 skipped.
- verifier_var_off: 1/24 passed; 1 skipped.
No selected test failed. The VM used panic_on_warn and panic_on_oops;
no WARN_ON report, Oops or panic was found. Unprivileged BPF was
enabled only in the isolated guest; the kernel printed the expected
Spectre-v2 notice when enabling that sysctl.
The broad test_progs build used BPF_STRICT_BUILD=0 for unselected
objects requiring unavailable kernel features. The changed objects
and required fixtures were also compiled in strict mode. Annotated
lifetime tests check loading and diagnostics. The unfiltered full
suite, sanitizer and architecture matrices, and new unmodified-base
differential probes were not run in this rebase validation.
Three 512-byte stack-limit variants skip on this 2 KiB-stack kernel;
their large-stack counterparts passed. ctx_arg_rewrite skips because
the kernel supports native context-argument tags.
Please queue the verifier fix for stable after it reaches the BPF tree.
Xu Yunxiang (2):
bpf: Destroy callee-local dynptrs on subprog return
selftests/bpf: Test dynptr teardown on subprog return
kernel/bpf/verifier.c | 40 +++++---
.../selftests/bpf/prog_tests/bpf_qdisc.c | 2 +
...disc_fail__invalid_dynptr_returned_slice.c | 76 ++++++++++++++
.../testing/selftests/bpf/progs/dynptr_fail.c | 99 ++++++++++++++++++-
4 files changed, 202 insertions(+), 15 deletions(-)
create mode 100644 tools/testing/selftests/bpf/progs/bpf_qdisc_fail__invalid_dynptr_returned_slice.c
base-commit: ea9358e1270ab2c3ba6f36bd9bdda68617665516
--
2.43.0
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH bpf-next v6 1/2] bpf: Destroy callee-local dynptrs on subprog return
2026-09-27 12:04 [PATCH bpf-next v6 0/2] bpf: Destroy callee-local dynptrs on subprog return Xu Yunxiang
@ 2026-09-27 12:04 ` Xu Yunxiang
2026-09-28 4:48 ` Amery Hung
2026-09-27 12:04 ` [PATCH bpf-next v6 2/2] selftests/bpf: Test dynptr teardown " Xu Yunxiang
2026-10-05 23:40 ` [PATCH bpf-next v6 0/2] bpf: Destroy callee-local dynptrs " patchwork-bot+netdevbpf
2 siblings, 1 reply; 9+ messages in thread
From: Xu Yunxiang @ 2026-09-27 12:04 UTC (permalink / raw)
To: bpf; +Cc: ast, daniel, andrii, eddyz87, memxor, ameryhung, sashiko-bot
A data slice is valid only while the specific dynptr that produced it
remains valid. prepare_func_exit() copies a subprogram return value into
the caller and then frees the callee state without applying the normal
destruction semantics to dynptrs in the callee stack.
A callee can therefore derive a slice from a local dynptr, return or spill
the slice into its caller, and then let the dynptr disappear with the
callee frame. The slice retains the id of a dynptr that is no longer in the
verifier state, so the verifier continues to accept accesses through it.
Before the object relationship refactor, bpf_dynptr_data() slices from
referenced dynptrs also carried the shared reference id, allowing a later
release to catch some of these escaped slices. The refactor made those
slices precise children of their source dynptr and exposed the missing
teardown as an accepted stale access even after the shared resource is
released.
Add destroy_dynptrs_in_stack_slots() to apply the existing dynptr teardown
to an inclusive range of stack slots. Reuse it for dynptr initialization,
variable-offset stack writes, and the entire callee stack before freeing
the frame. The teardown rejects losing the last dynptr for a referenced
resource and invalidates the dynptr and all its descendants, including
slices held in the caller. Propagate errors through prepare_func_exit().
Reuse the existing teardown API and diagnostic. Update the affected
callback test expectation in this change so the commit remains test-clean.
Fixes: 308c7a0ae885 ("bpf: Refactor object relationship tracking and fix dynptr UAF bug")
Reported-by: Sashiko <sashiko-bot@kernel.org>
Closes: https://lore.kernel.org/r/20260829040712.B1B511F000E9@smtp.kernel.org
Suggested-by: Amery Hung <ameryhung@gmail.com>
Link: https://lore.kernel.org/r/CAMB2axOmdHwSrHihSRskDywkCmEGQOX+6dZPN_A9u-HhxY3UDA@mail.gmail.com
Link: https://lore.kernel.org/r/CAMB2axMmrTb+UZ84UD48MwMtXbk1s9bWtreU3AOfjzU0fPT0BA@mail.gmail.com
Assisted-by: LLM
Signed-off-by: Xu Yunxiang <xyx2021@mail.ustc.edu.cn>
---
kernel/bpf/verifier.c | 40 +++++++++++++------
.../testing/selftests/bpf/progs/dynptr_fail.c | 4 +-
2 files changed, 29 insertions(+), 15 deletions(-)
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 03dbc0e003986..7dca855aecb1a 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -723,6 +723,21 @@ static void mark_dynptr_cb_reg(struct bpf_verifier_env *env,
static int destroy_if_dynptr_stack_slot(struct bpf_verifier_env *env,
struct bpf_func_state *state, int spi);
+static int destroy_dynptrs_in_stack_slots(struct bpf_verifier_env *env,
+ struct bpf_func_state *state,
+ int first_spi, int last_spi)
+{
+ int spi, err;
+
+ for (spi = first_spi; spi <= last_spi; spi++) {
+ err = destroy_if_dynptr_stack_slot(env, state, spi);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
static int mark_stack_slots_dynptr(struct bpf_verifier_env *env, struct bpf_reg_state *reg,
enum bpf_arg_type arg_type, int insn_idx,
struct ref_obj_desc *ref_obj, struct bpf_dynptr_desc *dynptr)
@@ -736,7 +751,7 @@ static int mark_stack_slots_dynptr(struct bpf_verifier_env *env, struct bpf_reg_
return spi;
/* We cannot assume both spi and spi - 1 belong to the same dynptr,
- * hence we need to call destroy_if_dynptr_stack_slot twice for both,
+ * hence we need to destroy dynptrs in both slots,
* to ensure that for the following example:
* [d1][d1][d2][d2]
* spi 3 2 1 0
@@ -744,10 +759,7 @@ static int mark_stack_slots_dynptr(struct bpf_verifier_env *env, struct bpf_reg_
* case they do belong to same dynptr, second call won't see slot_type
* as STACK_DYNPTR and will simply skip destruction.
*/
- err = destroy_if_dynptr_stack_slot(env, state, spi);
- if (err)
- return err;
- err = destroy_if_dynptr_stack_slot(env, state, spi - 1);
+ err = destroy_dynptrs_in_stack_slots(env, state, spi - 1, spi);
if (err)
return err;
@@ -3823,14 +3835,10 @@ static int check_stack_write_var_off(struct bpf_verifier_env *env,
(!value_reg && is_bpf_st_mem(insn) && insn->imm == 0))
writing_zero = true;
- for (i = min_off; i < max_off; i++) {
- int spi;
-
- spi = bpf_get_spi(i);
- err = destroy_if_dynptr_stack_slot(env, state, spi);
- if (err)
- return err;
- }
+ err = destroy_dynptrs_in_stack_slots(env, state, bpf_get_spi(max_off - 1),
+ bpf_get_spi(min_off));
+ if (err)
+ return err;
/* Variable offset writes destroy any spilled pointers in range. */
for (i = min_off; i < max_off; i++) {
@@ -11675,6 +11683,12 @@ static int prepare_func_exit(struct bpf_verifier_env *env, int *insn_idx)
}
}
+ /* Invalidate callee-local dynptrs and their slices before the frame goes away. */
+ err = destroy_dynptrs_in_stack_slots(env, callee, 0,
+ callee->allocated_stack / BPF_REG_SIZE - 1);
+ if (err)
+ return err;
+
/* for callbacks like bpf_loop or bpf_for_each_map_elem go back to callsite,
* there function call logic would reschedule callback visit. If iteration
* converges is_state_visited() would prune that visit eventually.
diff --git a/tools/testing/selftests/bpf/progs/dynptr_fail.c b/tools/testing/selftests/bpf/progs/dynptr_fail.c
index 9418dfe4d7b7f..0d13f828710e7 100644
--- a/tools/testing/selftests/bpf/progs/dynptr_fail.c
+++ b/tools/testing/selftests/bpf/progs/dynptr_fail.c
@@ -125,9 +125,9 @@ static int missing_release_callback_fn(__u32 index, void *data)
return 0;
}
-/* Any dynptr initialized within a callback must have bpf_dynptr_put called */
+/* A callback cannot return with the last dynptr for a referenced resource. */
SEC("?raw_tp")
-__failure __msg("Unreleased reference id")
+__failure __msg("cannot overwrite referenced dynptr")
int ringbuf_missing_release_callback(void *ctx)
{
bpf_loop(10, missing_release_callback_fn, NULL, 0);
--
2.43.0
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH bpf-next v6 2/2] selftests/bpf: Test dynptr teardown on subprog return
2026-09-27 12:04 [PATCH bpf-next v6 0/2] bpf: Destroy callee-local dynptrs on subprog return Xu Yunxiang
2026-09-27 12:04 ` [PATCH bpf-next v6 1/2] " Xu Yunxiang
@ 2026-09-27 12:04 ` Xu Yunxiang
2026-09-28 5:24 ` Amery Hung
2026-10-05 23:40 ` [PATCH bpf-next v6 0/2] bpf: Destroy callee-local dynptrs " patchwork-bot+netdevbpf
2 siblings, 1 reply; 9+ messages in thread
From: Xu Yunxiang @ 2026-09-27 12:04 UTC (permalink / raw)
To: bpf; +Cc: ast, daniel, andrii, eddyz87, memxor, ameryhung, sashiko-bot
Add failure tests for slices that escape a subprogram from a dynptr in the
callee stack. Cover immediate use of a ring buffer clone slice,
release-time invalidation of a caller dynptr slice after it crosses a
subprogram return, and immediate use of an skb dynptr slice in a qdisc
program.
Cover the error path where a subprogram would otherwise lose the last
dynptr capable of releasing a referenced resource.
Also add a success control which derives a slice from a caller-owned dynptr
and creates a separate clone in the callee. Destroying that callee-local
clone on return must not invalidate the caller dynptr's slice.
Assisted-by: LLM
Signed-off-by: Xu Yunxiang <xyx2021@mail.ustc.edu.cn>
---
.../selftests/bpf/prog_tests/bpf_qdisc.c | 2 +
...disc_fail__invalid_dynptr_returned_slice.c | 76 +++++++++++++++
.../testing/selftests/bpf/progs/dynptr_fail.c | 95 +++++++++++++++++++
3 files changed, 173 insertions(+)
create mode 100644 tools/testing/selftests/bpf/progs/bpf_qdisc_fail__invalid_dynptr_returned_slice.c
diff --git a/tools/testing/selftests/bpf/prog_tests/bpf_qdisc.c b/tools/testing/selftests/bpf/prog_tests/bpf_qdisc.c
index 6dbd1487343c0..122ecb7e98e2a 100644
--- a/tools/testing/selftests/bpf/prog_tests/bpf_qdisc.c
+++ b/tools/testing/selftests/bpf/prog_tests/bpf_qdisc.c
@@ -11,6 +11,7 @@
#include "bpf_qdisc_fail__invalid_dynptr.skel.h"
#include "bpf_qdisc_fail__invalid_dynptr_slice.skel.h"
#include "bpf_qdisc_fail__invalid_dynptr_cross_frame.skel.h"
+#include "bpf_qdisc_fail__invalid_dynptr_returned_slice.skel.h"
#include "bpf_qdisc_fail__untrusted_write.skel.h"
#include "bpf_qdisc_dynptr_use_after_invalidate_clone.skel.h"
@@ -230,6 +231,7 @@ void test_ns_bpf_qdisc(void)
test_incompl_ops();
RUN_TESTS(bpf_qdisc_fail__invalid_dynptr);
RUN_TESTS(bpf_qdisc_fail__invalid_dynptr_cross_frame);
+ RUN_TESTS(bpf_qdisc_fail__invalid_dynptr_returned_slice);
RUN_TESTS(bpf_qdisc_fail__invalid_dynptr_slice);
RUN_TESTS(bpf_qdisc_fail__untrusted_write);
RUN_TESTS(bpf_qdisc_dynptr_use_after_invalidate_clone);
diff --git a/tools/testing/selftests/bpf/progs/bpf_qdisc_fail__invalid_dynptr_returned_slice.c b/tools/testing/selftests/bpf/progs/bpf_qdisc_fail__invalid_dynptr_returned_slice.c
new file mode 100644
index 0000000000000..8217f4c4c00c4
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/bpf_qdisc_fail__invalid_dynptr_returned_slice.c
@@ -0,0 +1,76 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <vmlinux.h>
+#include "bpf_experimental.h"
+#include "bpf_qdisc_common.h"
+#include "bpf_misc.h"
+
+char _license[] SEC("license") = "GPL";
+
+int proto;
+
+static __noinline struct ethhdr *slice_in_subprog(struct sk_buff *skb)
+{
+ struct bpf_dynptr ptr;
+
+ bpf_dynptr_from_skb((struct __sk_buff *)skb, 0, &ptr);
+ return bpf_dynptr_slice(&ptr, 0, NULL, sizeof(struct ethhdr));
+}
+
+SEC("struct_ops")
+__failure __msg("invalid mem access 'scalar'")
+int BPF_PROG(invalid_dynptr_returned_slice, struct sk_buff *skb,
+ struct Qdisc *sch, struct bpf_sk_buff_ptr *to_free)
+{
+ struct ethhdr *hdr;
+
+ hdr = slice_in_subprog(skb);
+ if (!hdr) {
+ bpf_qdisc_skb_drop(skb, to_free);
+ return NET_XMIT_DROP;
+ }
+
+ /* this should fail */
+ proto = hdr->h_proto;
+
+ bpf_qdisc_skb_drop(skb, to_free);
+
+ return NET_XMIT_DROP;
+}
+
+SEC("struct_ops")
+__auxiliary
+struct sk_buff *BPF_PROG(bpf_qdisc_test_dequeue, struct Qdisc *sch)
+{
+ return NULL;
+}
+
+SEC("struct_ops")
+__auxiliary
+int BPF_PROG(bpf_qdisc_test_init, struct Qdisc *sch, struct nlattr *opt,
+ struct netlink_ext_ack *extack)
+{
+ return 0;
+}
+
+SEC("struct_ops")
+__auxiliary
+void BPF_PROG(bpf_qdisc_test_reset, struct Qdisc *sch)
+{
+}
+
+SEC("struct_ops")
+__auxiliary
+void BPF_PROG(bpf_qdisc_test_destroy, struct Qdisc *sch)
+{
+}
+
+SEC(".struct_ops")
+struct Qdisc_ops test = {
+ .enqueue = (void *)invalid_dynptr_returned_slice,
+ .dequeue = (void *)bpf_qdisc_test_dequeue,
+ .init = (void *)bpf_qdisc_test_init,
+ .reset = (void *)bpf_qdisc_test_reset,
+ .destroy = (void *)bpf_qdisc_test_destroy,
+ .id = "bpf_qdisc_test",
+};
diff --git a/tools/testing/selftests/bpf/progs/dynptr_fail.c b/tools/testing/selftests/bpf/progs/dynptr_fail.c
index 0d13f828710e7..8c5387388d45f 100644
--- a/tools/testing/selftests/bpf/progs/dynptr_fail.c
+++ b/tools/testing/selftests/bpf/progs/dynptr_fail.c
@@ -1895,6 +1895,101 @@ int clone_invalidate4(void *ctx)
return 0;
}
+static __noinline void clone_slice_in_subprog(struct bpf_dynptr *ptr, int **data)
+{
+ struct bpf_dynptr clone;
+
+ bpf_dynptr_clone(ptr, &clone);
+ *data = bpf_dynptr_data(&clone, 0, sizeof(val));
+}
+
+static __noinline void caller_slice_in_subprog(struct bpf_dynptr *ptr, int **data)
+{
+ struct bpf_dynptr clone;
+
+ *data = bpf_dynptr_data(ptr, 0, sizeof(val));
+ bpf_dynptr_clone(ptr, &clone);
+}
+
+static __noinline void reserve_dynptr_in_subprog(void)
+{
+ struct bpf_dynptr ptr;
+
+ bpf_ringbuf_reserve_dynptr(&ringbuf, val, 0, &ptr);
+}
+
+/* A subprogram cannot lose the last dynptr that can release a resource. */
+SEC("?raw_tp")
+__failure __msg("cannot overwrite referenced dynptr")
+int referenced_dynptr_lost_on_subprog_return(void *ctx)
+{
+ reserve_dynptr_in_subprog();
+
+ return 0;
+}
+
+/*
+ * Destroying a callee-local clone on return must not invalidate a slice whose
+ * source dynptr belongs to the caller.
+ */
+SEC("?raw_tp")
+__success
+int caller_dynptr_slice_across_subprog_valid(void *ctx)
+{
+ struct bpf_dynptr ptr;
+ int *data = NULL;
+
+ bpf_ringbuf_reserve_dynptr(&ringbuf, val, 0, &ptr);
+ caller_slice_in_subprog(&ptr, &data);
+ if (data)
+ *data = 123;
+ bpf_ringbuf_submit_dynptr(&ptr, 0);
+
+ return 0;
+}
+
+/*
+ * A slice derived from a callee-local clone is invalid after the subprogram
+ * returns.
+ */
+SEC("?raw_tp")
+__failure __msg("invalid mem access 'scalar'")
+int callee_dynptr_slice_invalid_after_return(void *ctx)
+{
+ struct bpf_dynptr ptr;
+ int *data = NULL;
+
+ bpf_ringbuf_reserve_dynptr(&ringbuf, val, 0, &ptr);
+ clone_slice_in_subprog(&ptr, &data);
+ if (data)
+ /* this should fail */
+ *data = 123;
+ bpf_ringbuf_submit_dynptr(&ptr, 0);
+
+ return 0;
+}
+
+/*
+ * A slice from a caller-owned dynptr survives the subprogram return, but
+ * releasing the shared reservation must invalidate it.
+ */
+SEC("?raw_tp")
+__failure __msg("invalid mem access 'scalar'")
+int caller_dynptr_slice_release_after_subprog_invalid(void *ctx)
+{
+ struct bpf_dynptr ptr;
+ int *data = NULL;
+
+ bpf_ringbuf_reserve_dynptr(&ringbuf, val, 0, &ptr);
+ caller_slice_in_subprog(&ptr, &data);
+ bpf_ringbuf_submit_dynptr(&ptr, 0);
+ if (data)
+ /* this should fail */
+ *data = 123;
+
+ return 0;
+}
+
/* Invalidating a dynptr should invalidate any data slices
* of its parent
*/
--
2.43.0
^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH bpf-next v6 1/2] bpf: Destroy callee-local dynptrs on subprog return
2026-09-27 12:04 ` [PATCH bpf-next v6 1/2] " Xu Yunxiang
@ 2026-09-28 4:48 ` Amery Hung
2026-10-02 11:29 ` Alexei Starovoitov
0 siblings, 1 reply; 9+ messages in thread
From: Amery Hung @ 2026-09-28 4:48 UTC (permalink / raw)
To: Xu Yunxiang; +Cc: bpf, ast, daniel, andrii, eddyz87, memxor, sashiko-bot
On Sun, Sep 27, 2026 at 5:04 AM Xu Yunxiang <xyx2021@mail.ustc.edu.cn> wrote:
>
> A data slice is valid only while the specific dynptr that produced it
> remains valid. prepare_func_exit() copies a subprogram return value into
> the caller and then frees the callee state without applying the normal
> destruction semantics to dynptrs in the callee stack.
>
> A callee can therefore derive a slice from a local dynptr, return or spill
> the slice into its caller, and then let the dynptr disappear with the
> callee frame. The slice retains the id of a dynptr that is no longer in the
> verifier state, so the verifier continues to accept accesses through it.
>
> Before the object relationship refactor, bpf_dynptr_data() slices from
> referenced dynptrs also carried the shared reference id, allowing a later
> release to catch some of these escaped slices. The refactor made those
> slices precise children of their source dynptr and exposed the missing
> teardown as an accepted stale access even after the shared resource is
> released.
>
> Add destroy_dynptrs_in_stack_slots() to apply the existing dynptr teardown
> to an inclusive range of stack slots. Reuse it for dynptr initialization,
> variable-offset stack writes, and the entire callee stack before freeing
> the frame. The teardown rejects losing the last dynptr for a referenced
> resource and invalidates the dynptr and all its descendants, including
> slices held in the caller. Propagate errors through prepare_func_exit().
>
> Reuse the existing teardown API and diagnostic. Update the affected
> callback test expectation in this change so the commit remains test-clean.
>
> Fixes: 308c7a0ae885 ("bpf: Refactor object relationship tracking and fix dynptr UAF bug")
> Reported-by: Sashiko <sashiko-bot@kernel.org>
> Closes: https://lore.kernel.org/r/20260829040712.B1B511F000E9@smtp.kernel.org
> Suggested-by: Amery Hung <ameryhung@gmail.com>
> Link: https://lore.kernel.org/r/CAMB2axOmdHwSrHihSRskDywkCmEGQOX+6dZPN_A9u-HhxY3UDA@mail.gmail.com
> Link: https://lore.kernel.org/r/CAMB2axMmrTb+UZ84UD48MwMtXbk1s9bWtreU3AOfjzU0fPT0BA@mail.gmail.com
> Assisted-by: LLM
> Signed-off-by: Xu Yunxiang <xyx2021@mail.ustc.edu.cn>
Reviewed-by: Amery Hung <ameryhung@gmail.com>
Just a note: this series addresses a related but distinct problem from
Ihor’s work [0]. This series handles descendants escaping from
callee-local stack dynptrs, while Ihor’s handles helper-owned callback
arguments whose lifetime ends when the callback returns.
[...]
> @@ -11675,6 +11683,12 @@ static int prepare_func_exit(struct bpf_verifier_env *env, int *insn_idx)
> }
> }
>
> + /* Invalidate callee-local dynptrs and their slices before the frame goes away. */
> + err = destroy_dynptrs_in_stack_slots(env, callee, 0,
> + callee->allocated_stack / BPF_REG_SIZE - 1);
> + if (err)
> + return err;
> +
Conceptually, Ihor’s frame-reference mechanism could subsume this
cleanup if every local dynptr had a separate frame-ownership
relationship. The current parent_id cannot express that directly
because dynptrs already use it for object and shared-resource
ancestry.
Supporting both would require another ownership edge or field, rather
than simply assigning parent_id to the REF_TYPE_FRAME id.
[0] https://lore.kernel.org/r/20260922010333.1226537-1-ihor.solodrai@linux.dev
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH bpf-next v6 2/2] selftests/bpf: Test dynptr teardown on subprog return
2026-09-27 12:04 ` [PATCH bpf-next v6 2/2] selftests/bpf: Test dynptr teardown " Xu Yunxiang
@ 2026-09-28 5:24 ` Amery Hung
0 siblings, 0 replies; 9+ messages in thread
From: Amery Hung @ 2026-09-28 5:24 UTC (permalink / raw)
To: Xu Yunxiang; +Cc: bpf, ast, daniel, andrii, eddyz87, memxor, sashiko-bot
On Sun, Sep 27, 2026 at 5:04 AM Xu Yunxiang <xyx2021@mail.ustc.edu.cn> wrote:
>
> Add failure tests for slices that escape a subprogram from a dynptr in the
> callee stack. Cover immediate use of a ring buffer clone slice,
> release-time invalidation of a caller dynptr slice after it crosses a
> subprogram return, and immediate use of an skb dynptr slice in a qdisc
> program.
>
> Cover the error path where a subprogram would otherwise lose the last
> dynptr capable of releasing a referenced resource.
>
> Also add a success control which derives a slice from a caller-owned dynptr
> and creates a separate clone in the callee. Destroying that callee-local
> clone on return must not invalidate the caller dynptr's slice.
>
> Assisted-by: LLM
> Signed-off-by: Xu Yunxiang <xyx2021@mail.ustc.edu.cn>
Reviewed-by: Amery Hung <ameryhung@gmail.com>
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH bpf-next v6 1/2] bpf: Destroy callee-local dynptrs on subprog return
2026-09-28 4:48 ` Amery Hung
@ 2026-10-02 11:29 ` Alexei Starovoitov
2026-10-05 21:59 ` Ihor Solodrai
0 siblings, 1 reply; 9+ messages in thread
From: Alexei Starovoitov @ 2026-10-02 11:29 UTC (permalink / raw)
To: Amery Hung, Xu Yunxiang
Cc: bpf, ast, daniel, andrii, eddyz87, memxor, sashiko-bot
On Mon Sep 28, 2026 at 4:48 AM UTC, Amery Hung wrote:
> On Sun, Sep 27, 2026 at 5:04 AM Xu Yunxiang <xyx2021@mail.ustc.edu.cn> wrote:
>>
>> A data slice is valid only while the specific dynptr that produced it
>> remains valid. prepare_func_exit() copies a subprogram return value into
>> the caller and then frees the callee state without applying the normal
>> destruction semantics to dynptrs in the callee stack.
>>
>> A callee can therefore derive a slice from a local dynptr, return or spill
>> the slice into its caller, and then let the dynptr disappear with the
>> callee frame. The slice retains the id of a dynptr that is no longer in the
>> verifier state, so the verifier continues to accept accesses through it.
>>
>> Before the object relationship refactor, bpf_dynptr_data() slices from
>> referenced dynptrs also carried the shared reference id, allowing a later
>> release to catch some of these escaped slices. The refactor made those
>> slices precise children of their source dynptr and exposed the missing
>> teardown as an accepted stale access even after the shared resource is
>> released.
>>
>> Add destroy_dynptrs_in_stack_slots() to apply the existing dynptr teardown
>> to an inclusive range of stack slots. Reuse it for dynptr initialization,
>> variable-offset stack writes, and the entire callee stack before freeing
>> the frame. The teardown rejects losing the last dynptr for a referenced
>> resource and invalidates the dynptr and all its descendants, including
>> slices held in the caller. Propagate errors through prepare_func_exit().
>>
>> Reuse the existing teardown API and diagnostic. Update the affected
>> callback test expectation in this change so the commit remains test-clean.
>>
>> Fixes: 308c7a0ae885 ("bpf: Refactor object relationship tracking and fix dynptr UAF bug")
>> Reported-by: Sashiko <sashiko-bot@kernel.org>
>> Closes: https://lore.kernel.org/r/20260829040712.B1B511F000E9@smtp.kernel.org
>> Suggested-by: Amery Hung <ameryhung@gmail.com>
>> Link: https://lore.kernel.org/r/CAMB2axOmdHwSrHihSRskDywkCmEGQOX+6dZPN_A9u-HhxY3UDA@mail.gmail.com
>> Link: https://lore.kernel.org/r/CAMB2axMmrTb+UZ84UD48MwMtXbk1s9bWtreU3AOfjzU0fPT0BA@mail.gmail.com
>> Assisted-by: LLM
>> Signed-off-by: Xu Yunxiang <xyx2021@mail.ustc.edu.cn>
>
> Reviewed-by: Amery Hung <ameryhung@gmail.com>
>
> Just a note: this series addresses a related but distinct problem from
> Ihor’s work [0]. This series handles descendants escaping from
> callee-local stack dynptrs, while Ihor’s handles helper-owned callback
> arguments whose lifetime ends when the callback returns.
>
> [...]
>
>> @@ -11675,6 +11683,12 @@ static int prepare_func_exit(struct bpf_verifier_env *env, int *insn_idx)
>> }
>> }
>>
>> + /* Invalidate callee-local dynptrs and their slices before the frame goes away. */
>> + err = destroy_dynptrs_in_stack_slots(env, callee, 0,
>> + callee->allocated_stack / BPF_REG_SIZE - 1);
>> + if (err)
>> + return err;
>> +
>
> Conceptually, Ihor’s frame-reference mechanism could subsume this
> cleanup if every local dynptr had a separate frame-ownership
> relationship. The current parent_id cannot express that directly
> because dynptrs already use it for object and shared-resource
> ancestry.
> Supporting both would require another ownership edge or field, rather
> than simply assigning parent_id to the REF_TYPE_FRAME id.
>
> [0] https://lore.kernel.org/r/20260922010333.1226537-1-ihor.solodrai@linux.dev
I feel it's ok to apply as-is or should we defer until Ihor's fix?
pw-bot: cr
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH bpf-next v6 1/2] bpf: Destroy callee-local dynptrs on subprog return
2026-10-02 11:29 ` Alexei Starovoitov
@ 2026-10-05 21:59 ` Ihor Solodrai
2026-10-05 22:33 ` Amery Hung
0 siblings, 1 reply; 9+ messages in thread
From: Ihor Solodrai @ 2026-10-05 21:59 UTC (permalink / raw)
To: Alexei Starovoitov, Amery Hung, Xu Yunxiang
Cc: bpf, ast, daniel, andrii, eddyz87, memxor, sashiko-bot
On 10/2/26 4:29 AM, Alexei Starovoitov wrote:
> On Mon Sep 28, 2026 at 4:48 AM UTC, Amery Hung wrote:
>> On Sun, Sep 27, 2026 at 5:04 AM Xu Yunxiang <xyx2021@mail.ustc.edu.cn> wrote:
>>>
>>> A data slice is valid only while the specific dynptr that produced it
>>> remains valid. prepare_func_exit() copies a subprogram return value into
>>> the caller and then frees the callee state without applying the normal
>>> destruction semantics to dynptrs in the callee stack.
>>>
>>> A callee can therefore derive a slice from a local dynptr, return or spill
>>> the slice into its caller, and then let the dynptr disappear with the
>>> callee frame. The slice retains the id of a dynptr that is no longer in the
>>> verifier state, so the verifier continues to accept accesses through it.
>>>
>>> Before the object relationship refactor, bpf_dynptr_data() slices from
>>> referenced dynptrs also carried the shared reference id, allowing a later
>>> release to catch some of these escaped slices. The refactor made those
>>> slices precise children of their source dynptr and exposed the missing
>>> teardown as an accepted stale access even after the shared resource is
>>> released.
>>>
>>> Add destroy_dynptrs_in_stack_slots() to apply the existing dynptr teardown
>>> to an inclusive range of stack slots. Reuse it for dynptr initialization,
>>> variable-offset stack writes, and the entire callee stack before freeing
>>> the frame. The teardown rejects losing the last dynptr for a referenced
>>> resource and invalidates the dynptr and all its descendants, including
>>> slices held in the caller. Propagate errors through prepare_func_exit().
>>>
>>> Reuse the existing teardown API and diagnostic. Update the affected
>>> callback test expectation in this change so the commit remains test-clean.
>>>
>>> Fixes: 308c7a0ae885 ("bpf: Refactor object relationship tracking and fix dynptr UAF bug")
>>> Reported-by: Sashiko <sashiko-bot@kernel.org>
>>> Closes: https://lore.kernel.org/r/20260829040712.B1B511F000E9@smtp.kernel.org
>>> Suggested-by: Amery Hung <ameryhung@gmail.com>
>>> Link: https://lore.kernel.org/r/CAMB2axOmdHwSrHihSRskDywkCmEGQOX+6dZPN_A9u-HhxY3UDA@mail.gmail.com
>>> Link: https://lore.kernel.org/r/CAMB2axMmrTb+UZ84UD48MwMtXbk1s9bWtreU3AOfjzU0fPT0BA@mail.gmail.com
>>> Assisted-by: LLM
>>> Signed-off-by: Xu Yunxiang <xyx2021@mail.ustc.edu.cn>
>>
>> Reviewed-by: Amery Hung <ameryhung@gmail.com>
>>
>> Just a note: this series addresses a related but distinct problem from
>> Ihor’s work [0]. This series handles descendants escaping from
>> callee-local stack dynptrs, while Ihor’s handles helper-owned callback
>> arguments whose lifetime ends when the callback returns.
>>
>> [...]
>>
>>> @@ -11675,6 +11683,12 @@ static int prepare_func_exit(struct bpf_verifier_env *env, int *insn_idx)
>>> }
>>> }
>>>
>>> + /* Invalidate callee-local dynptrs and their slices before the frame goes away. */
>>> + err = destroy_dynptrs_in_stack_slots(env, callee, 0,
>>> + callee->allocated_stack / BPF_REG_SIZE - 1);
>>> + if (err)
>>> + return err;
>>> +
>>
>> Conceptually, Ihor’s frame-reference mechanism could subsume this
>> cleanup if every local dynptr had a separate frame-ownership
>> relationship. The current parent_id cannot express that directly
>> because dynptrs already use it for object and shared-resource
>> ancestry.
>> Supporting both would require another ownership edge or field, rather
>> than simply assigning parent_id to the REF_TYPE_FRAME id.
>>
>> [0] https://lore.kernel.org/r/20260922010333.1226537-1-ihor.solodrai@linux.dev
>
>
> I feel it's ok to apply as-is or should we defer until Ihor's fix?
Let's apply this fix as is.
Acked-by: Ihor Solodrai <ihor.solodrai@linux.dev>
It can probably be backported, and there are no conflicts with what
I'm doing.
The REF_TYPE_FRAME change is a more long-term thing, not easily
backportable. And it's not ready to land yet, so no reason to wait.
Thanks.
>
> pw-bot: cr
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH bpf-next v6 1/2] bpf: Destroy callee-local dynptrs on subprog return
2026-10-05 21:59 ` Ihor Solodrai
@ 2026-10-05 22:33 ` Amery Hung
0 siblings, 0 replies; 9+ messages in thread
From: Amery Hung @ 2026-10-05 22:33 UTC (permalink / raw)
To: Ihor Solodrai
Cc: Alexei Starovoitov, Xu Yunxiang, bpf, ast, daniel, andrii,
eddyz87, memxor, sashiko-bot
On Mon, Oct 5, 2026 at 2:59 PM Ihor Solodrai <ihor.solodrai@linux.dev> wrote:
>
> On 10/2/26 4:29 AM, Alexei Starovoitov wrote:
> > On Mon Sep 28, 2026 at 4:48 AM UTC, Amery Hung wrote:
> >> On Sun, Sep 27, 2026 at 5:04 AM Xu Yunxiang <xyx2021@mail.ustc.edu.cn> wrote:
> >>>
> >>> A data slice is valid only while the specific dynptr that produced it
> >>> remains valid. prepare_func_exit() copies a subprogram return value into
> >>> the caller and then frees the callee state without applying the normal
> >>> destruction semantics to dynptrs in the callee stack.
> >>>
> >>> A callee can therefore derive a slice from a local dynptr, return or spill
> >>> the slice into its caller, and then let the dynptr disappear with the
> >>> callee frame. The slice retains the id of a dynptr that is no longer in the
> >>> verifier state, so the verifier continues to accept accesses through it.
> >>>
> >>> Before the object relationship refactor, bpf_dynptr_data() slices from
> >>> referenced dynptrs also carried the shared reference id, allowing a later
> >>> release to catch some of these escaped slices. The refactor made those
> >>> slices precise children of their source dynptr and exposed the missing
> >>> teardown as an accepted stale access even after the shared resource is
> >>> released.
> >>>
> >>> Add destroy_dynptrs_in_stack_slots() to apply the existing dynptr teardown
> >>> to an inclusive range of stack slots. Reuse it for dynptr initialization,
> >>> variable-offset stack writes, and the entire callee stack before freeing
> >>> the frame. The teardown rejects losing the last dynptr for a referenced
> >>> resource and invalidates the dynptr and all its descendants, including
> >>> slices held in the caller. Propagate errors through prepare_func_exit().
> >>>
> >>> Reuse the existing teardown API and diagnostic. Update the affected
> >>> callback test expectation in this change so the commit remains test-clean.
> >>>
> >>> Fixes: 308c7a0ae885 ("bpf: Refactor object relationship tracking and fix dynptr UAF bug")
> >>> Reported-by: Sashiko <sashiko-bot@kernel.org>
> >>> Closes: https://lore.kernel.org/r/20260829040712.B1B511F000E9@smtp.kernel.org
> >>> Suggested-by: Amery Hung <ameryhung@gmail.com>
> >>> Link: https://lore.kernel.org/r/CAMB2axOmdHwSrHihSRskDywkCmEGQOX+6dZPN_A9u-HhxY3UDA@mail.gmail.com
> >>> Link: https://lore.kernel.org/r/CAMB2axMmrTb+UZ84UD48MwMtXbk1s9bWtreU3AOfjzU0fPT0BA@mail.gmail.com
> >>> Assisted-by: LLM
> >>> Signed-off-by: Xu Yunxiang <xyx2021@mail.ustc.edu.cn>
> >>
> >> Reviewed-by: Amery Hung <ameryhung@gmail.com>
> >>
> >> Just a note: this series addresses a related but distinct problem from
> >> Ihor’s work [0]. This series handles descendants escaping from
> >> callee-local stack dynptrs, while Ihor’s handles helper-owned callback
> >> arguments whose lifetime ends when the callback returns.
> >>
> >> [...]
> >>
> >>> @@ -11675,6 +11683,12 @@ static int prepare_func_exit(struct bpf_verifier_env *env, int *insn_idx)
> >>> }
> >>> }
> >>>
> >>> + /* Invalidate callee-local dynptrs and their slices before the frame goes away. */
> >>> + err = destroy_dynptrs_in_stack_slots(env, callee, 0,
> >>> + callee->allocated_stack / BPF_REG_SIZE - 1);
> >>> + if (err)
> >>> + return err;
> >>> +
> >>
> >> Conceptually, Ihor’s frame-reference mechanism could subsume this
> >> cleanup if every local dynptr had a separate frame-ownership
> >> relationship. The current parent_id cannot express that directly
> >> because dynptrs already use it for object and shared-resource
> >> ancestry.
> >> Supporting both would require another ownership edge or field, rather
> >> than simply assigning parent_id to the REF_TYPE_FRAME id.
> >>
> >> [0] https://lore.kernel.org/r/20260922010333.1226537-1-ihor.solodrai@linux.dev
> >
> >
> > I feel it's ok to apply as-is or should we defer until Ihor's fix?
>
> Let's apply this fix as is.
Second this.
>
> Acked-by: Ihor Solodrai <ihor.solodrai@linux.dev>
>
> It can probably be backported, and there are no conflicts with what
> I'm doing.
>
> The REF_TYPE_FRAME change is a more long-term thing, not easily
> backportable. And it's not ready to land yet, so no reason to wait.
>
> Thanks.
>
> >
> > pw-bot: cr
>
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH bpf-next v6 0/2] bpf: Destroy callee-local dynptrs on subprog return
2026-09-27 12:04 [PATCH bpf-next v6 0/2] bpf: Destroy callee-local dynptrs on subprog return Xu Yunxiang
2026-09-27 12:04 ` [PATCH bpf-next v6 1/2] " Xu Yunxiang
2026-09-27 12:04 ` [PATCH bpf-next v6 2/2] selftests/bpf: Test dynptr teardown " Xu Yunxiang
@ 2026-10-05 23:40 ` patchwork-bot+netdevbpf
2 siblings, 0 replies; 9+ messages in thread
From: patchwork-bot+netdevbpf @ 2026-10-05 23:40 UTC (permalink / raw)
To: Xu Yunxiang
Cc: bpf, ast, daniel, andrii, eddyz87, memxor, ameryhung, sashiko-bot
Hello:
This series was applied to bpf/bpf-next.git (master)
by Kumar Kartikeya Dwivedi <memxor@gmail.com>:
On Sun, 27 Sep 2026 20:04:20 +0800 you wrote:
> A dynptr data slice is valid only while the specific dynptr that produced
> it remains valid. Apply normal dynptr stack-slot teardown before a callee
> frame disappears, so its descendants are invalidated and its last
> release-capable referenced dynptr cannot be lost.
>
> Patch 1 adds a helper to destroy dynptrs in an inclusive range of stack
> slots and reuses it for initialization, variable-offset writes and
> subprogram returns. The callback diagnostic expectation changes in that
> same commit. Patch 2 tests callee-local slices, last-holder rejection and
> caller-owned slice compatibility.
>
> [...]
Here is the summary with links:
- [bpf-next,v6,1/2] bpf: Track borrowed iterator results by default
(no matching commit)
- [bpf-next,v6,2/2] selftests/bpf: Test dynptr teardown on subprog return
https://git.kernel.org/bpf/bpf-next/c/b04ba9375a9e
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2026-10-05 23:40 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-27 12:04 [PATCH bpf-next v6 0/2] bpf: Destroy callee-local dynptrs on subprog return Xu Yunxiang
2026-09-27 12:04 ` [PATCH bpf-next v6 1/2] " Xu Yunxiang
2026-09-28 4:48 ` Amery Hung
2026-10-02 11:29 ` Alexei Starovoitov
2026-10-05 21:59 ` Ihor Solodrai
2026-10-05 22:33 ` Amery Hung
2026-09-27 12:04 ` [PATCH bpf-next v6 2/2] selftests/bpf: Test dynptr teardown " Xu Yunxiang
2026-09-28 5:24 ` Amery Hung
2026-10-05 23:40 ` [PATCH bpf-next v6 0/2] bpf: Destroy callee-local dynptrs " patchwork-bot+netdevbpf
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox