Commit 63260df1 authored by Alexei Starovoitov's avatar Alexei Starovoitov Committed by Andrii Nakryiko

bpf: Refactor btf_nested_type_is_trusted().

btf_nested_type_is_trusted() tries to find a struct member at corresponding offset.
It works for flat structures and falls apart in more complex structs with nested structs.
The offset->member search is already performed by btf_struct_walk() including nested structs.
Reuse this work and pass {field name, field btf id} into btf_nested_type_is_trusted()
instead of offset to make BTF_TYPE_SAFE*() logic more robust.
Signed-off-by: default avatarAlexei Starovoitov <ast@kernel.org>
Signed-off-by: default avatarAndrii Nakryiko <andrii@kernel.org>
Acked-by: default avatarDavid Vernet <void@manifault.com>
Link: https://lore.kernel.org/bpf/20230404045029.82870-4-alexei.starovoitov@gmail.com
parent b7e852a9
...@@ -2263,7 +2263,7 @@ static inline bool bpf_tracing_btf_ctx_access(int off, int size, ...@@ -2263,7 +2263,7 @@ static inline bool bpf_tracing_btf_ctx_access(int off, int size,
int btf_struct_access(struct bpf_verifier_log *log, int btf_struct_access(struct bpf_verifier_log *log,
const struct bpf_reg_state *reg, const struct bpf_reg_state *reg,
int off, int size, enum bpf_access_type atype, int off, int size, enum bpf_access_type atype,
u32 *next_btf_id, enum bpf_type_flag *flag); u32 *next_btf_id, enum bpf_type_flag *flag, const char **field_name);
bool btf_struct_ids_match(struct bpf_verifier_log *log, bool btf_struct_ids_match(struct bpf_verifier_log *log,
const struct btf *btf, u32 id, int off, const struct btf *btf, u32 id, int off,
const struct btf *need_btf, u32 need_type_id, const struct btf *need_btf, u32 need_type_id,
...@@ -2302,7 +2302,7 @@ struct bpf_core_ctx { ...@@ -2302,7 +2302,7 @@ struct bpf_core_ctx {
bool btf_nested_type_is_trusted(struct bpf_verifier_log *log, bool btf_nested_type_is_trusted(struct bpf_verifier_log *log,
const struct bpf_reg_state *reg, const struct bpf_reg_state *reg,
int off, const char *suffix); const char *field_name, u32 btf_id, const char *suffix);
bool btf_type_ids_nocast_alias(struct bpf_verifier_log *log, bool btf_type_ids_nocast_alias(struct bpf_verifier_log *log,
const struct btf *reg_btf, u32 reg_id, const struct btf *reg_btf, u32 reg_id,
...@@ -2517,7 +2517,8 @@ static inline struct bpf_prog *bpf_prog_by_id(u32 id) ...@@ -2517,7 +2517,8 @@ static inline struct bpf_prog *bpf_prog_by_id(u32 id)
static inline int btf_struct_access(struct bpf_verifier_log *log, static inline int btf_struct_access(struct bpf_verifier_log *log,
const struct bpf_reg_state *reg, const struct bpf_reg_state *reg,
int off, int size, enum bpf_access_type atype, int off, int size, enum bpf_access_type atype,
u32 *next_btf_id, enum bpf_type_flag *flag) u32 *next_btf_id, enum bpf_type_flag *flag,
const char **field_name)
{ {
return -EACCES; return -EACCES;
} }
......
...@@ -6166,7 +6166,8 @@ enum bpf_struct_walk_result { ...@@ -6166,7 +6166,8 @@ enum bpf_struct_walk_result {
static int btf_struct_walk(struct bpf_verifier_log *log, const struct btf *btf, static int btf_struct_walk(struct bpf_verifier_log *log, const struct btf *btf,
const struct btf_type *t, int off, int size, const struct btf_type *t, int off, int size,
u32 *next_btf_id, enum bpf_type_flag *flag) u32 *next_btf_id, enum bpf_type_flag *flag,
const char **field_name)
{ {
u32 i, moff, mtrue_end, msize = 0, total_nelems = 0; u32 i, moff, mtrue_end, msize = 0, total_nelems = 0;
const struct btf_type *mtype, *elem_type = NULL; const struct btf_type *mtype, *elem_type = NULL;
...@@ -6395,6 +6396,8 @@ static int btf_struct_walk(struct bpf_verifier_log *log, const struct btf *btf, ...@@ -6395,6 +6396,8 @@ static int btf_struct_walk(struct bpf_verifier_log *log, const struct btf *btf,
if (btf_type_is_struct(stype)) { if (btf_type_is_struct(stype)) {
*next_btf_id = id; *next_btf_id = id;
*flag |= tmp_flag; *flag |= tmp_flag;
if (field_name)
*field_name = mname;
return WALK_PTR; return WALK_PTR;
} }
} }
...@@ -6421,7 +6424,8 @@ static int btf_struct_walk(struct bpf_verifier_log *log, const struct btf *btf, ...@@ -6421,7 +6424,8 @@ static int btf_struct_walk(struct bpf_verifier_log *log, const struct btf *btf,
int btf_struct_access(struct bpf_verifier_log *log, int btf_struct_access(struct bpf_verifier_log *log,
const struct bpf_reg_state *reg, const struct bpf_reg_state *reg,
int off, int size, enum bpf_access_type atype __maybe_unused, int off, int size, enum bpf_access_type atype __maybe_unused,
u32 *next_btf_id, enum bpf_type_flag *flag) u32 *next_btf_id, enum bpf_type_flag *flag,
const char **field_name)
{ {
const struct btf *btf = reg->btf; const struct btf *btf = reg->btf;
enum bpf_type_flag tmp_flag = 0; enum bpf_type_flag tmp_flag = 0;
...@@ -6453,7 +6457,7 @@ int btf_struct_access(struct bpf_verifier_log *log, ...@@ -6453,7 +6457,7 @@ int btf_struct_access(struct bpf_verifier_log *log,
t = btf_type_by_id(btf, id); t = btf_type_by_id(btf, id);
do { do {
err = btf_struct_walk(log, btf, t, off, size, &id, &tmp_flag); err = btf_struct_walk(log, btf, t, off, size, &id, &tmp_flag, field_name);
switch (err) { switch (err) {
case WALK_PTR: case WALK_PTR:
...@@ -6528,7 +6532,7 @@ bool btf_struct_ids_match(struct bpf_verifier_log *log, ...@@ -6528,7 +6532,7 @@ bool btf_struct_ids_match(struct bpf_verifier_log *log,
type = btf_type_by_id(btf, id); type = btf_type_by_id(btf, id);
if (!type) if (!type)
return false; return false;
err = btf_struct_walk(log, btf, type, off, 1, &id, &flag); err = btf_struct_walk(log, btf, type, off, 1, &id, &flag, NULL);
if (err != WALK_STRUCT) if (err != WALK_STRUCT)
return false; return false;
...@@ -8488,16 +8492,15 @@ int bpf_core_apply(struct bpf_core_ctx *ctx, const struct bpf_core_relo *relo, ...@@ -8488,16 +8492,15 @@ int bpf_core_apply(struct bpf_core_ctx *ctx, const struct bpf_core_relo *relo,
bool btf_nested_type_is_trusted(struct bpf_verifier_log *log, bool btf_nested_type_is_trusted(struct bpf_verifier_log *log,
const struct bpf_reg_state *reg, const struct bpf_reg_state *reg,
int off, const char *suffix) const char *field_name, u32 btf_id, const char *suffix)
{ {
struct btf *btf = reg->btf; struct btf *btf = reg->btf;
const struct btf_type *walk_type, *safe_type; const struct btf_type *walk_type, *safe_type;
const char *tname; const char *tname;
char safe_tname[64]; char safe_tname[64];
long ret, safe_id; long ret, safe_id;
const struct btf_member *member, *m_walk = NULL; const struct btf_member *member;
u32 i; u32 i;
const char *walk_name;
walk_type = btf_type_by_id(btf, reg->btf_id); walk_type = btf_type_by_id(btf, reg->btf_id);
if (!walk_type) if (!walk_type)
...@@ -8517,30 +8520,17 @@ bool btf_nested_type_is_trusted(struct bpf_verifier_log *log, ...@@ -8517,30 +8520,17 @@ bool btf_nested_type_is_trusted(struct bpf_verifier_log *log,
if (!safe_type) if (!safe_type)
return false; return false;
for_each_member(i, walk_type, member) {
u32 moff;
/* We're looking for the PTR_TO_BTF_ID member in the struct
* type we're walking which matches the specified offset.
* Below, we'll iterate over the fields in the safe variant of
* the struct and see if any of them has a matching type /
* name.
*/
moff = __btf_member_bit_offset(walk_type, member) / 8;
if (off == moff) {
m_walk = member;
break;
}
}
if (m_walk == NULL)
return false;
walk_name = __btf_name_by_offset(btf, m_walk->name_off);
for_each_member(i, safe_type, member) { for_each_member(i, safe_type, member) {
const char *m_name = __btf_name_by_offset(btf, member->name_off); const char *m_name = __btf_name_by_offset(btf, member->name_off);
const struct btf_type *mtype = btf_type_by_id(btf, member->type);
u32 id;
if (!btf_type_is_ptr(mtype))
continue;
btf_type_skip_modifiers(btf, mtype->type, &id);
/* If we match on both type and name, the field is considered trusted. */ /* If we match on both type and name, the field is considered trusted. */
if (m_walk->type == member->type && !strcmp(walk_name, m_name)) if (btf_id == id && !strcmp(field_name, m_name))
return true; return true;
} }
......
...@@ -5400,12 +5400,12 @@ BTF_TYPE_SAFE_RCU(struct css_set) { ...@@ -5400,12 +5400,12 @@ BTF_TYPE_SAFE_RCU(struct css_set) {
/* full trusted: these fields are trusted even outside of RCU CS and never NULL */ /* full trusted: these fields are trusted even outside of RCU CS and never NULL */
BTF_TYPE_SAFE_TRUSTED(struct bpf_iter_meta) { BTF_TYPE_SAFE_TRUSTED(struct bpf_iter_meta) {
__bpf_md_ptr(struct seq_file *, seq); struct seq_file *seq;
}; };
BTF_TYPE_SAFE_TRUSTED(struct bpf_iter__task) { BTF_TYPE_SAFE_TRUSTED(struct bpf_iter__task) {
__bpf_md_ptr(struct bpf_iter_meta *, meta); struct bpf_iter_meta *meta;
__bpf_md_ptr(struct task_struct *, task); struct task_struct *task;
}; };
BTF_TYPE_SAFE_TRUSTED(struct linux_binprm) { BTF_TYPE_SAFE_TRUSTED(struct linux_binprm) {
...@@ -5427,17 +5427,17 @@ BTF_TYPE_SAFE_TRUSTED(struct socket) { ...@@ -5427,17 +5427,17 @@ BTF_TYPE_SAFE_TRUSTED(struct socket) {
static bool type_is_rcu(struct bpf_verifier_env *env, static bool type_is_rcu(struct bpf_verifier_env *env,
struct bpf_reg_state *reg, struct bpf_reg_state *reg,
int off) const char *field_name, u32 btf_id)
{ {
BTF_TYPE_EMIT(BTF_TYPE_SAFE_RCU(struct task_struct)); BTF_TYPE_EMIT(BTF_TYPE_SAFE_RCU(struct task_struct));
BTF_TYPE_EMIT(BTF_TYPE_SAFE_RCU(struct css_set)); BTF_TYPE_EMIT(BTF_TYPE_SAFE_RCU(struct css_set));
return btf_nested_type_is_trusted(&env->log, reg, off, "__safe_rcu"); return btf_nested_type_is_trusted(&env->log, reg, field_name, btf_id, "__safe_rcu");
} }
static bool type_is_trusted(struct bpf_verifier_env *env, static bool type_is_trusted(struct bpf_verifier_env *env,
struct bpf_reg_state *reg, struct bpf_reg_state *reg,
int off) const char *field_name, u32 btf_id)
{ {
BTF_TYPE_EMIT(BTF_TYPE_SAFE_TRUSTED(struct bpf_iter_meta)); BTF_TYPE_EMIT(BTF_TYPE_SAFE_TRUSTED(struct bpf_iter_meta));
BTF_TYPE_EMIT(BTF_TYPE_SAFE_TRUSTED(struct bpf_iter__task)); BTF_TYPE_EMIT(BTF_TYPE_SAFE_TRUSTED(struct bpf_iter__task));
...@@ -5446,7 +5446,7 @@ static bool type_is_trusted(struct bpf_verifier_env *env, ...@@ -5446,7 +5446,7 @@ static bool type_is_trusted(struct bpf_verifier_env *env,
BTF_TYPE_EMIT(BTF_TYPE_SAFE_TRUSTED(struct dentry)); BTF_TYPE_EMIT(BTF_TYPE_SAFE_TRUSTED(struct dentry));
BTF_TYPE_EMIT(BTF_TYPE_SAFE_TRUSTED(struct socket)); BTF_TYPE_EMIT(BTF_TYPE_SAFE_TRUSTED(struct socket));
return btf_nested_type_is_trusted(&env->log, reg, off, "__safe_trusted"); return btf_nested_type_is_trusted(&env->log, reg, field_name, btf_id, "__safe_trusted");
} }
static int check_ptr_to_btf_access(struct bpf_verifier_env *env, static int check_ptr_to_btf_access(struct bpf_verifier_env *env,
...@@ -5458,6 +5458,7 @@ static int check_ptr_to_btf_access(struct bpf_verifier_env *env, ...@@ -5458,6 +5458,7 @@ static int check_ptr_to_btf_access(struct bpf_verifier_env *env,
struct bpf_reg_state *reg = regs + regno; struct bpf_reg_state *reg = regs + regno;
const struct btf_type *t = btf_type_by_id(reg->btf, reg->btf_id); const struct btf_type *t = btf_type_by_id(reg->btf, reg->btf_id);
const char *tname = btf_name_by_offset(reg->btf, t->name_off); const char *tname = btf_name_by_offset(reg->btf, t->name_off);
const char *field_name = NULL;
enum bpf_type_flag flag = 0; enum bpf_type_flag flag = 0;
u32 btf_id = 0; u32 btf_id = 0;
int ret; int ret;
...@@ -5526,7 +5527,7 @@ static int check_ptr_to_btf_access(struct bpf_verifier_env *env, ...@@ -5526,7 +5527,7 @@ static int check_ptr_to_btf_access(struct bpf_verifier_env *env,
return -EFAULT; return -EFAULT;
} }
ret = btf_struct_access(&env->log, reg, off, size, atype, &btf_id, &flag); ret = btf_struct_access(&env->log, reg, off, size, atype, &btf_id, &flag, &field_name);
} }
if (ret < 0) if (ret < 0)
...@@ -5554,10 +5555,10 @@ static int check_ptr_to_btf_access(struct bpf_verifier_env *env, ...@@ -5554,10 +5555,10 @@ static int check_ptr_to_btf_access(struct bpf_verifier_env *env,
* A regular RCU-protected pointer with __rcu tag can also be deemed * A regular RCU-protected pointer with __rcu tag can also be deemed
* trusted if we are in an RCU CS. Such pointer can be NULL. * trusted if we are in an RCU CS. Such pointer can be NULL.
*/ */
if (type_is_trusted(env, reg, off)) { if (type_is_trusted(env, reg, field_name, btf_id)) {
flag |= PTR_TRUSTED; flag |= PTR_TRUSTED;
} else if (in_rcu_cs(env) && !type_may_be_null(reg->type)) { } else if (in_rcu_cs(env) && !type_may_be_null(reg->type)) {
if (type_is_rcu(env, reg, off)) { if (type_is_rcu(env, reg, field_name, btf_id)) {
/* ignore __rcu tag and mark it MEM_RCU */ /* ignore __rcu tag and mark it MEM_RCU */
flag |= MEM_RCU; flag |= MEM_RCU;
} else if (flag & MEM_RCU) { } else if (flag & MEM_RCU) {
...@@ -5640,7 +5641,7 @@ static int check_ptr_to_map_access(struct bpf_verifier_env *env, ...@@ -5640,7 +5641,7 @@ static int check_ptr_to_map_access(struct bpf_verifier_env *env,
/* Simulate access to a PTR_TO_BTF_ID */ /* Simulate access to a PTR_TO_BTF_ID */
memset(&map_reg, 0, sizeof(map_reg)); memset(&map_reg, 0, sizeof(map_reg));
mark_btf_ld_reg(env, &map_reg, 0, PTR_TO_BTF_ID, btf_vmlinux, *map->ops->map_btf_id, 0); mark_btf_ld_reg(env, &map_reg, 0, PTR_TO_BTF_ID, btf_vmlinux, *map->ops->map_btf_id, 0);
ret = btf_struct_access(&env->log, &map_reg, off, size, atype, &btf_id, &flag); ret = btf_struct_access(&env->log, &map_reg, off, size, atype, &btf_id, &flag, NULL);
if (ret < 0) if (ret < 0)
return ret; return ret;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment