Add BPF verifier support for multi-level pointer parameters and return
values in BPF trampolines. The implementation treats these parameters as
PTR_TO_MEM with read-only semantics, applying either untrusted or trusted
access patterns while honoring __nullable annotations. Runtime safety is
ensured through existing exception handling mechanisms for untrusted
memory reads, with the verifier enforcing bounds checking and null
validation.

Signed-off-by: Slava Imameev <[email protected]>
---
 include/linux/bpf.h   |  3 ++-
 kernel/bpf/btf.c      | 54 ++++++++++++++++++++++++++++++++++++-------
 kernel/bpf/verifier.c |  4 +++-
 3 files changed, 51 insertions(+), 10 deletions(-)

diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index cd9b96434904..6dd6a85cf13a 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -1052,7 +1052,8 @@ struct bpf_insn_access_aux {
                        struct btf *btf;
                        u32 btf_id;
                        u32 ref_obj_id;
-               };
+               }; /* base type PTR_TO_BTF_ID */
+               u32 mem_size; /* base type PTR_TO_MEM */
        };
        struct bpf_verifier_log *log; /* for verbose logs */
        bool is_retval; /* is accessing function return value ? */
diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c
index 7708958e3fb8..7b7cb30cdc98 100644
--- a/kernel/bpf/btf.c
+++ b/kernel/bpf/btf.c
@@ -760,6 +760,21 @@ const struct btf_type *btf_type_resolve_func_ptr(const 
struct btf *btf,
        return NULL;
 }
 
+static bool is_multilevel_ptr(const struct btf *btf, const struct btf_type *t)
+{
+       u32 depth = 0;
+
+       if (!btf_type_is_ptr(t))
+               return false;
+
+       do {
+               depth += 1;
+               t = btf_type_skip_modifiers(btf, t->type, NULL);
+       } while (btf_type_is_ptr(t) && depth < 2);
+
+       return depth > 1;
+}
+
 /* Types that act only as a source, not sink or intermediate
  * type when resolving.
  */
@@ -6790,6 +6805,7 @@ bool btf_ctx_access(int off, int size, enum 
bpf_access_type type,
        const char *tag_value;
        u32 nr_args, arg;
        int i, ret;
+       bool trusted, nullable;
 
        if (off % 8) {
                bpf_log(log, "func '%s' offset %d is not multiple of 8\n",
@@ -6927,12 +6943,8 @@ bool btf_ctx_access(int off, int size, enum 
bpf_access_type type,
                }
        }
 
-       info->reg_type = PTR_TO_BTF_ID;
-       if (prog_args_trusted(prog))
-               info->reg_type |= PTR_TRUSTED;
-
-       if (btf_param_match_suffix(btf, &args[arg], "__nullable"))
-               info->reg_type |= PTR_MAYBE_NULL;
+       trusted = prog_args_trusted(prog);
+       nullable = btf_param_match_suffix(btf, &args[arg], "__nullable");
 
        if (prog->expected_attach_type == BPF_TRACE_RAW_TP) {
                struct btf *btf = prog->aux->attach_btf;
@@ -6953,7 +6965,7 @@ bool btf_ctx_access(int off, int size, enum 
bpf_access_type type,
                        if (strcmp(tname, raw_tp_null_args[i].func))
                                continue;
                        if (raw_tp_null_args[i].mask & (0x1ULL << (arg * 4)))
-                               info->reg_type |= PTR_MAYBE_NULL;
+                               nullable = true;
                        /* Is the current arg IS_ERR? */
                        if (raw_tp_null_args[i].mask & (0x2ULL << (arg * 4)))
                                ptr_err_raw_tp = true;
@@ -6964,9 +6976,35 @@ bool btf_ctx_access(int off, int size, enum 
bpf_access_type type,
                 * argument as PTR_MAYBE_NULL.
                 */
                if (i == ARRAY_SIZE(raw_tp_null_args) && btf_is_module(btf))
-                       info->reg_type |= PTR_MAYBE_NULL;
+                       nullable = true;
        }
 
+       if (is_multilevel_ptr(btf, t)) {
+               /* If it can be IS_ERR at runtime, mark as scalar. */
+               if (ptr_err_raw_tp) {
+                       bpf_log(log, "marking func '%s' pointer arg%d as scalar 
as it may encode error",
+                               tname, arg);
+                       info->reg_type = SCALAR_VALUE;
+               } else {
+                       info->reg_type = PTR_TO_MEM | MEM_RDONLY;
+                       if (!trusted)
+                               info->reg_type |= PTR_UNTRUSTED;
+                       /* for return value be conservative and mark it 
nullable */
+                       if (nullable || arg == nr_args)
+                               info->reg_type |= PTR_MAYBE_NULL;
+                       /* this is a pointer to another pointer */
+                       info->mem_size = sizeof(void *);
+               }
+               return true;
+       }
+
+       info->reg_type = PTR_TO_BTF_ID;
+       if (trusted)
+               info->reg_type |= PTR_TRUSTED;
+
+       if (nullable)
+               info->reg_type |= PTR_MAYBE_NULL;
+
        if (tgt_prog) {
                enum bpf_prog_type tgt_type;
 
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 0162f946032f..5de56336e169 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -6311,7 +6311,7 @@ static int check_ctx_access(struct bpf_verifier_env *env, 
int insn_idx, int off,
                                        off);
                                return -EACCES;
                        }
-               } else {
+               } else if (base_type(info->reg_type) != PTR_TO_MEM) {
                        env->insn_aux_data[insn_idx].ctx_field_size = 
info->ctx_field_size;
                }
                /* remember the offset of last byte accessed in ctx */
@@ -7771,6 +7771,8 @@ static int check_mem_access(struct bpf_verifier_env *env, 
int insn_idx, u32 regn
                                        regs[value_regno].btf = info.btf;
                                        regs[value_regno].btf_id = info.btf_id;
                                        regs[value_regno].ref_obj_id = 
info.ref_obj_id;
+                               } else if (base_type(info.reg_type) == 
PTR_TO_MEM) {
+                                       regs[value_regno].mem_size = 
info.mem_size;
                                }
                        }
                        regs[value_regno].type = info.reg_type;
-- 
2.50.1 (Apple Git-155)


Reply via email to