Skip to content

Commit 2eee6fe

Browse files
ameryhungAlexei Starovoitov
authored andcommitted
bpf: Fix dynptr ref counting to scan all call frames
When checking whether a referenced dynptr can be overwritten, destroy_if_dynptr_stack_slot only counted sibling dynptrs in the current call frame. If a clone sharing the same virtual ref parent existed in a different frame (e.g., passed to a subprog), it would not be counted, causing the verifier to incorrectly reject the overwrite with "cannot overwrite referenced dynptr". Fix by extracting the counting into dynptr_ref_cnt() which uses bpf_for_each_reg_in_vstate_mask() to scan dynptr stack slots across all call frames. Fixes: 017f5c4 ("bpf: Allow overwriting referenced dynptr when refcnt > 1") Reported-by: Eduard Zingerman <eddyz87@gmail.com> Acked-by: Eduard Zingerman <eddyz87@gmail.com> Signed-off-by: Amery Hung <ameryhung@gmail.com> Link: https://lore.kernel.org/r/20260529014936.2811085-10-ameryhung@gmail.com Signed-off-by: Alexei Starovoitov <ast@kernel.org>
1 parent bcfcb15 commit 2eee6fe

1 file changed

Lines changed: 29 additions & 23 deletions

File tree

kernel/bpf/verifier.c

Lines changed: 29 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -786,10 +786,29 @@ static void mark_reg_invalid(const struct bpf_verifier_env *env, struct bpf_reg_
786786
__mark_reg_unknown(env, reg);
787787
}
788788

789+
static int dynptr_ref_cnt(struct bpf_verifier_env *env, int v_parent_id)
790+
{
791+
struct bpf_stack_state *stack;
792+
struct bpf_func_state *state;
793+
struct bpf_reg_state *reg;
794+
int ref_cnt = 0;
795+
796+
bpf_for_each_reg_in_vstate_mask(env->cur_state, state, reg, stack, 1 << STACK_DYNPTR, ({
797+
if (!stack || stack->slot_type[0] != STACK_DYNPTR)
798+
continue;
799+
if (!stack->spilled_ptr.dynptr.first_slot)
800+
continue;
801+
if (stack->spilled_ptr.parent_id == v_parent_id)
802+
ref_cnt++;
803+
}));
804+
805+
return ref_cnt;
806+
}
807+
789808
static int destroy_if_dynptr_stack_slot(struct bpf_verifier_env *env,
790809
struct bpf_func_state *state, int spi)
791810
{
792-
int i, err = 0;
811+
int err = 0;
793812

794813
/* We always ensure that STACK_DYNPTR is never set partially,
795814
* hence just checking for slot_type[0] is enough. This is
@@ -803,28 +822,15 @@ static int destroy_if_dynptr_stack_slot(struct bpf_verifier_env *env,
803822
if (!state->stack[spi].spilled_ptr.dynptr.first_slot)
804823
spi = spi + 1;
805824

806-
if (dynptr_type_referenced(state->stack[spi].spilled_ptr.dynptr.type)) {
807-
int v_parent_id = state->stack[spi].spilled_ptr.parent_id;
808-
int ref_cnt = 0;
809-
810-
/*
811-
* A referenced dynptr can be overwritten only if there is at
812-
* least one other dynptr sharing the same virtual ref parent,
813-
* ensuring the reference can still be properly released.
814-
*/
815-
for (i = 0; i < state->allocated_stack / BPF_REG_SIZE; i++) {
816-
if (state->stack[i].slot_type[0] != STACK_DYNPTR)
817-
continue;
818-
if (!state->stack[i].spilled_ptr.dynptr.first_slot)
819-
continue;
820-
if (state->stack[i].spilled_ptr.parent_id == v_parent_id)
821-
ref_cnt++;
822-
}
823-
824-
if (ref_cnt <= 1) {
825-
verbose(env, "cannot overwrite referenced dynptr\n");
826-
return -EINVAL;
827-
}
825+
/*
826+
* A referenced dynptr can be overwritten only if there is at
827+
* least one other dynptr sharing the same virtual ref parent,
828+
* ensuring the reference can still be properly released.
829+
*/
830+
if (dynptr_type_referenced(state->stack[spi].spilled_ptr.dynptr.type) &&
831+
dynptr_ref_cnt(env, state->stack[spi].spilled_ptr.parent_id) <= 1) {
832+
verbose(env, "cannot overwrite referenced dynptr\n");
833+
return -EINVAL;
828834
}
829835

830836
/* Invalidate the dynptr and any derived slices */

0 commit comments

Comments
 (0)