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.
Unified · Split
| @@ -332,7 +332,10 @@ enum bpf_type_flag { · 331 unmodified lines | ||
| 332 | 332 | */ |
| 333 | 333 | MEM_ALLOC = BIT(2 + BPF_BASE_TYPE_BITS), |
| 334 | 334 | |
| 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, | |
| 336 | 339 | }; |
| 337 | 340 | |
| 338 | 341 | /* Max number of base types. */ |
| @@ -588,7 +591,7 @@ struct bpf_verifier_ops { · 249 unmodified lines | ||
| 588 | 591 | const struct btf *btf, |
| 589 | 592 | const struct btf_type *t, int off, int size, |
| 590 | 593 | enum bpf_access_type atype, |
| 591 | − u32 *next_btf_id); | |
| 594 | + u32 *next_btf_id, enum bpf_type_flag *flag); | |
| 592 | 595 | }; |
| 593 | 596 | |
| 594 | 597 | struct bpf_prog_offload_ops { |
| @@ -1780,7 +1783,7 @@ static inline bool bpf_tracing_btf_ctx_access(int off, int size, · 1185 unmodified lines | ||
| 1780 | 1783 | int btf_struct_access(struct bpf_verifier_log *log, const struct btf *btf, |
| 1781 | 1784 | const struct btf_type *t, int off, int size, |
| 1782 | 1785 | enum bpf_access_type atype, |
| 1783 | − u32 *next_btf_id); | |
| 1786 | + u32 *next_btf_id, enum bpf_type_flag *flag); | |
| 1784 | 1787 | bool btf_struct_ids_match(struct bpf_verifier_log *log, |
| 1785 | 1788 | const struct btf *btf, u32 id, int off, |
| 1786 | 1789 | const struct btf *need_btf, u32 need_type_id); |
| @@ -238,6 +238,11 @@ static inline bool btf_type_is_var(const struct btf_type *t) · 237 unmodified lines | ||
| 238 | 238 | return BTF_INFO_KIND(t->info) == BTF_KIND_VAR; |
| 239 | 239 | } |
| 240 | 240 | |
| 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 | + | |
| 241 | 246 | /* union is only a special case of struct: |
| 242 | 247 | * all its offsetof(member) == 0 |
| 243 | 248 | */ |
| @@ -4886,6 +4886,7 @@ bool btf_ctx_access(int off, int size, enum bpf_access_type type, · 4885 unmodified lines | ||
| 4886 | 4886 | const char *tname = prog->aux->attach_func_name; |
| 4887 | 4887 | struct bpf_verifier_log *log = info->log; |
| 4888 | 4888 | const struct btf_param *args; |
| 4889 | + const char *tag_value; | |
| 4889 | 4890 | u32 nr_args, arg; |
| 4890 | 4891 | int i, ret; |
| 4891 | 4892 | |
| @@ -5038,6 +5039,13 @@ bool btf_ctx_access(int off, int size, enum bpf_access_type type, · 146 unmodified lines | ||
| 5038 | 5039 | info->btf = btf; |
| 5039 | 5040 | info->btf_id = t->type; |
| 5040 | 5041 | 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 | + | |
| 5041 | 5049 | /* skip modifiers */ |
| 5042 | 5050 | while (btf_type_is_modifier(t)) { |
| 5043 | 5051 | info->btf_id = t->type; |
| @@ -5064,12 +5072,12 @@ enum bpf_struct_walk_result { · 20 unmodified lines | ||
| 5064 | 5072 | |
| 5065 | 5073 | static int btf_struct_walk(struct bpf_verifier_log *log, const struct btf *btf, |
| 5066 | 5074 | const struct btf_type *t, int off, int size, |
| 5067 | − u32 *next_btf_id) | |
| 5075 | + u32 *next_btf_id, enum bpf_type_flag *flag) | |
| 5068 | 5076 | { |
| 5069 | 5077 | u32 i, moff, mtrue_end, msize = 0, total_nelems = 0; |
| 5070 | 5078 | const struct btf_type *mtype, *elem_type = NULL; |
| 5071 | 5079 | const struct btf_member *member; |
| 5072 | − const char *tname, *mname; | |
| 5080 | + const char *tname, *mname, *tag_value; | |
| 5073 | 5081 | u32 vlen, elem_id, mid; |
| 5074 | 5082 | |
| 5075 | 5083 | again: |
| @@ -5253,7 +5261,8 @@ error: · 177 unmodified lines | ||
| 5253 | 5261 | } |
| 5254 | 5262 | |
| 5255 | 5263 | 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; | |
| 5257 | 5266 | u32 id; |
| 5258 | 5267 | |
| 5259 | 5268 | if (msize != size || off != moff) { |
| @@ -5262,9 +5271,19 @@ error: · 2 unmodified lines | ||
| 5262 | 5271 | mname, moff, tname, off, size); |
| 5263 | 5272 | return -EACCES; |
| 5264 | 5273 | } |
| 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 | + | |
| 5265 | 5283 | stype = btf_type_skip_modifiers(btf, mtype->type, &id); |
| 5266 | 5284 | if (btf_type_is_struct(stype)) { |
| 5267 | 5285 | *next_btf_id = id; |
| 5286 | + *flag = tmp_flag; | |
| 5268 | 5287 | return WALK_PTR; |
| 5269 | 5288 | } |
| 5270 | 5289 | } |
| @@ -5291,13 +5310,14 @@ error: · 20 unmodified lines | ||
| 5291 | 5310 | int btf_struct_access(struct bpf_verifier_log *log, const struct btf *btf, |
| 5292 | 5311 | const struct btf_type *t, int off, int size, |
| 5293 | 5312 | enum bpf_access_type atype __maybe_unused, |
| 5294 | − u32 *next_btf_id) | |
| 5313 | + u32 *next_btf_id, enum bpf_type_flag *flag) | |
| 5295 | 5314 | { |
| 5315 | + enum bpf_type_flag tmp_flag = 0; | |
| 5296 | 5316 | int err; |
| 5297 | 5317 | u32 id; |
| 5298 | 5318 | |
| 5299 | 5319 | 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); | |
| 5301 | 5321 | |
| 5302 | 5322 | switch (err) { |
| 5303 | 5323 | case WALK_PTR: |
| @@ -5305,6 +5325,7 @@ int btf_struct_access(struct bpf_verifier_log *log, const struct btf *btf, · 1 unmodified line | ||
| 5305 | 5325 | * we're done. |
| 5306 | 5326 | */ |
| 5307 | 5327 | *next_btf_id = id; |
| 5328 | + *flag = tmp_flag; | |
| 5308 | 5329 | return PTR_TO_BTF_ID; |
| 5309 | 5330 | case WALK_SCALAR: |
| 5310 | 5331 | return SCALAR_VALUE; |
| @@ -5349,6 +5370,7 @@ bool btf_struct_ids_match(struct bpf_verifier_log *log, · 38 unmodified lines | ||
| 5349 | 5370 | const struct btf *need_btf, u32 need_type_id) |
| 5350 | 5371 | { |
| 5351 | 5372 | const struct btf_type *type; |
| 5373 | + enum bpf_type_flag flag; | |
| 5352 | 5374 | int err; |
| 5353 | 5375 | |
| 5354 | 5376 | /* Are we already done? */ |
| @@ -5359,7 +5381,7 @@ again: · 4 unmodified lines | ||
| 5359 | 5381 | type = btf_type_by_id(btf, id); |
| 5360 | 5382 | if (!type) |
| 5361 | 5383 | 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); | |
| 5363 | 5385 | if (err != WALK_STRUCT) |
| 5364 | 5386 | return false; |
| 5365 | 5387 | |
| @@ -536,7 +536,7 @@ static bool is_cmpxchg_insn(const struct bpf_insn *insn) · 535 unmodified lines | ||
| 536 | 536 | static const char *reg_type_str(struct bpf_verifier_env *env, |
| 537 | 537 | enum bpf_reg_type type) |
| 538 | 538 | { |
| 539 | − char postfix[16] = {0}, prefix[16] = {0}; | |
| 539 | + char postfix[16] = {0}, prefix[32] = {0}; | |
| 540 | 540 | static const char * const str[] = { |
| 541 | 541 | [NOT_INIT] = "?", |
| 542 | 542 | [SCALAR_VALUE] = "inv", |
| @@ -570,9 +570,11 @@ static const char *reg_type_str(struct bpf_verifier_env *env, · 27 unmodified lines | ||
| 570 | 570 | } |
| 571 | 571 | |
| 572 | 572 | if (type & MEM_RDONLY) |
| 573 | − strncpy(prefix, "rdonly_", 16); | |
| 573 | + strncpy(prefix, "rdonly_", 32); | |
| 574 | 574 | if (type & MEM_ALLOC) |
| 575 | − strncpy(prefix, "alloc_", 16); | |
| 575 | + strncpy(prefix, "alloc_", 32); | |
| 576 | + if (type & MEM_USER) | |
| 577 | + strncpy(prefix, "user_", 32); | |
| 576 | 578 | |
| 577 | 579 | snprintf(env->type_str_buf, TYPE_STR_BUF_LEN, "%s%s%s", |
| 578 | 580 | prefix, str[base_type(type)], postfix); |
| @@ -1547,14 +1549,15 @@ static void mark_reg_not_init(struct bpf_verifier_env *env, · 968 unmodified lines | ||
| 1547 | 1549 | static void mark_btf_ld_reg(struct bpf_verifier_env *env, |
| 1548 | 1550 | struct bpf_reg_state *regs, u32 regno, |
| 1549 | 1551 | 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) | |
| 1551 | 1554 | { |
| 1552 | 1555 | if (reg_type == SCALAR_VALUE) { |
| 1553 | 1556 | mark_reg_unknown(env, regs, regno); |
| 1554 | 1557 | return; |
| 1555 | 1558 | } |
| 1556 | 1559 | mark_reg_known_zero(env, regs, regno); |
| 1557 | − regs[regno].type = PTR_TO_BTF_ID; | |
| 1560 | + regs[regno].type = PTR_TO_BTF_ID | flag; | |
| 1558 | 1561 | regs[regno].btf = btf; |
| 1559 | 1562 | regs[regno].btf_id = btf_id; |
| 1560 | 1563 | } |
| @@ -4152,6 +4155,7 @@ static int check_ptr_to_btf_access(struct bpf_verifier_env *env, · 2591 unmodified lines | ||
| 4152 | 4155 | struct bpf_reg_state *reg = regs + regno; |
| 4153 | 4156 | const struct btf_type *t = btf_type_by_id(reg->btf, reg->btf_id); |
| 4154 | 4157 | const char *tname = btf_name_by_offset(reg->btf, t->name_off); |
| 4158 | + enum bpf_type_flag flag = 0; | |
| 4155 | 4159 | u32 btf_id; |
| 4156 | 4160 | int ret; |
| 4157 | 4161 | |
| @@ -4171,9 +4175,16 @@ static int check_ptr_to_btf_access(struct bpf_verifier_env *env, · 13 unmodified lines | ||
| 4171 | 4175 | return -EACCES; |
| 4172 | 4176 | } |
| 4173 | 4177 | |
| 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 | + | |
| 4174 | 4185 | if (env->ops->btf_struct_access) { |
| 4175 | 4186 | ret = env->ops->btf_struct_access(&env->log, reg->btf, t, |
| 4176 | − off, size, atype, &btf_id); | |
| 4187 | + off, size, atype, &btf_id, &flag); | |
| 4177 | 4188 | } else { |
| 4178 | 4189 | if (atype != BPF_READ) { |
| 4179 | 4190 | verbose(env, "only read is supported\n"); |
| @@ -4181,14 +4192,14 @@ static int check_ptr_to_btf_access(struct bpf_verifier_env *env, · 1 unmodified line | ||
| 4181 | 4192 | } |
| 4182 | 4193 | |
| 4183 | 4194 | ret = btf_struct_access(&env->log, reg->btf, t, off, size, |
| 4184 | − atype, &btf_id); | |
| 4195 | + atype, &btf_id, &flag); | |
| 4185 | 4196 | } |
| 4186 | 4197 | |
| 4187 | 4198 | if (ret < 0) |
| 4188 | 4199 | return ret; |
| 4189 | 4200 | |
| 4190 | 4201 | 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); | |
| 4192 | 4203 | |
| 4193 | 4204 | return 0; |
| 4194 | 4205 | } |
| @@ -4201,6 +4212,7 @@ static int check_ptr_to_map_access(struct bpf_verifier_env *env, · 6 unmodified lines | ||
| 4201 | 4212 | { |
| 4202 | 4213 | struct bpf_reg_state *reg = regs + regno; |
| 4203 | 4214 | struct bpf_map *map = reg->map_ptr; |
| 4215 | + enum bpf_type_flag flag = 0; | |
| 4204 | 4216 | const struct btf_type *t; |
| 4205 | 4217 | const char *tname; |
| 4206 | 4218 | u32 btf_id; |
| @@ -4238,12 +4250,12 @@ static int check_ptr_to_map_access(struct bpf_verifier_env *env, · 31 unmodified lines | ||
| 4238 | 4250 | return -EACCES; |
| 4239 | 4251 | } |
| 4240 | 4252 | |
| 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); | |
| 4242 | 4254 | if (ret < 0) |
| 4243 | 4255 | return ret; |
| 4244 | 4256 | |
| 4245 | 4257 | 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); | |
| 4247 | 4259 | |
| 4248 | 4260 | return 0; |
| 4249 | 4261 | } |
| @@ -4444,7 +4456,8 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, u32 regn · 194 unmodified lines | ||
| 4444 | 4456 | if (err < 0) |
| 4445 | 4457 | return err; |
| 4446 | 4458 | |
| 4447 | − err = check_ctx_access(env, insn_idx, off, size, t, ®_type, &btf, &btf_id); | |
| 4459 | + err = check_ctx_access(env, insn_idx, off, size, t, ®_type, &btf, | |
| 4460 | + &btf_id); | |
| 4448 | 4461 | if (err) |
| 4449 | 4462 | verbose_linfo(env, insn_idx, "; "); |
| 4450 | 4463 | if (!err && t == BPF_READ && value_regno >= 0) { |
| @@ -145,7 +145,8 @@ static int bpf_dummy_ops_btf_struct_access(struct bpf_verifier_log *log, · 144 unmodified lines | ||
| 145 | 145 | const struct btf *btf, |
| 146 | 146 | const struct btf_type *t, int off, |
| 147 | 147 | int size, enum bpf_access_type atype, |
| 148 | − u32 *next_btf_id) | |
| 148 | + u32 *next_btf_id, | |
| 149 | + enum bpf_type_flag *flag) | |
| 149 | 150 | { |
| 150 | 151 | const struct btf_type *state; |
| 151 | 152 | s32 type_id; |
| @@ -162,7 +163,8 @@ static int bpf_dummy_ops_btf_struct_access(struct bpf_verifier_log *log, · 10 unmodified lines | ||
| 162 | 163 | return -EACCES; |
| 163 | 164 | } |
| 164 | 165 | |
| 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); | |
| 166 | 168 | if (err < 0) |
| 167 | 169 | return err; |
| 168 | 170 | |
| @@ -96,12 +96,14 @@ static int bpf_tcp_ca_btf_struct_access(struct bpf_verifier_log *log, · 95 unmodified lines | ||
| 96 | 96 | const struct btf *btf, |
| 97 | 97 | const struct btf_type *t, int off, |
| 98 | 98 | int size, enum bpf_access_type atype, |
| 99 | − u32 *next_btf_id) | |
| 99 | + u32 *next_btf_id, | |
| 100 | + enum bpf_type_flag *flag) | |
| 100 | 101 | { |
| 101 | 102 | size_t end; |
| 102 | 103 | |
| 103 | 104 | 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); | |
| 105 | 107 | |
| 106 | 108 | if (t != tcp_sock_type) { |
| 107 | 109 | bpf_log(log, "only read is supported\n"); |