|
11 | 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
12 | 12 | * General Public License for more details. |
13 | 13 | */ |
| 14 | +#include <uapi/linux/btf.h> |
14 | 15 | #include <linux/kernel.h> |
15 | 16 | #include <linux/types.h> |
16 | 17 | #include <linux/slab.h> |
17 | 18 | #include <linux/bpf.h> |
| 19 | +#include <linux/btf.h> |
18 | 20 | #include <linux/bpf_verifier.h> |
19 | 21 | #include <linux/filter.h> |
20 | 22 | #include <net/netlink.h> |
@@ -4639,6 +4641,114 @@ static int check_cfg(struct bpf_verifier_env *env) |
4639 | 4641 | return ret; |
4640 | 4642 | } |
4641 | 4643 |
|
| 4644 | +/* The minimum supported BTF func info size */ |
| 4645 | +#define MIN_BPF_FUNCINFO_SIZE 8 |
| 4646 | +#define MAX_FUNCINFO_REC_SIZE 252 |
| 4647 | + |
| 4648 | +static int check_btf_func(struct bpf_prog *prog, struct bpf_verifier_env *env, |
| 4649 | + union bpf_attr *attr, union bpf_attr __user *uattr) |
| 4650 | +{ |
| 4651 | + u32 i, nfuncs, urec_size, min_size, prev_offset; |
| 4652 | + u32 krec_size = sizeof(struct bpf_func_info); |
| 4653 | + struct bpf_func_info krecord = {}; |
| 4654 | + const struct btf_type *type; |
| 4655 | + void __user *urecord; |
| 4656 | + struct btf *btf; |
| 4657 | + int ret = 0; |
| 4658 | + |
| 4659 | + nfuncs = attr->func_info_cnt; |
| 4660 | + if (!nfuncs) |
| 4661 | + return 0; |
| 4662 | + |
| 4663 | + if (nfuncs != env->subprog_cnt) { |
| 4664 | + verbose(env, "number of funcs in func_info doesn't match number of subprogs\n"); |
| 4665 | + return -EINVAL; |
| 4666 | + } |
| 4667 | + |
| 4668 | + urec_size = attr->func_info_rec_size; |
| 4669 | + if (urec_size < MIN_BPF_FUNCINFO_SIZE || |
| 4670 | + urec_size > MAX_FUNCINFO_REC_SIZE || |
| 4671 | + urec_size % sizeof(u32)) { |
| 4672 | + verbose(env, "invalid func info rec size %u\n", urec_size); |
| 4673 | + return -EINVAL; |
| 4674 | + } |
| 4675 | + |
| 4676 | + btf = btf_get_by_fd(attr->prog_btf_fd); |
| 4677 | + if (IS_ERR(btf)) { |
| 4678 | + verbose(env, "unable to get btf from fd\n"); |
| 4679 | + return PTR_ERR(btf); |
| 4680 | + } |
| 4681 | + |
| 4682 | + urecord = u64_to_user_ptr(attr->func_info); |
| 4683 | + min_size = min_t(u32, krec_size, urec_size); |
| 4684 | + |
| 4685 | + for (i = 0; i < nfuncs; i++) { |
| 4686 | + ret = bpf_check_uarg_tail_zero(urecord, krec_size, urec_size); |
| 4687 | + if (ret) { |
| 4688 | + if (ret == -E2BIG) { |
| 4689 | + verbose(env, "nonzero tailing record in func info"); |
| 4690 | + /* set the size kernel expects so loader can zero |
| 4691 | + * out the rest of the record. |
| 4692 | + */ |
| 4693 | + if (put_user(min_size, &uattr->func_info_rec_size)) |
| 4694 | + ret = -EFAULT; |
| 4695 | + } |
| 4696 | + goto free_btf; |
| 4697 | + } |
| 4698 | + |
| 4699 | + if (copy_from_user(&krecord, urecord, min_size)) { |
| 4700 | + ret = -EFAULT; |
| 4701 | + goto free_btf; |
| 4702 | + } |
| 4703 | + |
| 4704 | + /* check insn_offset */ |
| 4705 | + if (i == 0) { |
| 4706 | + if (krecord.insn_offset) { |
| 4707 | + verbose(env, |
| 4708 | + "nonzero insn_offset %u for the first func info record", |
| 4709 | + krecord.insn_offset); |
| 4710 | + ret = -EINVAL; |
| 4711 | + goto free_btf; |
| 4712 | + } |
| 4713 | + } else if (krecord.insn_offset <= prev_offset) { |
| 4714 | + verbose(env, |
| 4715 | + "same or smaller insn offset (%u) than previous func info record (%u)", |
| 4716 | + krecord.insn_offset, prev_offset); |
| 4717 | + ret = -EINVAL; |
| 4718 | + goto free_btf; |
| 4719 | + } |
| 4720 | + |
| 4721 | + if (env->subprog_info[i].start != krecord.insn_offset) { |
| 4722 | + verbose(env, "func_info BTF section doesn't match subprog layout in BPF program\n"); |
| 4723 | + ret = -EINVAL; |
| 4724 | + goto free_btf; |
| 4725 | + } |
| 4726 | + |
| 4727 | + /* check type_id */ |
| 4728 | + type = btf_type_by_id(btf, krecord.type_id); |
| 4729 | + if (!type || BTF_INFO_KIND(type->info) != BTF_KIND_FUNC) { |
| 4730 | + verbose(env, "invalid type id %d in func info", |
| 4731 | + krecord.type_id); |
| 4732 | + ret = -EINVAL; |
| 4733 | + goto free_btf; |
| 4734 | + } |
| 4735 | + |
| 4736 | + if (i == 0) |
| 4737 | + prog->aux->type_id = krecord.type_id; |
| 4738 | + env->subprog_info[i].type_id = krecord.type_id; |
| 4739 | + |
| 4740 | + prev_offset = krecord.insn_offset; |
| 4741 | + urecord += urec_size; |
| 4742 | + } |
| 4743 | + |
| 4744 | + prog->aux->btf = btf; |
| 4745 | + return 0; |
| 4746 | + |
| 4747 | +free_btf: |
| 4748 | + btf_put(btf); |
| 4749 | + return ret; |
| 4750 | +} |
| 4751 | + |
4642 | 4752 | /* check %cur's range satisfies %old's */ |
4643 | 4753 | static bool range_within(struct bpf_reg_state *old, |
4644 | 4754 | struct bpf_reg_state *cur) |
@@ -5939,6 +6049,9 @@ static int jit_subprogs(struct bpf_verifier_env *env) |
5939 | 6049 | func[i]->aux->name[0] = 'F'; |
5940 | 6050 | func[i]->aux->stack_depth = env->subprog_info[i].stack_depth; |
5941 | 6051 | func[i]->jit_requested = 1; |
| 6052 | + /* the btf will be freed only at prog->aux */ |
| 6053 | + func[i]->aux->btf = prog->aux->btf; |
| 6054 | + func[i]->aux->type_id = env->subprog_info[i].type_id; |
5942 | 6055 | func[i] = bpf_int_jit_compile(func[i]); |
5943 | 6056 | if (!func[i]->jited) { |
5944 | 6057 | err = -ENOTSUPP; |
@@ -6325,7 +6438,8 @@ static void free_states(struct bpf_verifier_env *env) |
6325 | 6438 | kfree(env->explored_states); |
6326 | 6439 | } |
6327 | 6440 |
|
6328 | | -int bpf_check(struct bpf_prog **prog, union bpf_attr *attr) |
| 6441 | +int bpf_check(struct bpf_prog **prog, union bpf_attr *attr, |
| 6442 | + union bpf_attr __user *uattr) |
6329 | 6443 | { |
6330 | 6444 | struct bpf_verifier_env *env; |
6331 | 6445 | struct bpf_verifier_log *log; |
@@ -6397,6 +6511,10 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr) |
6397 | 6511 | if (ret < 0) |
6398 | 6512 | goto skip_full_check; |
6399 | 6513 |
|
| 6514 | + ret = check_btf_func(env->prog, env, attr, uattr); |
| 6515 | + if (ret < 0) |
| 6516 | + goto skip_full_check; |
| 6517 | + |
6400 | 6518 | ret = do_check(env); |
6401 | 6519 | if (env->cur_state) { |
6402 | 6520 | free_verifier_state(env->cur_state, true); |
|
0 commit comments