]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
bpf: Refactor to handle memory and size together
authorYonghong Song <yonghong.song@linux.dev>
Thu, 23 Apr 2026 03:34:51 +0000 (20:34 -0700)
committerAlexei Starovoitov <ast@kernel.org>
Thu, 23 Apr 2026 14:54:50 +0000 (07:54 -0700)
Similar to the previous patch, try to pass bpf_reg_state from caller
to callee. Both mem_reg and size_reg are passed to helper functions.
This is important for stack arguments as they may be beyond registers 1-5.

Acked-by: Puranjay Mohan <puranjay@kernel.org>
Acked-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
Signed-off-by: Yonghong Song <yonghong.song@linux.dev>
Link: https://lore.kernel.org/r/20260423033451.2539065-1-yonghong.song@linux.dev
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
kernel/bpf/verifier.c

index debae6133e4c28fd6f152dbdad84d849c659992f..1b9a4918fa576fd97984cee89976338df7ea2d83 100644 (file)
@@ -6934,12 +6934,12 @@ mark:
        return 0;
 }
 
-static int check_helper_mem_access(struct bpf_verifier_env *env, int regno,
+static int check_helper_mem_access(struct bpf_verifier_env *env, struct bpf_reg_state *reg, int regno,
                                   int access_size, enum bpf_access_type access_type,
                                   bool zero_size_allowed,
                                   struct bpf_call_arg_meta *meta)
 {
-       struct bpf_reg_state *regs = cur_regs(env), *reg = &regs[regno];
+       struct bpf_reg_state *regs = cur_regs(env);
        u32 *max_access;
 
        switch (base_type(reg->type)) {
@@ -7022,12 +7022,12 @@ static int check_helper_mem_access(struct bpf_verifier_env *env, int regno,
 /* verify arguments to helpers or kfuncs consisting of a pointer and an access
  * size.
  *
- * @regno is the register containing the access size. regno-1 is the register
- * containing the pointer.
+ * @mem_reg contains the pointer, @size_reg contains the access size.
  */
 static int check_mem_size_reg(struct bpf_verifier_env *env,
-                             struct bpf_reg_state *reg, u32 regno,
-                             enum bpf_access_type access_type,
+                             struct bpf_reg_state *mem_reg,
+                             struct bpf_reg_state *size_reg, u32 mem_regno,
+                             u32 size_regno, enum bpf_access_type access_type,
                              bool zero_size_allowed,
                              struct bpf_call_arg_meta *meta)
 {
@@ -7041,37 +7041,37 @@ static int check_mem_size_reg(struct bpf_verifier_env *env,
         * out. Only upper bounds can be learned because retval is an
         * int type and negative retvals are allowed.
         */
-       meta->msize_max_value = reg->umax_value;
+       meta->msize_max_value = size_reg->umax_value;
 
        /* The register is SCALAR_VALUE; the access check happens using
         * its boundaries. For unprivileged variable accesses, disable
         * raw mode so that the program is required to initialize all
         * the memory that the helper could just partially fill up.
         */
-       if (!tnum_is_const(reg->var_off))
+       if (!tnum_is_const(size_reg->var_off))
                meta = NULL;
 
-       if (reg->smin_value < 0) {
+       if (size_reg->smin_value < 0) {
                verbose(env, "R%d min value is negative, either use unsigned or 'var &= const'\n",
-                       regno);
+                       size_regno);
                return -EACCES;
        }
 
-       if (reg->umin_value == 0 && !zero_size_allowed) {
+       if (size_reg->umin_value == 0 && !zero_size_allowed) {
                verbose(env, "R%d invalid zero-sized read: u64=[%lld,%lld]\n",
-                       regno, reg->umin_value, reg->umax_value);
+                       size_regno, size_reg->umin_value, size_reg->umax_value);
                return -EACCES;
        }
 
-       if (reg->umax_value >= BPF_MAX_VAR_SIZ) {
+       if (size_reg->umax_value >= BPF_MAX_VAR_SIZ) {
                verbose(env, "R%d unbounded memory access, use 'var &= const' or 'if (var < const)'\n",
-                       regno);
+                       size_regno);
                return -EACCES;
        }
-       err = check_helper_mem_access(env, regno - 1, reg->umax_value,
+       err = check_helper_mem_access(env, mem_reg, mem_regno, size_reg->umax_value,
                                      access_type, zero_size_allowed, meta);
        if (!err)
-               err = mark_chain_precision(env, regno);
+               err = mark_chain_precision(env, size_regno);
        return err;
 }
 
@@ -7096,8 +7096,8 @@ static int check_mem_reg(struct bpf_verifier_env *env, struct bpf_reg_state *reg
 
        int size = base_type(reg->type) == PTR_TO_STACK ? -(int)mem_size : mem_size;
 
-       err = check_helper_mem_access(env, regno, size, BPF_READ, true, NULL);
-       err = err ?: check_helper_mem_access(env, regno, size, BPF_WRITE, true, NULL);
+       err = check_helper_mem_access(env, reg, regno, size, BPF_READ, true, NULL);
+       err = err ?: check_helper_mem_access(env, reg, regno, size, BPF_WRITE, true, NULL);
 
        if (may_be_null)
                *reg = saved_reg;
@@ -7105,10 +7105,9 @@ static int check_mem_reg(struct bpf_verifier_env *env, struct bpf_reg_state *reg
        return err;
 }
 
-static int check_kfunc_mem_size_reg(struct bpf_verifier_env *env, struct bpf_reg_state *reg,
-                                   u32 regno)
+static int check_kfunc_mem_size_reg(struct bpf_verifier_env *env, struct bpf_reg_state *mem_reg,
+                                   struct bpf_reg_state *size_reg, u32 mem_regno, u32 size_regno)
 {
-       struct bpf_reg_state *mem_reg = &cur_regs(env)[regno - 1];
        bool may_be_null = type_may_be_null(mem_reg->type);
        struct bpf_reg_state saved_reg;
        struct bpf_call_arg_meta meta;
@@ -7121,8 +7120,8 @@ static int check_kfunc_mem_size_reg(struct bpf_verifier_env *env, struct bpf_reg
                mark_ptr_not_null_reg(mem_reg);
        }
 
-       err = check_mem_size_reg(env, reg, regno, BPF_READ, true, &meta);
-       err = err ?: check_mem_size_reg(env, reg, regno, BPF_WRITE, true, &meta);
+       err = check_mem_size_reg(env, mem_reg, size_reg, mem_regno, size_regno, BPF_READ, true, &meta);
+       err = err ?: check_mem_size_reg(env, mem_reg, size_reg, mem_regno, size_regno, BPF_WRITE, true, &meta);
 
        if (may_be_null)
                *mem_reg = saved_reg;
@@ -8566,7 +8565,7 @@ skip_type_check:
                        return -EFAULT;
                }
                key_size = meta->map.ptr->key_size;
-               err = check_helper_mem_access(env, regno, key_size, BPF_READ, false, NULL);
+               err = check_helper_mem_access(env, reg, regno, key_size, BPF_READ, false, NULL);
                if (err)
                        return err;
                if (can_elide_value_nullness(meta->map.ptr->map_type)) {
@@ -8593,7 +8592,7 @@ skip_type_check:
                        return -EFAULT;
                }
                meta->raw_mode = arg_type & MEM_UNINIT;
-               err = check_helper_mem_access(env, regno, meta->map.ptr->value_size,
+               err = check_helper_mem_access(env, reg, regno, meta->map.ptr->value_size,
                                              arg_type & MEM_WRITE ? BPF_WRITE : BPF_READ,
                                              false, meta);
                break;
@@ -8637,7 +8636,7 @@ skip_type_check:
                 */
                meta->raw_mode = arg_type & MEM_UNINIT;
                if (arg_type & MEM_FIXED_SIZE) {
-                       err = check_helper_mem_access(env, regno, fn->arg_size[arg],
+                       err = check_helper_mem_access(env, reg, regno, fn->arg_size[arg],
                                                      arg_type & MEM_WRITE ? BPF_WRITE : BPF_READ,
                                                      false, meta);
                        if (err)
@@ -8647,13 +8646,13 @@ skip_type_check:
                }
                break;
        case ARG_CONST_SIZE:
-               err = check_mem_size_reg(env, reg, regno,
+               err = check_mem_size_reg(env, reg_state(env, regno - 1), reg, regno - 1, regno,
                                         fn->arg_type[arg - 1] & MEM_WRITE ?
                                         BPF_WRITE : BPF_READ,
                                         false, meta);
                break;
        case ARG_CONST_SIZE_OR_ZERO:
-               err = check_mem_size_reg(env, reg, regno,
+               err = check_mem_size_reg(env, reg_state(env, regno - 1), reg, regno - 1, regno,
                                         fn->arg_type[arg - 1] & MEM_WRITE ?
                                         BPF_WRITE : BPF_READ,
                                         true, meta);
@@ -12384,7 +12383,7 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_
                        const struct btf_param *size_arg = &args[i + 1];
 
                        if (!bpf_register_is_null(buff_reg) || !is_kfunc_arg_nullable(meta->btf, buff_arg)) {
-                               ret = check_kfunc_mem_size_reg(env, size_reg, regno + 1);
+                               ret = check_kfunc_mem_size_reg(env, buff_reg, size_reg, regno, regno + 1);
                                if (ret < 0) {
                                        verbose(env, "arg#%d arg#%d memory, len pair leads to invalid memory access\n", i, i + 1);
                                        return ret;