Skip to content

Commit c6f1bfe

Browse files
yonghong-songAlexei Starovoitov
authored andcommitted
bpf: reject program if a __user tagged memory accessed in kernel way
BPF verifier supports direct memory access for BPF_PROG_TYPE_TRACING type of bpf programs, e.g., a->b. If "a" is a pointer pointing to kernel memory, bpf verifier will allow user to write code in C like a->b and the verifier will translate it to a kernel load properly. If "a" is a pointer to user memory, it is expected that bpf developer should be bpf_probe_read_user() helper to get the value a->b. Without utilizing BTF __user tagging information, current verifier will assume that a->b is a kernel memory access and this may generate incorrect result. Now BTF contains __user information, it can check whether the pointer points to a user memory or not. If it is, the verifier can reject the program and force users to use bpf_probe_read_user() helper explicitly. In the future, we can easily extend btf_add_space for other address space tagging, for example, rcu/percpu etc. Signed-off-by: Yonghong Song <yhs@fb.com> Link: https://lore.kernel.org/r/20220127154606.654961-1-yhs@fb.com Signed-off-by: Alexei Starovoitov <ast@kernel.org>
1 parent 7472d5a commit c6f1bfe

File tree

6 files changed

+71
-24
lines changed

6 files changed

+71
-24
lines changed

include/linux/bpf.h

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -332,7 +332,10 @@ enum bpf_type_flag {
332332
*/
333333
MEM_ALLOC = BIT(2 + BPF_BASE_TYPE_BITS),
334334

335-
__BPF_TYPE_LAST_FLAG = MEM_ALLOC,
335+
/* MEM is in user address space. */
336+
MEM_USER = BIT(3 + BPF_BASE_TYPE_BITS),
337+
338+
__BPF_TYPE_LAST_FLAG = MEM_USER,
336339
};
337340

338341
/* Max number of base types. */
@@ -588,7 +591,7 @@ struct bpf_verifier_ops {
588591
const struct btf *btf,
589592
const struct btf_type *t, int off, int size,
590593
enum bpf_access_type atype,
591-
u32 *next_btf_id);
594+
u32 *next_btf_id, enum bpf_type_flag *flag);
592595
};
593596

594597
struct bpf_prog_offload_ops {
@@ -1780,7 +1783,7 @@ static inline bool bpf_tracing_btf_ctx_access(int off, int size,
17801783
int btf_struct_access(struct bpf_verifier_log *log, const struct btf *btf,
17811784
const struct btf_type *t, int off, int size,
17821785
enum bpf_access_type atype,
1783-
u32 *next_btf_id);
1786+
u32 *next_btf_id, enum bpf_type_flag *flag);
17841787
bool btf_struct_ids_match(struct bpf_verifier_log *log,
17851788
const struct btf *btf, u32 id, int off,
17861789
const struct btf *need_btf, u32 need_type_id);

include/linux/btf.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,11 @@ static inline bool btf_type_is_var(const struct btf_type *t)
238238
return BTF_INFO_KIND(t->info) == BTF_KIND_VAR;
239239
}
240240

241+
static inline bool btf_type_is_type_tag(const struct btf_type *t)
242+
{
243+
return BTF_INFO_KIND(t->info) == BTF_KIND_TYPE_TAG;
244+
}
245+
241246
/* union is only a special case of struct:
242247
* all its offsetof(member) == 0
243248
*/

kernel/bpf/btf.c

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4886,6 +4886,7 @@ bool btf_ctx_access(int off, int size, enum bpf_access_type type,
48864886
const char *tname = prog->aux->attach_func_name;
48874887
struct bpf_verifier_log *log = info->log;
48884888
const struct btf_param *args;
4889+
const char *tag_value;
48894890
u32 nr_args, arg;
48904891
int i, ret;
48914892

@@ -5038,6 +5039,13 @@ bool btf_ctx_access(int off, int size, enum bpf_access_type type,
50385039
info->btf = btf;
50395040
info->btf_id = t->type;
50405041
t = btf_type_by_id(btf, t->type);
5042+
5043+
if (btf_type_is_type_tag(t)) {
5044+
tag_value = __btf_name_by_offset(btf, t->name_off);
5045+
if (strcmp(tag_value, "user") == 0)
5046+
info->reg_type |= MEM_USER;
5047+
}
5048+
50415049
/* skip modifiers */
50425050
while (btf_type_is_modifier(t)) {
50435051
info->btf_id = t->type;
@@ -5064,12 +5072,12 @@ enum bpf_struct_walk_result {
50645072

50655073
static int btf_struct_walk(struct bpf_verifier_log *log, const struct btf *btf,
50665074
const struct btf_type *t, int off, int size,
5067-
u32 *next_btf_id)
5075+
u32 *next_btf_id, enum bpf_type_flag *flag)
50685076
{
50695077
u32 i, moff, mtrue_end, msize = 0, total_nelems = 0;
50705078
const struct btf_type *mtype, *elem_type = NULL;
50715079
const struct btf_member *member;
5072-
const char *tname, *mname;
5080+
const char *tname, *mname, *tag_value;
50735081
u32 vlen, elem_id, mid;
50745082

50755083
again:
@@ -5253,7 +5261,8 @@ static int btf_struct_walk(struct bpf_verifier_log *log, const struct btf *btf,
52535261
}
52545262

52555263
if (btf_type_is_ptr(mtype)) {
5256-
const struct btf_type *stype;
5264+
const struct btf_type *stype, *t;
5265+
enum bpf_type_flag tmp_flag = 0;
52575266
u32 id;
52585267

52595268
if (msize != size || off != moff) {
@@ -5262,9 +5271,19 @@ static int btf_struct_walk(struct bpf_verifier_log *log, const struct btf *btf,
52625271
mname, moff, tname, off, size);
52635272
return -EACCES;
52645273
}
5274+
5275+
/* check __user tag */
5276+
t = btf_type_by_id(btf, mtype->type);
5277+
if (btf_type_is_type_tag(t)) {
5278+
tag_value = __btf_name_by_offset(btf, t->name_off);
5279+
if (strcmp(tag_value, "user") == 0)
5280+
tmp_flag = MEM_USER;
5281+
}
5282+
52655283
stype = btf_type_skip_modifiers(btf, mtype->type, &id);
52665284
if (btf_type_is_struct(stype)) {
52675285
*next_btf_id = id;
5286+
*flag = tmp_flag;
52685287
return WALK_PTR;
52695288
}
52705289
}
@@ -5291,20 +5310,22 @@ static int btf_struct_walk(struct bpf_verifier_log *log, const struct btf *btf,
52915310
int btf_struct_access(struct bpf_verifier_log *log, const struct btf *btf,
52925311
const struct btf_type *t, int off, int size,
52935312
enum bpf_access_type atype __maybe_unused,
5294-
u32 *next_btf_id)
5313+
u32 *next_btf_id, enum bpf_type_flag *flag)
52955314
{
5315+
enum bpf_type_flag tmp_flag = 0;
52965316
int err;
52975317
u32 id;
52985318

52995319
do {
5300-
err = btf_struct_walk(log, btf, t, off, size, &id);
5320+
err = btf_struct_walk(log, btf, t, off, size, &id, &tmp_flag);
53015321

53025322
switch (err) {
53035323
case WALK_PTR:
53045324
/* If we found the pointer or scalar on t+off,
53055325
* we're done.
53065326
*/
53075327
*next_btf_id = id;
5328+
*flag = tmp_flag;
53085329
return PTR_TO_BTF_ID;
53095330
case WALK_SCALAR:
53105331
return SCALAR_VALUE;
@@ -5349,6 +5370,7 @@ bool btf_struct_ids_match(struct bpf_verifier_log *log,
53495370
const struct btf *need_btf, u32 need_type_id)
53505371
{
53515372
const struct btf_type *type;
5373+
enum bpf_type_flag flag;
53525374
int err;
53535375

53545376
/* Are we already done? */
@@ -5359,7 +5381,7 @@ bool btf_struct_ids_match(struct bpf_verifier_log *log,
53595381
type = btf_type_by_id(btf, id);
53605382
if (!type)
53615383
return false;
5362-
err = btf_struct_walk(log, btf, type, off, 1, &id);
5384+
err = btf_struct_walk(log, btf, type, off, 1, &id, &flag);
53635385
if (err != WALK_STRUCT)
53645386
return false;
53655387

kernel/bpf/verifier.c

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -536,7 +536,7 @@ static bool is_cmpxchg_insn(const struct bpf_insn *insn)
536536
static const char *reg_type_str(struct bpf_verifier_env *env,
537537
enum bpf_reg_type type)
538538
{
539-
char postfix[16] = {0}, prefix[16] = {0};
539+
char postfix[16] = {0}, prefix[32] = {0};
540540
static const char * const str[] = {
541541
[NOT_INIT] = "?",
542542
[SCALAR_VALUE] = "inv",
@@ -570,9 +570,11 @@ static const char *reg_type_str(struct bpf_verifier_env *env,
570570
}
571571

572572
if (type & MEM_RDONLY)
573-
strncpy(prefix, "rdonly_", 16);
573+
strncpy(prefix, "rdonly_", 32);
574574
if (type & MEM_ALLOC)
575-
strncpy(prefix, "alloc_", 16);
575+
strncpy(prefix, "alloc_", 32);
576+
if (type & MEM_USER)
577+
strncpy(prefix, "user_", 32);
576578

577579
snprintf(env->type_str_buf, TYPE_STR_BUF_LEN, "%s%s%s",
578580
prefix, str[base_type(type)], postfix);
@@ -1547,14 +1549,15 @@ static void mark_reg_not_init(struct bpf_verifier_env *env,
15471549
static void mark_btf_ld_reg(struct bpf_verifier_env *env,
15481550
struct bpf_reg_state *regs, u32 regno,
15491551
enum bpf_reg_type reg_type,
1550-
struct btf *btf, u32 btf_id)
1552+
struct btf *btf, u32 btf_id,
1553+
enum bpf_type_flag flag)
15511554
{
15521555
if (reg_type == SCALAR_VALUE) {
15531556
mark_reg_unknown(env, regs, regno);
15541557
return;
15551558
}
15561559
mark_reg_known_zero(env, regs, regno);
1557-
regs[regno].type = PTR_TO_BTF_ID;
1560+
regs[regno].type = PTR_TO_BTF_ID | flag;
15581561
regs[regno].btf = btf;
15591562
regs[regno].btf_id = btf_id;
15601563
}
@@ -4152,6 +4155,7 @@ static int check_ptr_to_btf_access(struct bpf_verifier_env *env,
41524155
struct bpf_reg_state *reg = regs + regno;
41534156
const struct btf_type *t = btf_type_by_id(reg->btf, reg->btf_id);
41544157
const char *tname = btf_name_by_offset(reg->btf, t->name_off);
4158+
enum bpf_type_flag flag = 0;
41554159
u32 btf_id;
41564160
int ret;
41574161

@@ -4171,24 +4175,31 @@ static int check_ptr_to_btf_access(struct bpf_verifier_env *env,
41714175
return -EACCES;
41724176
}
41734177

4178+
if (reg->type & MEM_USER) {
4179+
verbose(env,
4180+
"R%d is ptr_%s access user memory: off=%d\n",
4181+
regno, tname, off);
4182+
return -EACCES;
4183+
}
4184+
41744185
if (env->ops->btf_struct_access) {
41754186
ret = env->ops->btf_struct_access(&env->log, reg->btf, t,
4176-
off, size, atype, &btf_id);
4187+
off, size, atype, &btf_id, &flag);
41774188
} else {
41784189
if (atype != BPF_READ) {
41794190
verbose(env, "only read is supported\n");
41804191
return -EACCES;
41814192
}
41824193

41834194
ret = btf_struct_access(&env->log, reg->btf, t, off, size,
4184-
atype, &btf_id);
4195+
atype, &btf_id, &flag);
41854196
}
41864197

41874198
if (ret < 0)
41884199
return ret;
41894200

41904201
if (atype == BPF_READ && value_regno >= 0)
4191-
mark_btf_ld_reg(env, regs, value_regno, ret, reg->btf, btf_id);
4202+
mark_btf_ld_reg(env, regs, value_regno, ret, reg->btf, btf_id, flag);
41924203

41934204
return 0;
41944205
}
@@ -4201,6 +4212,7 @@ static int check_ptr_to_map_access(struct bpf_verifier_env *env,
42014212
{
42024213
struct bpf_reg_state *reg = regs + regno;
42034214
struct bpf_map *map = reg->map_ptr;
4215+
enum bpf_type_flag flag = 0;
42044216
const struct btf_type *t;
42054217
const char *tname;
42064218
u32 btf_id;
@@ -4238,12 +4250,12 @@ static int check_ptr_to_map_access(struct bpf_verifier_env *env,
42384250
return -EACCES;
42394251
}
42404252

4241-
ret = btf_struct_access(&env->log, btf_vmlinux, t, off, size, atype, &btf_id);
4253+
ret = btf_struct_access(&env->log, btf_vmlinux, t, off, size, atype, &btf_id, &flag);
42424254
if (ret < 0)
42434255
return ret;
42444256

42454257
if (value_regno >= 0)
4246-
mark_btf_ld_reg(env, regs, value_regno, ret, btf_vmlinux, btf_id);
4258+
mark_btf_ld_reg(env, regs, value_regno, ret, btf_vmlinux, btf_id, flag);
42474259

42484260
return 0;
42494261
}
@@ -4444,7 +4456,8 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, u32 regn
44444456
if (err < 0)
44454457
return err;
44464458

4447-
err = check_ctx_access(env, insn_idx, off, size, t, &reg_type, &btf, &btf_id);
4459+
err = check_ctx_access(env, insn_idx, off, size, t, &reg_type, &btf,
4460+
&btf_id);
44484461
if (err)
44494462
verbose_linfo(env, insn_idx, "; ");
44504463
if (!err && t == BPF_READ && value_regno >= 0) {

net/bpf/bpf_dummy_struct_ops.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,8 @@ static int bpf_dummy_ops_btf_struct_access(struct bpf_verifier_log *log,
145145
const struct btf *btf,
146146
const struct btf_type *t, int off,
147147
int size, enum bpf_access_type atype,
148-
u32 *next_btf_id)
148+
u32 *next_btf_id,
149+
enum bpf_type_flag *flag)
149150
{
150151
const struct btf_type *state;
151152
s32 type_id;
@@ -162,7 +163,8 @@ static int bpf_dummy_ops_btf_struct_access(struct bpf_verifier_log *log,
162163
return -EACCES;
163164
}
164165

165-
err = btf_struct_access(log, btf, t, off, size, atype, next_btf_id);
166+
err = btf_struct_access(log, btf, t, off, size, atype, next_btf_id,
167+
flag);
166168
if (err < 0)
167169
return err;
168170

net/ipv4/bpf_tcp_ca.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,12 +96,14 @@ static int bpf_tcp_ca_btf_struct_access(struct bpf_verifier_log *log,
9696
const struct btf *btf,
9797
const struct btf_type *t, int off,
9898
int size, enum bpf_access_type atype,
99-
u32 *next_btf_id)
99+
u32 *next_btf_id,
100+
enum bpf_type_flag *flag)
100101
{
101102
size_t end;
102103

103104
if (atype == BPF_READ)
104-
return btf_struct_access(log, btf, t, off, size, atype, next_btf_id);
105+
return btf_struct_access(log, btf, t, off, size, atype, next_btf_id,
106+
flag);
105107

106108
if (t != tcp_sock_type) {
107109
bpf_log(log, "only read is supported\n");

0 commit comments

Comments
 (0)