{
        struct bpf_func_state *state = func(env, reg);
        enum bpf_dynptr_type type;
-       int spi, i, id;
+       int spi, i, id, err;
 
        spi = dynptr_get_spi(env, reg);
        if (spi < 0)
        if (!is_spi_bounds_valid(state, spi, BPF_DYNPTR_NR_SLOTS))
                return -EINVAL;
 
+       /* 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,
+        * to ensure that for the following example:
+        *      [d1][d1][d2][d2]
+        * spi    3   2   1   0
+        * So marking spi = 2 should lead to destruction of both d1 and d2. In
+        * 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);
+       if (err)
+               return err;
+
        for (i = 0; i < BPF_REG_SIZE; i++) {
                state->stack[spi].slot_type[i] = STACK_DYNPTR;
                state->stack[spi - 1].slot_type[i] = STACK_DYNPTR;
 static bool is_dynptr_reg_valid_uninit(struct bpf_verifier_env *env, struct bpf_reg_state *reg)
 {
        struct bpf_func_state *state = func(env, reg);
-       int spi, i;
+       int spi;
 
        if (reg->type == CONST_PTR_TO_DYNPTR)
                return false;
        if (!is_spi_bounds_valid(state, spi, BPF_DYNPTR_NR_SLOTS))
                return true;
 
-       for (i = 0; i < BPF_REG_SIZE; i++) {
-               if (state->stack[spi].slot_type[i] == STACK_DYNPTR ||
-                   state->stack[spi - 1].slot_type[i] == STACK_DYNPTR)
-                       return false;
-       }
-
+       /* We allow overwriting existing unreferenced STACK_DYNPTR slots, see
+        * mark_stack_slots_dynptr which calls destroy_if_dynptr_stack_slot to
+        * ensure dynptr objects at the slots we are touching are completely
+        * destructed before we reinitialize them for a new one. For referenced
+        * ones, destroy_if_dynptr_stack_slot returns an error early instead of
+        * delaying it until the end where the user will get "Unreleased
+        * reference" error.
+        */
        return true;
 }