Skip to content

Commit 57539b1

Browse files
Byte-LabAlexei Starovoitov
authored andcommitted
bpf: Enable annotating trusted nested pointers
In kfuncs, a "trusted" pointer is a pointer that the kfunc can assume is safe, and which the verifier will allow to be passed to a KF_TRUSTED_ARGS kfunc. Currently, a KF_TRUSTED_ARGS kfunc disallows any pointer to be passed at a nonzero offset, but sometimes this is in fact safe if the "nested" pointer's lifetime is inherited from its parent. For example, the const cpumask_t *cpus_ptr field in a struct task_struct will remain valid until the task itself is destroyed, and thus would also be safe to pass to a KF_TRUSTED_ARGS kfunc. While it would be conceptually simple to enable this by using BTF tags, gcc unfortunately does not yet support this. In the interim, this patch enables support for this by using a type-naming convention. A new BTF_TYPE_SAFE_NESTED macro is defined in verifier.c which allows a developer to specify the nested fields of a type which are considered trusted if its parent is also trusted. The verifier is also updated to account for this. A patch with selftests will be added in a follow-on change, along with documentation for this feature. Signed-off-by: David Vernet <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Alexei Starovoitov <[email protected]>
1 parent 0eb9d19 commit 57539b1

File tree

3 files changed

+94
-3
lines changed

3 files changed

+94
-3
lines changed

include/linux/bpf.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2187,6 +2187,10 @@ struct bpf_core_ctx {
21872187
const struct btf *btf;
21882188
};
21892189

2190+
bool btf_nested_type_is_trusted(struct bpf_verifier_log *log,
2191+
const struct bpf_reg_state *reg,
2192+
int off);
2193+
21902194
int bpf_core_apply(struct bpf_core_ctx *ctx, const struct bpf_core_relo *relo,
21912195
int relo_idx, void *insn);
21922196

kernel/bpf/btf.c

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8227,3 +8227,64 @@ int bpf_core_apply(struct bpf_core_ctx *ctx, const struct bpf_core_relo *relo,
82278227
}
82288228
return err;
82298229
}
8230+
8231+
bool btf_nested_type_is_trusted(struct bpf_verifier_log *log,
8232+
const struct bpf_reg_state *reg,
8233+
int off)
8234+
{
8235+
struct btf *btf = reg->btf;
8236+
const struct btf_type *walk_type, *safe_type;
8237+
const char *tname;
8238+
char safe_tname[64];
8239+
long ret, safe_id;
8240+
const struct btf_member *member, *m_walk = NULL;
8241+
u32 i;
8242+
const char *walk_name;
8243+
8244+
walk_type = btf_type_by_id(btf, reg->btf_id);
8245+
if (!walk_type)
8246+
return false;
8247+
8248+
tname = btf_name_by_offset(btf, walk_type->name_off);
8249+
8250+
ret = snprintf(safe_tname, sizeof(safe_tname), "%s__safe_fields", tname);
8251+
if (ret < 0)
8252+
return false;
8253+
8254+
safe_id = btf_find_by_name_kind(btf, safe_tname, BTF_INFO_KIND(walk_type->info));
8255+
if (safe_id < 0)
8256+
return false;
8257+
8258+
safe_type = btf_type_by_id(btf, safe_id);
8259+
if (!safe_type)
8260+
return false;
8261+
8262+
for_each_member(i, walk_type, member) {
8263+
u32 moff;
8264+
8265+
/* We're looking for the PTR_TO_BTF_ID member in the struct
8266+
* type we're walking which matches the specified offset.
8267+
* Below, we'll iterate over the fields in the safe variant of
8268+
* the struct and see if any of them has a matching type /
8269+
* name.
8270+
*/
8271+
moff = __btf_member_bit_offset(walk_type, member) / 8;
8272+
if (off == moff) {
8273+
m_walk = member;
8274+
break;
8275+
}
8276+
}
8277+
if (m_walk == NULL)
8278+
return false;
8279+
8280+
walk_name = __btf_name_by_offset(btf, m_walk->name_off);
8281+
for_each_member(i, safe_type, member) {
8282+
const char *m_name = __btf_name_by_offset(btf, member->name_off);
8283+
8284+
/* If we match on both type and name, the field is considered trusted. */
8285+
if (m_walk->type == member->type && !strcmp(walk_name, m_name))
8286+
return true;
8287+
}
8288+
8289+
return false;
8290+
}

kernel/bpf/verifier.c

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4943,6 +4943,25 @@ static int bpf_map_direct_read(struct bpf_map *map, int off, int size, u64 *val)
49434943
return 0;
49444944
}
49454945

4946+
#define BTF_TYPE_SAFE_NESTED(__type) __PASTE(__type, __safe_fields)
4947+
4948+
BTF_TYPE_SAFE_NESTED(struct task_struct) {
4949+
const cpumask_t *cpus_ptr;
4950+
};
4951+
4952+
static bool nested_ptr_is_trusted(struct bpf_verifier_env *env,
4953+
struct bpf_reg_state *reg,
4954+
int off)
4955+
{
4956+
/* If its parent is not trusted, it can't regain its trusted status. */
4957+
if (!is_trusted_reg(reg))
4958+
return false;
4959+
4960+
BTF_TYPE_EMIT(BTF_TYPE_SAFE_NESTED(struct task_struct));
4961+
4962+
return btf_nested_type_is_trusted(&env->log, reg, off);
4963+
}
4964+
49464965
static int check_ptr_to_btf_access(struct bpf_verifier_env *env,
49474966
struct bpf_reg_state *regs,
49484967
int regno, int off, int size,
@@ -5031,10 +5050,17 @@ static int check_ptr_to_btf_access(struct bpf_verifier_env *env,
50315050
if (type_flag(reg->type) & PTR_UNTRUSTED)
50325051
flag |= PTR_UNTRUSTED;
50335052

5034-
/* By default any pointer obtained from walking a trusted pointer is
5035-
* no longer trusted except the rcu case below.
5053+
/* By default any pointer obtained from walking a trusted pointer is no
5054+
* longer trusted, unless the field being accessed has explicitly been
5055+
* marked as inheriting its parent's state of trust.
5056+
*
5057+
* An RCU-protected pointer can also be deemed trusted if we are in an
5058+
* RCU read region. This case is handled below.
50365059
*/
5037-
flag &= ~PTR_TRUSTED;
5060+
if (nested_ptr_is_trusted(env, reg, off))
5061+
flag |= PTR_TRUSTED;
5062+
else
5063+
flag &= ~PTR_TRUSTED;
50385064

50395065
if (flag & MEM_RCU) {
50405066
/* Mark value register as MEM_RCU only if it is protected by

0 commit comments

Comments
 (0)