* [PATCH 6.12.y v4 0/3] bpf: fix dynptr release handling
@ 2026-08-31 0:15 Xu Yunxiang
2026-08-31 0:15 ` [PATCH 6.12.y v4 1/3] bpf: Invalidate dynptr slices by dynptr_id on release Xu Yunxiang
` (3 more replies)
0 siblings, 4 replies; 8+ messages in thread
From: Xu Yunxiang @ 2026-08-31 0:15 UTC (permalink / raw)
To: stable
Cc: gregkh, bpf, ast, daniel, andrii, eddyz87, martin.lau, ameryhung,
joannelkoong, sashal
Upstream commit 308c7a0ae885 ("bpf: Refactor object relationship
tracking and fix dynptr UAF bug") fixes these lifetime bugs as part of
an 11-file parent_id refactor. That refactor cannot be applied to
6.12.y, which still represents the relationships with dynptr_id and
ref_obj_id.
This series supplies three stable-sized equivalents using the existing
6.12.y representation. Patch 1 invalidates slice registers with the
released dynptr_id. Patch 2 propagates a referenced-dynptr release
error instead of turning a callback rejection into a kernel warning.
Patch 3 scans every active call frame when invalidating cloned dynptr
stack slots. They are split because the bugs have different
introducing commits and independently testable effects.
These patches are stable-specific implementations authored for the old
verifier, not cherry-picks of Amery Hung's upstream diff. Each commit
keeps 308c7a0ae885 as the upstream provenance for the equivalent fix
and documents how its implementation differs.
Changes in v4:
- make Xu Yunxiang the author of all three stable-specific
implementations;
- remove the authorship, sign-off, and ack trailers copied from
308c7a0ae885 because those developers did not write or review these
rewritten stable patches;
- remove the backporter notes and retain the upstream commit only as
provenance for the equivalent fixes;
- leave all code and tests byte-for-byte unchanged from v3.
v3: https://lore.kernel.org/r/20260829102638.105170-1-xyx2021@mail.ustc.edu.cn
Changes in v3:
- check that a register has base type PTR_TO_MEM before reading its
dynptr_id union member, while retaining DYNPTR_TYPE_* flags;
- add a bpf_dynptr_slice() use-after-submit regression that reaches the
dynptr_id-only slice path on the target stable verifier;
- add a separate patch that propagates release_reference() errors from
referenced dynptr release and a callback regression test. This avoids
the verifier WARN, and the panic it causes with panic_on_warn=1, when a
callback tries to release its caller's dynptr;
- make the cross-frame clone regression use bpf_dynptr_data() after the
callee releases a clone, so it uniquely tests caller-slot invalidation
instead of a repeated release already rejected by patch 2;
- check STACK_DYNPTR before reading dynptr metadata while scanning call
frames, so a partially overwritten ordinary spill with stale metadata
is ignored, and add a success regression test;
- investigate the separately reported slice escape through a returned
subprogram frame. A minimal reproducer is also accepted by current
mainline after 308c7a0ae885, so that issue is not folded into this
stable-only series and needs a separate mainline fix.
Changes in v2:
- restore Amery as the author and retain the complete upstream trailer
chain;
- add bracketed notes documenting the stable-only implementation;
- remove the exact PTR_TO_MEM type filter so slices carrying
DYNPTR_TYPE_* flags are invalidated by dynptr_id;
- add the applicable dynptr slice/clone Fixes tags and identify the
reporter;
- add a cross-frame clone regression test and tighten the slice test.
The code and tests are unchanged from v3. The following results were
obtained on that identical code tree with Linux 6.12.107 and
panic_on_warn=1:
- test_progs -t dynptr -v --workers=1: 2/96 passed, 0 failed;
- the slice-after-submit and cross-frame stale-use programs were
rejected, while their control programs loaded successfully;
- the callback-release reproducer was rejected without a verifier
warning;
- the independent submit/discard, stale-spill, and cross-frame
reproducers produced the expected results.
Xu Yunxiang (3):
bpf: Invalidate dynptr slices by dynptr_id on release
bpf: Propagate referenced dynptr release errors
bpf: Invalidate dynptr clones in every call frame on release
kernel/bpf/verifier.c | 55 +++++++++----
.../testing/selftests/bpf/progs/dynptr_fail.c | 79 +++++++++++++++++++
2 files changed, 119 insertions(+), 15 deletions(-)
base-commit: f717995cb7dcd8998ab15516b8006aea09cfde0d
--
2.43.0
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH 6.12.y v4 1/3] bpf: Invalidate dynptr slices by dynptr_id on release
2026-08-31 0:15 [PATCH 6.12.y v4 0/3] bpf: fix dynptr release handling Xu Yunxiang
@ 2026-08-31 0:15 ` Xu Yunxiang
2026-08-31 0:15 ` [PATCH 6.12.y v4 2/3] bpf: Propagate referenced dynptr release errors Xu Yunxiang
` (2 subsequent siblings)
3 siblings, 0 replies; 8+ messages in thread
From: Xu Yunxiang @ 2026-08-31 0:15 UTC (permalink / raw)
To: stable
Cc: gregkh, bpf, ast, daniel, andrii, eddyz87, martin.lau, ameryhung,
joannelkoong, sashal
[ Upstream commit 308c7a0ae8859b34d9d90a3dff953b2d14242145 ]
This is a 6.12.y-sized equivalent, not a cherry-pick of the upstream
diff.
unmark_stack_slots_dynptr() comments say it invalidates slices of the
released dynptr, but it only calls release_reference(ref_obj_id).
bpf_dynptr_slice() and bpf_dynptr_slice_rdwr() stamp dynptr_id and
leave ref_obj_id at 0, so LDX/STX through those slices remains allowed
after bpf_ringbuf_submit_dynptr() / bpf_ringbuf_discard_dynptr().
bpf_dynptr_data() slices already carry ref_obj_id.
Walk registers with a matching dynptr_id, the same way
destroy_if_dynptr_stack_slot() already does when a dynptr stack slot is
overwritten. Do this for the released dynptr, for each clone, and for
the non-refcounted path.
Check the register base type before reading dynptr_id because the field
shares a union with other register metadata. Use base_type() rather than
an exact type match so slices carrying DYNPTR_TYPE_* flags remain covered.
The upstream commit is Amery Hung's parent_id refactor (Fixes:
870c28588afa, qdisc kfuncs). It cannot be applied here:
- It is an 11-file bpf-next change, well over the 100-line stable
limit, and it removes dynptr_id / ref_obj_id.
- 6.12.y still uses dynptr_id and does not have the qdisc kfuncs in
that Fixes: tag, so AUTOSEL never pulled it.
Deviations from 308c7a0ae8859: keep dynptr_id and ref_obj_id; do not
introduce parent_id; only add slice invalidation on
unmark_stack_slots_dynptr().
Fixes: 66e3a13e7c2c ("bpf: Add bpf_dynptr_slice and bpf_dynptr_slice_rdwr")
Reported-by: Xu Yunxiang <xyx2021@mail.ustc.edu.cn>
Closes: https://lore.kernel.org/r/20260829033725.2365697-2-xyx2021@mail.ustc.edu.cn
Assisted-by: Pi:GLM-5.3
Signed-off-by: Xu Yunxiang <xyx2021@mail.ustc.edu.cn>
---
kernel/bpf/verifier.c | 27 +++++++++++++++++--
.../testing/selftests/bpf/progs/dynptr_fail.c | 20 ++++++++++++++
2 files changed, 45 insertions(+), 2 deletions(-)
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 99b3f539e431..dbc9cfcffcd6 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -805,6 +805,9 @@ static void invalidate_dynptr(struct bpf_verifier_env *env, struct bpf_func_stat
state->stack[spi - 1].spilled_ptr.live |= REG_LIVE_WRITTEN;
}
+static void mark_reg_invalid(const struct bpf_verifier_env *env, struct bpf_reg_state *reg);
+static void invalidate_slices_of_dynptr(struct bpf_verifier_env *env, int dynptr_id);
+
static int unmark_stack_slots_dynptr(struct bpf_verifier_env *env, struct bpf_reg_state *reg)
{
struct bpf_func_state *state = func(env, reg);
@@ -815,6 +818,7 @@ static int unmark_stack_slots_dynptr(struct bpf_verifier_env *env, struct bpf_re
return spi;
if (!dynptr_type_refcounted(state->stack[spi].spilled_ptr.dynptr.type)) {
+ invalidate_slices_of_dynptr(env, state->stack[spi].spilled_ptr.id);
invalidate_dynptr(env, state, spi);
return 0;
}
@@ -828,8 +832,14 @@ static int unmark_stack_slots_dynptr(struct bpf_verifier_env *env, struct bpf_re
* 2) Any slices derived from this dynptr.
*/
- /* Invalidate any slices associated with this dynptr */
+ /* Invalidate any slices associated with this dynptr.
+ * release_reference() only walks registers that carry ref_obj_id;
+ * slices are PTR_TO_MEM with dynptr_id set and ref_obj_id left 0.
+ * Mirror destroy_if_dynptr_stack_slot(), which already invalidates
+ * slices by dynptr_id.
+ */
WARN_ON_ONCE(release_reference(env, ref_obj_id));
+ invalidate_slices_of_dynptr(env, state->stack[spi].spilled_ptr.id);
/* Invalidate any dynptr clones */
for (i = 1; i < state->allocated_stack / BPF_REG_SIZE; i++) {
@@ -844,8 +854,10 @@ static int unmark_stack_slots_dynptr(struct bpf_verifier_env *env, struct bpf_re
verbose(env, "verifier internal error: misconfigured ref_obj_id\n");
return -EFAULT;
}
- if (state->stack[i].spilled_ptr.dynptr.first_slot)
+ if (state->stack[i].spilled_ptr.dynptr.first_slot) {
+ invalidate_slices_of_dynptr(env, state->stack[i].spilled_ptr.id);
invalidate_dynptr(env, state, i);
+ }
}
return 0;
@@ -862,6 +874,17 @@ static void mark_reg_invalid(const struct bpf_verifier_env *env, struct bpf_reg_
__mark_reg_unknown(env, reg);
}
+static void invalidate_slices_of_dynptr(struct bpf_verifier_env *env, int dynptr_id)
+{
+ struct bpf_func_state *fstate;
+ struct bpf_reg_state *dreg;
+
+ bpf_for_each_reg_in_vstate(env->cur_state, fstate, dreg, ({
+ if (base_type(dreg->type) == PTR_TO_MEM && dreg->dynptr_id == dynptr_id)
+ mark_reg_invalid(env, dreg);
+ }));
+}
+
static int destroy_if_dynptr_stack_slot(struct bpf_verifier_env *env,
struct bpf_func_state *state, int spi)
{
diff --git a/tools/testing/selftests/bpf/progs/dynptr_fail.c b/tools/testing/selftests/bpf/progs/dynptr_fail.c
index dfd817d0348c..61bd3f7c946f 100644
--- a/tools/testing/selftests/bpf/progs/dynptr_fail.c
+++ b/tools/testing/selftests/bpf/progs/dynptr_fail.c
@@ -316,6 +316,26 @@ int data_slice_use_after_release1(void *ctx)
return 0;
}
+/* Releasing a dynptr must invalidate slices that only carry dynptr_id. */
+SEC("?raw_tp")
+__failure __msg("invalid mem access 'scalar'")
+int slice_kfunc_use_after_submit(void *ctx)
+{
+ struct bpf_dynptr ptr;
+ struct sample *sample;
+
+ bpf_ringbuf_reserve_dynptr(&ringbuf, sizeof(*sample), 0, &ptr);
+ sample = bpf_dynptr_slice(&ptr, 0, NULL, sizeof(*sample));
+ if (!sample) {
+ bpf_ringbuf_discard_dynptr(&ptr, 0);
+ return 0;
+ }
+ bpf_ringbuf_submit_dynptr(&ptr, 0);
+ /* this should fail */
+ val = sample->pid;
+ return 0;
+}
+
/* A data slice can't be used after it has been released.
*
* This tests the case where the data slice tracks a dynptr (ptr2)
--
2.43.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH 6.12.y v4 2/3] bpf: Propagate referenced dynptr release errors
2026-08-31 0:15 [PATCH 6.12.y v4 0/3] bpf: fix dynptr release handling Xu Yunxiang
2026-08-31 0:15 ` [PATCH 6.12.y v4 1/3] bpf: Invalidate dynptr slices by dynptr_id on release Xu Yunxiang
@ 2026-08-31 0:15 ` Xu Yunxiang
2026-08-31 0:36 ` sashiko-bot
2026-08-31 0:15 ` [PATCH 6.12.y v4 3/3] bpf: Invalidate dynptr clones in every call frame on release Xu Yunxiang
2026-08-31 11:54 ` [PATCH 6.12.y v4 0/3] bpf: fix dynptr release handling Greg KH
3 siblings, 1 reply; 8+ messages in thread
From: Xu Yunxiang @ 2026-08-31 0:15 UTC (permalink / raw)
To: stable
Cc: gregkh, bpf, ast, daniel, andrii, eddyz87, martin.lau, ameryhung,
joannelkoong, sashal
[ Upstream commit 308c7a0ae8859b34d9d90a3dff953b2d14242145 ]
This is a stable-sized equivalent, not a cherry-pick of the upstream
diff.
release_reference_state() rejects an attempt from a BPF callback to
release a reference acquired by its caller. For referenced dynptrs,
unmark_stack_slots_dynptr() instead wraps release_reference() in
WARN_ON_ONCE() and then returns success.
Consequently, a callback that submits or discards its caller's ringbuf
dynptr can turn this normal verifier rejection into a kernel warning. A
kernel configured with panic_on_warn=1 panics while verifying the program.
Propagate the release_reference() error to check_helper_call(), which
already reports the invalid release and rejects the program. Add a
regression test using a non-constant bpf_loop() iteration count so the
callback is verified in a separate frame.
The upstream parent_id refactor returns release_reference() directly from
unmark_stack_slots_dynptr(). Extract that behavior here while retaining
the stable kernels' ref_obj_id representation.
Fixes: 270605317366 ("bpf: Rework process_dynptr_func")
Reported-by: Sashiko <sashiko-bot@kernel.org>
Closes: https://lore.kernel.org/r/20260829040110.D6A991F000E9@smtp.kernel.org
Assisted-by: Codex:GPT-5
Signed-off-by: Xu Yunxiang <xyx2021@mail.ustc.edu.cn>
---
kernel/bpf/verifier.c | 6 +++--
.../testing/selftests/bpf/progs/dynptr_fail.c | 22 +++++++++++++++++++
2 files changed, 26 insertions(+), 2 deletions(-)
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index dbc9cfcffcd6..65ee9afaa5ff 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -811,7 +811,7 @@ static void invalidate_slices_of_dynptr(struct bpf_verifier_env *env, int dynptr
static int unmark_stack_slots_dynptr(struct bpf_verifier_env *env, struct bpf_reg_state *reg)
{
struct bpf_func_state *state = func(env, reg);
- int spi, ref_obj_id, i;
+ int spi, ref_obj_id, i, err;
spi = dynptr_get_spi(env, reg);
if (spi < 0)
@@ -838,7 +838,9 @@ static int unmark_stack_slots_dynptr(struct bpf_verifier_env *env, struct bpf_re
* Mirror destroy_if_dynptr_stack_slot(), which already invalidates
* slices by dynptr_id.
*/
- WARN_ON_ONCE(release_reference(env, ref_obj_id));
+ err = release_reference(env, ref_obj_id);
+ if (err)
+ return err;
invalidate_slices_of_dynptr(env, state->stack[spi].spilled_ptr.id);
/* Invalidate any dynptr clones */
diff --git a/tools/testing/selftests/bpf/progs/dynptr_fail.c b/tools/testing/selftests/bpf/progs/dynptr_fail.c
index 61bd3f7c946f..52bd7f8871c5 100644
--- a/tools/testing/selftests/bpf/progs/dynptr_fail.c
+++ b/tools/testing/selftests/bpf/progs/dynptr_fail.c
@@ -134,6 +134,28 @@ int ringbuf_missing_release_callback(void *ctx)
return 0;
}
+static int release_caller_dynptr_callback_fn(__u32 index, void *data)
+{
+ struct bpf_dynptr *ptr = data;
+
+ bpf_ringbuf_submit_dynptr(ptr, 0);
+ return 0;
+}
+
+/* A callback cannot release a dynptr reference acquired by its caller. */
+SEC("?raw_tp")
+__failure __msg("reference has not been acquired before")
+int ringbuf_release_caller_dynptr_callback(void *ctx)
+{
+ struct bpf_dynptr ptr;
+
+ if (bpf_ringbuf_reserve_dynptr(&ringbuf, 8, 0, &ptr))
+ return 0;
+ bpf_loop(val & 1, release_caller_dynptr_callback_fn, &ptr, 0);
+ bpf_ringbuf_discard_dynptr(&ptr, 0);
+ return 0;
+}
+
/* Can't call bpf_ringbuf_submit/discard_dynptr on a non-initialized dynptr */
SEC("?raw_tp")
__failure __msg("arg 1 is an unacquired reference")
--
2.43.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH 6.12.y v4 3/3] bpf: Invalidate dynptr clones in every call frame on release
2026-08-31 0:15 [PATCH 6.12.y v4 0/3] bpf: fix dynptr release handling Xu Yunxiang
2026-08-31 0:15 ` [PATCH 6.12.y v4 1/3] bpf: Invalidate dynptr slices by dynptr_id on release Xu Yunxiang
2026-08-31 0:15 ` [PATCH 6.12.y v4 2/3] bpf: Propagate referenced dynptr release errors Xu Yunxiang
@ 2026-08-31 0:15 ` Xu Yunxiang
2026-08-31 11:54 ` [PATCH 6.12.y v4 0/3] bpf: fix dynptr release handling Greg KH
3 siblings, 0 replies; 8+ messages in thread
From: Xu Yunxiang @ 2026-08-31 0:15 UTC (permalink / raw)
To: stable
Cc: gregkh, bpf, ast, daniel, andrii, eddyz87, martin.lau, ameryhung,
joannelkoong, sashal
[ Upstream commit 308c7a0ae8859b34d9d90a3dff953b2d14242145 ]
This is a 6.12.y-sized equivalent, not a cherry-pick of the upstream
diff.
unmark_stack_slots_dynptr() walked clones only in the submitting
pointer's frame. A program that clones a ringbuf dynptr in a subprog
and submits the clone leaves the original STACK_DYNPTR slot active in
the caller. The caller can then pass that stale original to
bpf_dynptr_data(), even though the shared ringbuf reference has already
been released.
Walk every frame and invalidate matching STACK_DYNPTR slots. Check the
slot type before reading dynptr metadata: a partial stack overwrite can
turn a pointer spill into ordinary stack data while stale union fields
remain, and those fields must not be interpreted as a dynptr ref_obj_id.
The preceding patch makes a repeated release an ordinary verifier
rejection, but it does not prevent non-release uses of a stale dynptr.
Depends on "bpf: Invalidate dynptr slices by dynptr_id on release"
only for the slice helper call inside the new loop.
The upstream commit is Amery Hung's parent_id refactor. It cannot be
applied here (see the first patch). Deviations from 308c7a0ae8859:
keep dynptr_id / ref_obj_id; walk frames with the 6.12 STACK_DYNPTR
slot loop instead of the parent_id DFS.
Fixes: 361f129f3cc1 ("bpf: Add bpf_dynptr_clone")
Reported-by: Xu Yunxiang <xyx2021@mail.ustc.edu.cn>
Closes: https://lore.kernel.org/r/20260829033725.2365697-3-xyx2021@mail.ustc.edu.cn
Assisted-by: Pi:GLM-5.3
Signed-off-by: Xu Yunxiang <xyx2021@mail.ustc.edu.cn>
---
kernel/bpf/verifier.c | 30 +++++++--------
.../testing/selftests/bpf/progs/dynptr_fail.c | 37 +++++++++++++++++++
2 files changed, 52 insertions(+), 15 deletions(-)
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 65ee9afaa5ff..572437d48b7d 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -811,7 +811,7 @@ static void invalidate_slices_of_dynptr(struct bpf_verifier_env *env, int dynptr
static int unmark_stack_slots_dynptr(struct bpf_verifier_env *env, struct bpf_reg_state *reg)
{
struct bpf_func_state *state = func(env, reg);
- int spi, ref_obj_id, i, err;
+ int spi, ref_obj_id, i, err, frm;
spi = dynptr_get_spi(env, reg);
if (spi < 0)
@@ -843,22 +843,22 @@ static int unmark_stack_slots_dynptr(struct bpf_verifier_env *env, struct bpf_re
return err;
invalidate_slices_of_dynptr(env, state->stack[spi].spilled_ptr.id);
- /* Invalidate any dynptr clones */
- for (i = 1; i < state->allocated_stack / BPF_REG_SIZE; i++) {
- if (state->stack[i].spilled_ptr.ref_obj_id != ref_obj_id)
+ /* Invalidate any dynptr clones, including those in other frames. */
+ for (frm = 0; frm <= env->cur_state->curframe; frm++) {
+ struct bpf_func_state *f = env->cur_state->frame[frm];
+
+ if (!f)
continue;
+ for (i = 1; i < f->allocated_stack / BPF_REG_SIZE; i++) {
+ if (f->stack[i].slot_type[0] != STACK_DYNPTR)
+ continue;
+ if (f->stack[i].spilled_ptr.ref_obj_id != ref_obj_id)
+ continue;
- /* it should always be the case that if the ref obj id
- * matches then the stack slot also belongs to a
- * dynptr
- */
- if (state->stack[i].slot_type[0] != STACK_DYNPTR) {
- verbose(env, "verifier internal error: misconfigured ref_obj_id\n");
- return -EFAULT;
- }
- if (state->stack[i].spilled_ptr.dynptr.first_slot) {
- invalidate_slices_of_dynptr(env, state->stack[i].spilled_ptr.id);
- invalidate_dynptr(env, state, i);
+ if (f->stack[i].spilled_ptr.dynptr.first_slot) {
+ invalidate_slices_of_dynptr(env, f->stack[i].spilled_ptr.id);
+ invalidate_dynptr(env, f, i);
+ }
}
}
diff --git a/tools/testing/selftests/bpf/progs/dynptr_fail.c b/tools/testing/selftests/bpf/progs/dynptr_fail.c
index 52bd7f8871c5..02defdb63ddd 100644
--- a/tools/testing/selftests/bpf/progs/dynptr_fail.c
+++ b/tools/testing/selftests/bpf/progs/dynptr_fail.c
@@ -1534,6 +1534,43 @@ int clone_invalid2(struct xdp_md *xdp)
}
/* Invalidating a dynptr should invalidate its clones */
+static __noinline void submit_dynptr_clone(struct bpf_dynptr *ptr)
+{
+ struct bpf_dynptr clone;
+
+ bpf_dynptr_clone(ptr, &clone);
+ bpf_ringbuf_submit_dynptr(&clone, 0);
+}
+
+SEC("?raw_tp")
+__failure __msg("Expected an initialized dynptr")
+int clone_invalidate_cross_frame(void *ctx)
+{
+ struct bpf_dynptr ptr;
+
+ bpf_ringbuf_reserve_dynptr(&ringbuf, sizeof(struct sample), 0, &ptr);
+ submit_dynptr_clone(&ptr);
+ bpf_dynptr_data(&ptr, 0, sizeof(struct sample));
+ return 0;
+}
+
+SEC("?raw_tp")
+__success
+int clone_invalidate_cross_frame_stale_spill(void *ctx)
+{
+ struct bpf_dynptr ptr;
+ void *data;
+
+ bpf_ringbuf_reserve_dynptr(&ringbuf, sizeof(struct sample), 0, &ptr);
+ data = bpf_dynptr_data(&ptr, 0, sizeof(struct sample));
+ asm volatile("*(u64 *)(r10 - 24) = %[data];"
+ "*(u32 *)(r10 - 20) = 0x12345678;"
+ :: [data] "r"(data) : "memory");
+ /* A dead, scrubbed spill must not be mistaken for a dynptr slot. */
+ submit_dynptr_clone(&ptr);
+ return 0;
+}
+
SEC("?raw_tp")
__failure __msg("Expected an initialized dynptr as arg #2")
int clone_invalidate1(void *ctx)
--
2.43.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH 6.12.y v4 2/3] bpf: Propagate referenced dynptr release errors
2026-08-31 0:15 ` [PATCH 6.12.y v4 2/3] bpf: Propagate referenced dynptr release errors Xu Yunxiang
@ 2026-08-31 0:36 ` sashiko-bot
0 siblings, 0 replies; 8+ messages in thread
From: sashiko-bot @ 2026-08-31 0:36 UTC (permalink / raw)
To: Xu Yunxiang; +Cc: bpf
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
Pre-existing issues:
- [High] A BPF callback can trigger a kernel panic via WARN_ON_ONCE by destroying a caller-acquired iterator, bypassing verifier boundary checks.
- [High] A BPF callback performing a NULL check on a caller-acquired PTR_MAYBE_NULL reference triggers a kernel panic via WARN_ON_ONCE.
--
commit d4b9f8473b6b567b88014154d968c1bc465a559e
Author: Xu Yunxiang <xyx2021@mail.ustc.edu.cn>
bpf: Propagate referenced dynptr release errors
This commit propagates release_reference() errors in
unmark_stack_slots_dynptr() instead of wrapping them in a WARN_ON_ONCE().
This prevents kernel panics from BPF callbacks trying to release
caller-acquired dynptrs.
Closes: https://lore.kernel.org/r/20260829040110.D6A991F000E9@smtp.kernel.org
> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> index dbc9cfcffcd60..65ee9afaa5ff0 100644
> --- a/kernel/bpf/verifier.c
> +++ b/kernel/bpf/verifier.c
[ ... ]
> @@ -838,7 +838,9 @@ static int unmark_stack_slots_dynptr(struct bpf_verifier_env *env, struct bpf_re
> * Mirror destroy_if_dynptr_stack_slot(), which already invalidates
> * slices by dynptr_id.
> */
> - WARN_ON_ONCE(release_reference(env, ref_obj_id));
> + err = release_reference(env, ref_obj_id);
> + if (err)
> + return err;
> invalidate_slices_of_dynptr(env, state->stack[spi].spilled_ptr.id);
[Severity: High]
This isn't a bug introduced by this patch, but does unmark_stack_slots_iter()
have the exact same problem?
kernel/bpf/verifier.c:unmark_stack_slots_iter() {
...
if (i == 0)
WARN_ON_ONCE(release_reference(env, st->ref_obj_id));
...
}
If a caller frame initializes an iterator on its stack and passes a pointer
to this stack slot as the ctx argument to a bpf_loop callback, and the
callback calls bpf_iter_num_destroy(), could the verifier panic the kernel
via this WARN_ON_ONCE (assuming panic_on_warn=1)?
Since release_reference() would return -EINVAL for a caller's reference,
it seems it would trigger the warning.
[Severity: High]
This is a pre-existing issue, but does mark_ptr_or_null_regs() also need to
handle cross-frame reference errors instead of warning?
kernel/bpf/verifier.c:mark_ptr_or_null_regs() {
...
if (ref_obj_id && ref_obj_id == id && is_null)
/* regs[regno] is in the " == NULL" branch.
* No one could have freed the reference state before
* doing the NULL check.
*/
WARN_ON_ONCE(release_reference_state(state, id));
...
}
If a caller acquires a PTR_MAYBE_NULL reference, spills it, and passes a
pointer to it to a bpf_loop callback, and the callback reads the pointer
and performs a NULL check, would release_reference_state() refuse to release
the caller's reference and return -EINVAL?
If so, this could also trigger the WARN_ON_ONCE and panic the kernel.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260831001552.3223196-1-xyx2021@mail.ustc.edu.cn?part=2
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 6.12.y v4 0/3] bpf: fix dynptr release handling
2026-08-31 0:15 [PATCH 6.12.y v4 0/3] bpf: fix dynptr release handling Xu Yunxiang
` (2 preceding siblings ...)
2026-08-31 0:15 ` [PATCH 6.12.y v4 3/3] bpf: Invalidate dynptr clones in every call frame on release Xu Yunxiang
@ 2026-08-31 11:54 ` Greg KH
2026-08-31 15:58 ` Xu Yunxiang
3 siblings, 1 reply; 8+ messages in thread
From: Greg KH @ 2026-08-31 11:54 UTC (permalink / raw)
To: Xu Yunxiang
Cc: stable, bpf, ast, daniel, andrii, eddyz87, martin.lau, ameryhung,
joannelkoong, sashal
On Mon, Aug 31, 2026 at 08:15:49AM +0800, Xu Yunxiang wrote:
> Upstream commit 308c7a0ae885 ("bpf: Refactor object relationship
> tracking and fix dynptr UAF bug") fixes these lifetime bugs as part of
> an 11-file parent_id refactor. That refactor cannot be applied to
> 6.12.y, which still represents the relationships with dynptr_id and
> ref_obj_id.
>
> This series supplies three stable-sized equivalents using the existing
> 6.12.y representation. Patch 1 invalidates slice registers with the
> released dynptr_id. Patch 2 propagates a referenced-dynptr release
> error instead of turning a callback rejection into a kernel warning.
> Patch 3 scans every active call frame when invalidating cloned dynptr
> stack slots. They are split because the bugs have different
> introducing commits and independently testable effects.
>
> These patches are stable-specific implementations authored for the old
> verifier, not cherry-picks of Amery Hung's upstream diff. Each commit
> keeps 308c7a0ae885 as the upstream provenance for the equivalent fix
> and documents how its implementation differs.
>
> Changes in v4:
>
> - make Xu Yunxiang the author of all three stable-specific
> implementations;
> - remove the authorship, sign-off, and ack trailers copied from
> 308c7a0ae885 because those developers did not write or review these
> rewritten stable patches;
That's not ok at all! Take the original commit, and "just" backport
that, keeping the original changelog and authorship information
everywhere.
To pass this off as your own work isn't ok.
Also, you forgot the 6.18 and 7.1 trees, we can't take fixes only for
older kernels.
Please fix up all of these and do it properly.
thanks,
greg k-h
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 6.12.y v4 0/3] bpf: fix dynptr release handling
2026-08-31 11:54 ` [PATCH 6.12.y v4 0/3] bpf: fix dynptr release handling Greg KH
@ 2026-08-31 15:58 ` Xu Yunxiang
2026-08-31 20:15 ` Amery Hung
0 siblings, 1 reply; 8+ messages in thread
From: Xu Yunxiang @ 2026-08-31 15:58 UTC (permalink / raw)
To: gregkh
Cc: stable, bpf, ast, daniel, andrii, eddyz87, martin.lau, ameryhung,
joannelkoong, sashal
Hi Greg,
Thanks. I understand that I should drop the three stable-specific
implementations, preserve Amery's original authorship, changelog, and
trailer chain, and cover 6.6.y, 6.12.y, 6.18.y, and 7.1.y.
Before rerolling, could I confirm how the prerequisite chain should be
handled?
Commit 308c7a0ae885 ("bpf: Refactor object relationship tracking and
fix dynptr UAF bug") is patch 5 of its upstream series and relies on
these preceding commits:
bab08d1f70438 ("bpf: Simplify mark_stack_slot_obj_read() and callers")
b5c0a07eb2c23 ("bpf: Unify dynptr handling in the verifier")
94ac7553361f3 ("bpf: Assign reg->id when getting referenced kptr from ctx")
06d518a5583fa ("bpf: Preserve reg->id of pointer objects after null-check")
I do not find these commits in any of the four target stable branches.
In particular, the latter commits establish the reg->id semantics used
by 308c7a0ae885, so applying that commit alone would not be semantically
complete.
My plan is to prepare a five-patch backport series for each stable
branch, preserving the original authorship, changelog, and trailer chain
of every commit, and adding only branch-specific conflict notes and my
final Signed-off-by.
Is that what you had in mind?
Thanks,
Xu
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 6.12.y v4 0/3] bpf: fix dynptr release handling
2026-08-31 15:58 ` Xu Yunxiang
@ 2026-08-31 20:15 ` Amery Hung
0 siblings, 0 replies; 8+ messages in thread
From: Amery Hung @ 2026-08-31 20:15 UTC (permalink / raw)
To: Xu Yunxiang
Cc: gregkh, stable, bpf, ast, daniel, andrii, eddyz87, martin.lau,
joannelkoong, sashal
On Mon, Aug 31, 2026 at 8:58 AM Xu Yunxiang <xyx2021@mail.ustc.edu.cn> wrote:
>
> Hi Greg,
>
> Thanks. I understand that I should drop the three stable-specific
> implementations, preserve Amery's original authorship, changelog, and
> trailer chain, and cover 6.6.y, 6.12.y, 6.18.y, and 7.1.y.
>
> Before rerolling, could I confirm how the prerequisite chain should be
> handled?
>
> Commit 308c7a0ae885 ("bpf: Refactor object relationship tracking and
> fix dynptr UAF bug") is patch 5 of its upstream series and relies on
> these preceding commits:
>
> bab08d1f70438 ("bpf: Simplify mark_stack_slot_obj_read() and callers")
> b5c0a07eb2c23 ("bpf: Unify dynptr handling in the verifier")
> 94ac7553361f3 ("bpf: Assign reg->id when getting referenced kptr from ctx")
> 06d518a5583fa ("bpf: Preserve reg->id of pointer objects after null-check")
This patchset is too complex to backport properly, so let's not do
this. This is also not a security issue as dynptr is gated by CAP_BPF.
>
> I do not find these commits in any of the four target stable branches.
> In particular, the latter commits establish the reg->id semantics used
> by 308c7a0ae885, so applying that commit alone would not be semantically
> complete.
>
> My plan is to prepare a five-patch backport series for each stable
> branch, preserving the original authorship, changelog, and trailer chain
> of every commit, and adding only branch-specific conflict notes and my
> final Signed-off-by.
>
> Is that what you had in mind?
>
> Thanks,
> Xu
>
^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2026-08-31 20:15 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-31 0:15 [PATCH 6.12.y v4 0/3] bpf: fix dynptr release handling Xu Yunxiang
2026-08-31 0:15 ` [PATCH 6.12.y v4 1/3] bpf: Invalidate dynptr slices by dynptr_id on release Xu Yunxiang
2026-08-31 0:15 ` [PATCH 6.12.y v4 2/3] bpf: Propagate referenced dynptr release errors Xu Yunxiang
2026-08-31 0:36 ` sashiko-bot
2026-08-31 0:15 ` [PATCH 6.12.y v4 3/3] bpf: Invalidate dynptr clones in every call frame on release Xu Yunxiang
2026-08-31 11:54 ` [PATCH 6.12.y v4 0/3] bpf: fix dynptr release handling Greg KH
2026-08-31 15:58 ` Xu Yunxiang
2026-08-31 20:15 ` Amery Hung
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox