Skip to content

Commit 7ea203f

Browse files
yonghong-songNobody
authored and
Nobody
committed
bpf: reject program if a __user tagged memory accessed in kernel way
BPF verifier supports direct access, 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 bpf 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. In the current mechanism, if "a" is a user pointer, a->b access may trigger a page fault and the verifier generated code will simulate bpf_probe_read() and return 0 for a->b, which may not be correct value. 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. Signed-off-by: Yonghong Song <[email protected]>
1 parent 428aab4 commit 7ea203f

File tree

5 files changed

+32
-6
lines changed

5 files changed

+32
-6
lines changed

include/linux/bpf.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -473,6 +473,7 @@ struct bpf_insn_access_aux {
473473
struct {
474474
struct btf *btf;
475475
u32 btf_id;
476+
bool is_user;
476477
};
477478
};
478479
struct bpf_verifier_log *log; /* for verbose logs */

include/linux/bpf_verifier.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ struct bpf_reg_state {
6666
struct {
6767
struct btf *btf;
6868
u32 btf_id;
69+
bool is_user;
6970
};
7071

7172
u32 mem_size; /* for PTR_TO_MEM | PTR_TO_MEM_OR_NULL */

include/linux/btf.h

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

172+
static inline bool btf_type_is_type_tag(const struct btf_type *t)
173+
{
174+
return BTF_INFO_KIND(t->info) == BTF_KIND_TYPE_TAG;
175+
}
176+
172177
/* union is only a special case of struct:
173178
* all its offsetof(member) == 0
174179
*/

kernel/bpf/btf.c

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4999,6 +4999,14 @@ bool btf_ctx_access(int off, int size, enum bpf_access_type type,
49994999
info->btf = btf;
50005000
info->btf_id = t->type;
50015001
t = btf_type_by_id(btf, t->type);
5002+
5003+
if (btf_type_is_type_tag(t)) {
5004+
const char *tag_value = __btf_name_by_offset(btf, t->name_off);
5005+
5006+
if (strcmp(tag_value, "user") == 0)
5007+
info->is_user = true;
5008+
}
5009+
50025010
/* skip modifiers */
50035011
while (btf_type_is_modifier(t)) {
50045012
info->btf_id = t->type;
@@ -5010,8 +5018,9 @@ bool btf_ctx_access(int off, int size, enum bpf_access_type type,
50105018
tname, arg, btf_kind_str[BTF_INFO_KIND(t->info)]);
50115019
return false;
50125020
}
5013-
bpf_log(log, "func '%s' arg%d has btf_id %d type %s '%s'\n",
5014-
tname, arg, info->btf_id, btf_kind_str[BTF_INFO_KIND(t->info)],
5021+
bpf_log(log, "func '%s' arg%d has btf_id %d is_user %d type %s '%s'\n",
5022+
tname, arg, info->btf_id, info->is_user,
5023+
btf_kind_str[BTF_INFO_KIND(t->info)],
50155024
__btf_name_by_offset(btf, t->name_off));
50165025
return true;
50175026
}
@@ -5030,7 +5039,7 @@ static int btf_struct_walk(struct bpf_verifier_log *log, const struct btf *btf,
50305039
u32 i, moff, mtrue_end, msize = 0, total_nelems = 0;
50315040
const struct btf_type *mtype, *elem_type = NULL;
50325041
const struct btf_member *member;
5033-
const char *tname, *mname;
5042+
const char *tname, *mname, *tag_value;
50345043
u32 vlen, elem_id, mid;
50355044

50365045
again:

kernel/bpf/verifier.c

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -647,7 +647,8 @@ static void print_verifier_state(struct bpf_verifier_env *env,
647647
if (t == PTR_TO_BTF_ID ||
648648
t == PTR_TO_BTF_ID_OR_NULL ||
649649
t == PTR_TO_PERCPU_BTF_ID)
650-
verbose(env, "%s", kernel_type_name(reg->btf, reg->btf_id));
650+
verbose(env, "%s,is_user=%d", kernel_type_name(reg->btf, reg->btf_id),
651+
reg->is_user);
651652
verbose(env, "(id=%d", reg->id);
652653
if (reg_type_may_be_refcounted_or_null(t))
653654
verbose(env, ",ref_obj_id=%d", reg->ref_obj_id);
@@ -3551,7 +3552,7 @@ static int check_packet_access(struct bpf_verifier_env *env, u32 regno, int off,
35513552
/* check access to 'struct bpf_context' fields. Supports fixed offsets only */
35523553
static int check_ctx_access(struct bpf_verifier_env *env, int insn_idx, int off, int size,
35533554
enum bpf_access_type t, enum bpf_reg_type *reg_type,
3554-
struct btf **btf, u32 *btf_id)
3555+
struct btf **btf, u32 *btf_id, bool *is_user)
35553556
{
35563557
struct bpf_insn_access_aux info = {
35573558
.reg_type = *reg_type,
@@ -3572,6 +3573,7 @@ static int check_ctx_access(struct bpf_verifier_env *env, int insn_idx, int off,
35723573
if (*reg_type == PTR_TO_BTF_ID || *reg_type == PTR_TO_BTF_ID_OR_NULL) {
35733574
*btf = info.btf;
35743575
*btf_id = info.btf_id;
3576+
*is_user = info.is_user;
35753577
} else {
35763578
env->insn_aux_data[insn_idx].ctx_field_size = info.ctx_field_size;
35773579
}
@@ -4116,6 +4118,13 @@ static int check_ptr_to_btf_access(struct bpf_verifier_env *env,
41164118
return -EACCES;
41174119
}
41184120

4121+
if (reg->is_user) {
4122+
verbose(env,
4123+
"R%d is ptr_%s access user memory: off=%d\n",
4124+
regno, tname, off);
4125+
return -EACCES;
4126+
}
4127+
41194128
if (env->ops->btf_struct_access) {
41204129
ret = env->ops->btf_struct_access(&env->log, reg->btf, t,
41214130
off, size, atype, &btf_id);
@@ -4374,7 +4383,7 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, u32 regn
43744383
if (err < 0)
43754384
return err;
43764385

4377-
err = check_ctx_access(env, insn_idx, off, size, t, &reg_type, &btf, &btf_id);
4386+
err = check_ctx_access(env, insn_idx, off, size, t, &reg_type, &btf, &btf_id, &is_user);
43784387
if (err)
43794388
verbose_linfo(env, insn_idx, "; ");
43804389
if (!err && t == BPF_READ && value_regno >= 0) {
@@ -4399,6 +4408,7 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, u32 regn
43994408
reg_type == PTR_TO_BTF_ID_OR_NULL) {
44004409
regs[value_regno].btf = btf;
44014410
regs[value_regno].btf_id = btf_id;
4411+
regs[value_regno].is_user = is_user;
44024412
}
44034413
}
44044414
regs[value_regno].type = reg_type;

0 commit comments

Comments
 (0)