@@ -560,6 +560,8 @@ int check_kfunc_mem_size_reg(struct bpf_verifier_env *env, struct bpf_reg_state
u32 regno);
int check_mem_reg(struct bpf_verifier_env *env, struct bpf_reg_state *reg,
u32 regno, u32 mem_size);
+int check_const_str(struct bpf_verifier_env *env,
+ const struct bpf_reg_state *reg, int regno);
/* this lives here instead of in bpf.h because it needs to dereference tgt_prog */
static inline u64 bpf_trampoline_compute_key(const struct bpf_prog *tgt_prog,
@@ -6162,6 +6162,27 @@ static bool is_kfunc_arg_mem_size(const struct btf *btf,
return true;
}
+static bool btf_param_is_const_str_ptr(const struct btf *btf,
+ const struct btf_param *param)
+{
+ const struct btf_type *t;
+ bool is_const = false;
+
+ t = btf_type_by_id(btf, param->type);
+ if (!btf_type_is_ptr(t))
+ return false;
+
+ t = btf_type_by_id(btf, t->type);
+ while (btf_type_is_modifier(t)) {
+ if (BTF_INFO_KIND(t->info) == BTF_KIND_CONST)
+ is_const = true;
+ t = btf_type_by_id(btf, t->type);
+ }
+
+ return (is_const &&
+ !strcmp(btf_name_by_offset(btf, t->name_off), "char"));
+}
+
static int btf_check_func_arg_match(struct bpf_verifier_env *env,
const struct btf *btf, u32 func_id,
struct bpf_reg_state *regs,
@@ -6344,10 +6365,19 @@ static int btf_check_func_arg_match(struct bpf_verifier_env *env,
} else if (ptr_to_mem_ok) {
const struct btf_type *resolve_ret;
u32 type_size;
+ int err;
if (is_kfunc) {
bool arg_mem_size = i + 1 < nargs && is_kfunc_arg_mem_size(btf, &args[i + 1], ®s[regno + 1]);
+
+ if (btf_param_is_const_str_ptr(btf, &args[i])) {
+ err = check_const_str(env, reg, regno);
+ if (err < 0)
+ return err;
+ continue;
+ }
+
/* Permit pointer to mem, but only when argument
* type is pointer to scalar, or struct composed
* (recursively) of scalars.
@@ -5840,6 +5840,56 @@ static u32 stack_slot_get_id(struct bpf_verifier_env *env, struct bpf_reg_state
return state->stack[spi].spilled_ptr.id;
}
+int check_const_str(struct bpf_verifier_env *env,
+ const struct bpf_reg_state *reg, int regno)
+{
+ struct bpf_map *map;
+ int map_off;
+ u64 map_addr;
+ char *str_ptr;
+ int err;
+
+ if (reg->type != PTR_TO_MAP_VALUE)
+ return -EACCES;
+
+ map = reg->map_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);
+ 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)
@@ -6074,44 +6124,7 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
return err;
err = check_ptr_alignment(env, reg, 0, size, true);
} else if (arg_type == 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);
- 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;
- }
+ err = check_const_str(env, reg, regno);
} else if (arg_type == ARG_PTR_TO_KPTR) {
if (process_kptr_func(env, regno, meta))
return -EACCES;
kfuncs can handle pointers to memory when the next argument is the size of the memory that can be read and verify these as ARG_CONST_SIZE_OR_ZERO Similarly add support for string constants (const char *) and verify it similar to ARG_PTR_TO_CONST_STR. Signed-off-by: KP Singh <kpsingh@kernel.org> --- include/linux/bpf_verifier.h | 2 + kernel/bpf/btf.c | 30 ++++++++++++ kernel/bpf/verifier.c | 89 +++++++++++++++++++++--------------- 3 files changed, 83 insertions(+), 38 deletions(-)