Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions include/linux/btf.h
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,7 @@ struct btf *btf_base_btf(const struct btf *btf);
bool btf_type_is_i32(const struct btf_type *t);
bool btf_type_is_i64(const struct btf_type *t);
bool btf_type_is_primitive(const struct btf_type *t);
bool btf_type_is_const_char_ptr(const struct btf *btf, const struct btf_type *t);
bool btf_member_is_reg_int(const struct btf *btf, const struct btf_type *s,
const struct btf_member *m,
u32 expected_offset, u32 expected_size);
Expand Down
33 changes: 33 additions & 0 deletions kernel/bpf/btf.c
Original file line number Diff line number Diff line change
Expand Up @@ -897,6 +897,25 @@ bool btf_type_is_primitive(const struct btf_type *t)
btf_is_any_enum(t);
}

bool btf_type_is_const_char_ptr(const struct btf *btf, const struct btf_type *t)
{
const char *tname;

/* The type chain has to be PTR->CONST->CHAR */
if (BTF_INFO_KIND(t->info) != BTF_KIND_PTR)
return false;

t = btf_type_by_id(btf, t->type);
if (BTF_INFO_KIND(t->info) != BTF_KIND_CONST)
return false;

t = btf_type_by_id(btf, t->type);
tname = btf_name_by_offset(btf, t->name_off);
if (tname && strcmp(tname, "char") == 0)
return true;
return false;
}

/*
* Check that given struct member is a regular int with expected
* offset and size.
Expand Down Expand Up @@ -6746,6 +6765,20 @@ bool btf_ctx_access(int off, int size, enum bpf_access_type type,
/* Default prog with MAX_BPF_FUNC_REG_ARGS args */
return true;
t = btf_type_by_id(btf, args[arg].type);

/*
* For const string, we need to match "const char *"
* exactly. Therefore, do the check before the skipping
* modifiers.
*/
if (btf_type_is_const_char_ptr(btf, t)) {
info->reg_type = PTR_TO_BTF_ID;
if (prog_args_trusted(prog))
info->reg_type |= PTR_TRUSTED;
info->btf = btf;
info->btf_id = args[arg].type;
return true;
}
}

/* skip modifiers */
Expand Down
51 changes: 39 additions & 12 deletions kernel/bpf/verifier.c
Original file line number Diff line number Diff line change
Expand Up @@ -9598,18 +9598,19 @@ 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)
/*
* Check for const string saved in a bpf map. The caller is responsible
* to check reg->type == PTR_TO_MAP_VALUE.
*/
static int check_reg_const_str_in_map(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;
Expand Down Expand Up @@ -9646,6 +9647,26 @@ static int check_reg_const_str(struct bpf_verifier_env *env,
return 0;
}

/* Check for const string passed in as input to the bpf program. */
static int check_reg_const_str_arg(struct bpf_reg_state *reg)
{
const struct btf *btf;
const struct btf_type *t;
const char *tname;

if (base_type(reg->type) != PTR_TO_BTF_ID)
return -EINVAL;

btf = reg->btf;
t = btf_type_by_id(btf, reg->btf_id);
if (!t)
return -EINVAL;

if (btf_type_is_const_char_ptr(btf, t))
return 0;
return -EINVAL;
}

/* Returns constant key value in `value` if possible, else negative error */
static int get_constant_map_key(struct bpf_verifier_env *env,
struct bpf_reg_state *key,
Expand Down Expand Up @@ -9964,7 +9985,9 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
break;
case ARG_PTR_TO_CONST_STR:
{
err = check_reg_const_str(env, reg, regno);
if (reg->type != PTR_TO_MAP_VALUE)
return -EINVAL;
err = check_reg_const_str_in_map(env, reg, regno);
if (err)
return err;
break;
Expand Down Expand Up @@ -13626,13 +13649,17 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_
meta->arg_btf_id = reg->btf_id;
break;
case KF_ARG_PTR_TO_CONST_STR:
if (reg->type != PTR_TO_MAP_VALUE) {
verbose(env, "arg#%d doesn't point to a const string\n", i);
return -EINVAL;
if (reg->type == PTR_TO_MAP_VALUE) {
ret = check_reg_const_str_in_map(env, reg, regno);
if (ret)
return ret;
} else {
ret = check_reg_const_str_arg(reg);
if (ret) {
verbose(env, "arg#%d doesn't point to a const string\n", i);
return ret;
}
}
ret = check_reg_const_str(env, reg, regno);
if (ret)
return ret;
break;
case KF_ARG_PTR_TO_WORKQUEUE:
if (reg->type != PTR_TO_MAP_VALUE) {
Expand Down
Loading