bpf: Factor out helper check_reg_const_str()
authorSong Liu <song@kernel.org>
Tue, 7 Nov 2023 04:57:24 +0000 (20:57 -0800)
committerAlexei Starovoitov <ast@kernel.org>
Fri, 10 Nov 2023 03:07:38 +0000 (19:07 -0800)
ARG_PTR_TO_CONST_STR is used to specify constant string args for BPF
helpers. The logic that verifies a reg is ARG_PTR_TO_CONST_STR is
implemented in check_func_arg().

As we introduce kfuncs with constant string args, it is necessary to
do the same check for kfuncs (in check_kfunc_args). Factor out the logic
for ARG_PTR_TO_CONST_STR to a new check_reg_const_str() so that it can be
reused.

check_func_arg() ensures check_reg_const_str() is only called with reg of
type PTR_TO_MAP_VALUE. Add a redundent type check in check_reg_const_str()
to avoid misuse in the future. Other than this redundent check, there is
no change in behavior.

Signed-off-by: Song Liu <song@kernel.org>
Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
Acked-by: Andrii Nakryiko <andrii@kernel.org>
Acked-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>
Link: https://lore.kernel.org/bpf/20231107045725.2278852-3-song@kernel.org
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
kernel/bpf/verifier.c

index e801c50d385754896ae707cdddcf0cc3f1210dac..637f3423163372d309c30ddf039e3d7f3b9710ee 100644 (file)
@@ -8725,6 +8725,54 @@ static enum bpf_dynptr_type dynptr_get_type(struct bpf_verifier_env *env,
        return state->stack[spi].spilled_ptr.dynptr.type;
 }
 
+static int check_reg_const_str(struct bpf_verifier_env *env,
+                              struct bpf_reg_state *reg, u32 regno)
+{
+       struct bpf_map *map = reg->map_ptr;
+       int err;
+       int map_off;
+       u64 map_addr;
+       char *str_ptr;
+
+       if (reg->type != PTR_TO_MAP_VALUE)
+               return -EINVAL;
+
+       if (!bpf_map_is_rdonly(map)) {
+               verbose(env, "R%d does not point to a readonly map'\n", regno);
+               return -EACCES;
+       }
+
+       if (!tnum_is_const(reg->var_off)) {
+               verbose(env, "R%d is not a constant address'\n", regno);
+               return -EACCES;
+       }
+
+       if (!map->ops->map_direct_value_addr) {
+               verbose(env, "no direct value access support for this map type\n");
+               return -EACCES;
+       }
+
+       err = check_map_access(env, regno, reg->off,
+                              map->value_size - reg->off, false,
+                              ACCESS_HELPER);
+       if (err)
+               return err;
+
+       map_off = reg->off + reg->var_off.value;
+       err = map->ops->map_direct_value_addr(map, &map_addr, map_off);
+       if (err) {
+               verbose(env, "direct value access on string failed\n");
+               return err;
+       }
+
+       str_ptr = (char *)(long)(map_addr);
+       if (!strnchr(str_ptr + map_off, map->value_size - map_off, 0)) {
+               verbose(env, "string is not zero-terminated\n");
+               return -EINVAL;
+       }
+       return 0;
+}
+
 static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
                          struct bpf_call_arg_meta *meta,
                          const struct bpf_func_proto *fn,
@@ -8969,44 +9017,9 @@ skip_type_check:
        }
        case ARG_PTR_TO_CONST_STR:
        {
-               struct bpf_map *map = reg->map_ptr;
-               int map_off;
-               u64 map_addr;
-               char *str_ptr;
-
-               if (!bpf_map_is_rdonly(map)) {
-                       verbose(env, "R%d does not point to a readonly map'\n", regno);
-                       return -EACCES;
-               }
-
-               if (!tnum_is_const(reg->var_off)) {
-                       verbose(env, "R%d is not a constant address'\n", regno);
-                       return -EACCES;
-               }
-
-               if (!map->ops->map_direct_value_addr) {
-                       verbose(env, "no direct value access support for this map type\n");
-                       return -EACCES;
-               }
-
-               err = check_map_access(env, regno, reg->off,
-                                      map->value_size - reg->off, false,
-                                      ACCESS_HELPER);
+               err = check_reg_const_str(env, reg, regno);
                if (err)
                        return err;
-
-               map_off = reg->off + reg->var_off.value;
-               err = map->ops->map_direct_value_addr(map, &map_addr, map_off);
-               if (err) {
-                       verbose(env, "direct value access on string failed\n");
-                       return err;
-               }
-
-               str_ptr = (char *)(long)(map_addr);
-               if (!strnchr(str_ptr + map_off, map->value_size - map_off, 0)) {
-                       verbose(env, "string is not zero-terminated\n");
-                       return -EINVAL;
-               }
                break;
        }
        case ARG_PTR_TO_KPTR: