/* Indicates that the argument will be released. */
        OBJ_RELEASE             = BIT(5 + BPF_BASE_TYPE_BITS),
 
-       __BPF_TYPE_LAST_FLAG    = OBJ_RELEASE,
+       /* PTR is not trusted. This is only used with PTR_TO_BTF_ID, to mark
+        * unreferenced and referenced kptr loaded from map value using a load
+        * instruction, so that they can only be dereferenced but not escape the
+        * BPF program into the kernel (i.e. cannot be passed as arguments to
+        * kfunc or bpf helpers).
+        */
+       PTR_UNTRUSTED           = BIT(6 + BPF_BASE_TYPE_BITS),
+
+       __BPF_TYPE_LAST_FLAG    = PTR_UNTRUSTED,
 };
 
 /* Max number of base types. */
 
                strncpy(prefix, "user_", 32);
        if (type & MEM_PERCPU)
                strncpy(prefix, "percpu_", 32);
+       if (type & PTR_UNTRUSTED)
+               strncpy(prefix, "untrusted_", 32);
 
        snprintf(env->type_str_buf, TYPE_STR_BUF_LEN, "%s%s%s",
                 prefix, str[base_type(type)], postfix);
                               struct bpf_reg_state *reg, u32 regno)
 {
        const char *targ_name = kernel_type_name(off_desc->kptr.btf, off_desc->kptr.btf_id);
+       int perm_flags = PTR_MAYBE_NULL;
        const char *reg_name = "";
 
-       if (base_type(reg->type) != PTR_TO_BTF_ID || type_flag(reg->type) != PTR_MAYBE_NULL)
+       /* Only unreferenced case accepts untrusted pointers */
+       if (off_desc->type == BPF_KPTR_UNREF)
+               perm_flags |= PTR_UNTRUSTED;
+
+       if (base_type(reg->type) != PTR_TO_BTF_ID || (type_flag(reg->type) & ~perm_flags))
                goto bad_type;
 
        if (!btf_is_kernel(reg->btf)) {
 bad_type:
        verbose(env, "invalid kptr access, R%d type=%s%s ", regno,
                reg_type_str(env, reg->type), reg_name);
-       verbose(env, "expected=%s%s\n", reg_type_str(env, PTR_TO_BTF_ID), targ_name);
+       verbose(env, "expected=%s%s", reg_type_str(env, PTR_TO_BTF_ID), targ_name);
+       if (off_desc->type == BPF_KPTR_UNREF)
+               verbose(env, " or %s%s\n", reg_type_str(env, PTR_TO_BTF_ID | PTR_UNTRUSTED),
+                       targ_name);
+       else
+               verbose(env, "\n");
        return -EINVAL;
 }
 
                return -EACCES;
        }
 
-       /* We cannot directly access kptr_ref */
-       if (off_desc->type == BPF_KPTR_REF) {
-               verbose(env, "accessing referenced kptr disallowed\n");
+       /* We only allow loading referenced kptr, since it will be marked as
+        * untrusted, similar to unreferenced kptr.
+        */
+       if (class != BPF_LDX && off_desc->type == BPF_KPTR_REF) {
+               verbose(env, "store to referenced kptr disallowed\n");
                return -EACCES;
        }
 
                 * value from map as PTR_TO_BTF_ID, with the correct type.
                 */
                mark_btf_ld_reg(env, cur_regs(env), value_regno, PTR_TO_BTF_ID, off_desc->kptr.btf,
-                               off_desc->kptr.btf_id, PTR_MAYBE_NULL);
+                               off_desc->kptr.btf_id, PTR_MAYBE_NULL | PTR_UNTRUSTED);
                /* For mark_ptr_or_null_reg */
                val_reg->id = ++env->id_gen;
        } else if (class == BPF_STX) {
        if (ret < 0)
                return ret;
 
+       /* If this is an untrusted pointer, all pointers formed by walking it
+        * also inherit the untrusted flag.
+        */
+       if (type_flag(reg->type) & PTR_UNTRUSTED)
+               flag |= PTR_UNTRUSTED;
+
        if (atype == BPF_READ && value_regno >= 0)
                mark_btf_ld_reg(env, regs, value_regno, ret, reg->btf, btf_id, flag);
 
                if (!ctx_access)
                        continue;
 
-               switch (env->insn_aux_data[i + delta].ptr_type) {
+               switch ((int)env->insn_aux_data[i + delta].ptr_type) {
                case PTR_TO_CTX:
                        if (!ops->convert_ctx_access)
                                continue;
                        convert_ctx_access = bpf_xdp_sock_convert_ctx_access;
                        break;
                case PTR_TO_BTF_ID:
+               case PTR_TO_BTF_ID | PTR_UNTRUSTED:
                        if (type == BPF_READ) {
                                insn->code = BPF_LDX | BPF_PROBE_MEM |
                                        BPF_SIZE((insn)->code);