bpf_rdonly_cast() is an identity operation at runtime and is also fixed
up to a register move by the verifier. Its verifier return-state setup
must preserve lifetime metadata that belongs to the source pointer.

On this verifier model, dynptr-slice kfunc returns carry dynptr_id, while
refcounted dynptr release invalidates aliases by ref_obj_id. Attach the
ref_obj_id to bpf_dynptr_slice() and bpf_dynptr_slice_rdwr() returns.
Propagate source id and release metadata through bpf_rdonly_cast().
For typed rdonly_cast() aliases, keep the source dynptr_id in reg id so
stack-overwrite invalidation can distinguish the originating dynptr when a
clone keeps the ref alive.

Otherwise, a cast alias can survive bpf_ringbuf_discard_dynptr() and
remain readable after the dynptr release.

Fixes: 66e3a13e7c2c ("bpf: Add bpf_dynptr_slice and bpf_dynptr_slice_rdwr")
Fixes: a35b9af4ec2c ("bpf: Add a kfunc for generic type cast")
Signed-off-by: Nuoqi Gui <[email protected]>
---
 include/linux/bpf_verifier.h |  5 +++++
 kernel/bpf/verifier.c        | 36 ++++++++++++++++++++++++++++--------
 2 files changed, 33 insertions(+), 8 deletions(-)

diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h
index 185b2aa43a420..0bc4b40e76bd0 100644
--- a/include/linux/bpf_verifier.h
+++ b/include/linux/bpf_verifier.h
@@ -1366,6 +1366,11 @@ struct bpf_kfunc_call_arg_meta {
                u32 id;
                u32 ref_obj_id;
        } initialized_dynptr;
+       struct {
+               u32 id;
+               u32 dynptr_id;
+               u32 ref_obj_id;
+       } rdonly_cast_src;
        struct {
                u8 spi;
                u8 frameno;
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index ff9b1f68ceca4..7cdf20944f31a 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -832,10 +832,16 @@ static int destroy_if_dynptr_stack_slot(struct 
bpf_verifier_env *env,
        dynptr_id = state->stack[spi].spilled_ptr.id;
        /* Invalidate any slices associated with this dynptr */
        bpf_for_each_reg_in_vstate(env->cur_state, fstate, dreg, ({
-               /* Dynptr slices are only PTR_TO_MEM_OR_NULL and PTR_TO_MEM */
-               if (dreg->type != (PTR_TO_MEM | PTR_MAYBE_NULL) && dreg->type 
!= PTR_TO_MEM)
+               if (base_type(dreg->type) == PTR_TO_MEM &&
+                   dreg->dynptr_id == dynptr_id) {
+                       mark_reg_invalid(env, dreg);
                        continue;
-               if (dreg->dynptr_id == dynptr_id)
+               }
+
+               /* Typed bpf_rdonly_cast() aliases keep dynptr_id in reg id. */
+               if (base_type(dreg->type) == PTR_TO_BTF_ID &&
+                   (dreg->type & PTR_UNTRUSTED) &&
+                   dreg->id == dynptr_id)
                        mark_reg_invalid(env, dreg);
        }));
 
@@ -12085,8 +12091,17 @@ static int check_kfunc_args(struct bpf_verifier_env 
*env, struct bpf_kfunc_call_
                        continue;
                }
 
-               if (is_kfunc_arg_ignore(btf, &args[i]) || 
is_kfunc_arg_implicit(meta, i))
+               if (is_kfunc_arg_ignore(btf, &args[i]) || 
is_kfunc_arg_implicit(meta, i)) {
+                       if (meta->func_id == 
special_kfunc_list[KF_bpf_rdonly_cast] &&
+                           i == 0 && is_spillable_regtype(reg->type)) {
+                               meta->rdonly_cast_src.id = reg->id;
+                               if (base_type(reg->type) == PTR_TO_MEM)
+                                       meta->rdonly_cast_src.dynptr_id = 
reg->dynptr_id;
+                               meta->rdonly_cast_src.ref_obj_id = 
reg->ref_obj_id;
+                       }
+
                        continue;
+               }
 
                t = btf_type_skip_modifiers(btf, args[i].type, NULL);
 
@@ -12935,10 +12950,17 @@ static int check_special_kfunc(struct 
bpf_verifier_env *env, struct bpf_kfunc_ca
                        regs[BPF_REG_0].type = PTR_TO_BTF_ID | PTR_UNTRUSTED;
                        regs[BPF_REG_0].btf = desc_btf;
                        regs[BPF_REG_0].btf_id = meta->arg_constant.value;
+                       regs[BPF_REG_0].id = meta->rdonly_cast_src.id;
+                       if (!regs[BPF_REG_0].id)
+                               regs[BPF_REG_0].id = 
meta->rdonly_cast_src.dynptr_id;
+                       regs[BPF_REG_0].ref_obj_id = 
meta->rdonly_cast_src.ref_obj_id;
                } else if (btf_type_is_void(ret_t)) {
                        mark_reg_known_zero(env, regs, BPF_REG_0);
                        regs[BPF_REG_0].type = PTR_TO_MEM | MEM_RDONLY | 
PTR_UNTRUSTED;
                        regs[BPF_REG_0].mem_size = 0;
+                       regs[BPF_REG_0].id = meta->rdonly_cast_src.id;
+                       regs[BPF_REG_0].dynptr_id = 
meta->rdonly_cast_src.dynptr_id;
+                       regs[BPF_REG_0].ref_obj_id = 
meta->rdonly_cast_src.ref_obj_id;
                } else {
                        verbose(env,
                                "kfunc bpf_rdonly_cast type ID argument must be 
of a struct or void\n");
@@ -12975,11 +12997,9 @@ static int check_special_kfunc(struct bpf_verifier_env 
*env, struct bpf_kfunc_ca
                        return -EFAULT;
                }
                regs[BPF_REG_0].dynptr_id = meta->initialized_dynptr.id;
+               regs[BPF_REG_0].ref_obj_id = 
meta->initialized_dynptr.ref_obj_id;
 
-               /* we don't need to set BPF_REG_0's ref obj id
-                * because packet slices are not refcounted (see
-                * dynptr_type_refcounted)
-                */
+               /* ref_obj_id is zero for non-refcounted packet slices. */
        } else {
                return 0;
        }

-- 
2.34.1


Reply via email to