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)

