]> git.proxmox.com Git - mirror_ubuntu-hirsute-kernel.git/blobdiff - kernel/bpf/verifier.c
tipc: eliminate unnecessary probing
[mirror_ubuntu-hirsute-kernel.git] / kernel / bpf / verifier.c
index 5f26f7ad124f407ba5fe9d2d1f44c74de5344c3d..ab5aa549766650cdbbe6e42345ef90895f7d3e3b 100644 (file)
@@ -341,10 +341,12 @@ static int realloc_verifier_state(struct bpf_verifier_state *state, int size,
        return 0;
 }
 
-static void free_verifier_state(struct bpf_verifier_state *state)
+static void free_verifier_state(struct bpf_verifier_state *state,
+                               bool free_self)
 {
        kfree(state->stack);
-       kfree(state);
+       if (free_self)
+               kfree(state);
 }
 
 /* copy verifier state from src to dst growing dst stack space
@@ -382,6 +384,7 @@ static int pop_stack(struct bpf_verifier_env *env, int *prev_insn_idx,
        if (prev_insn_idx)
                *prev_insn_idx = head->prev_insn_idx;
        elem = head->next;
+       free_verifier_state(&head->st, false);
        kfree(head);
        env->head = elem;
        env->stack_size--;
@@ -399,14 +402,14 @@ static struct bpf_verifier_state *push_stack(struct bpf_verifier_env *env,
        if (!elem)
                goto err;
 
-       err = copy_verifier_state(&elem->st, cur);
-       if (err)
-               return NULL;
        elem->insn_idx = insn_idx;
        elem->prev_insn_idx = prev_insn_idx;
        elem->next = env->head;
        env->head = elem;
        env->stack_size++;
+       err = copy_verifier_state(&elem->st, cur);
+       if (err)
+               goto err;
        if (env->stack_size > BPF_COMPLEXITY_LIMIT_STACK) {
                verbose(env, "BPF program is too complex\n");
                goto err;
@@ -2529,7 +2532,7 @@ static void find_good_pkt_pointers(struct bpf_verifier_state *state,
                        continue;
                reg = &state->stack[i].spilled_ptr;
                if (reg->type == type && reg->id == dst_reg->id)
-                       reg->range = max_t(u16, reg->range, new_range);
+                       reg->range = max(reg->range, new_range);
        }
 }
 
@@ -2784,6 +2787,99 @@ static void mark_map_regs(struct bpf_verifier_state *state, u32 regno,
        }
 }
 
+static bool try_match_pkt_pointers(const struct bpf_insn *insn,
+                                  struct bpf_reg_state *dst_reg,
+                                  struct bpf_reg_state *src_reg,
+                                  struct bpf_verifier_state *this_branch,
+                                  struct bpf_verifier_state *other_branch)
+{
+       if (BPF_SRC(insn->code) != BPF_X)
+               return false;
+
+       switch (BPF_OP(insn->code)) {
+       case BPF_JGT:
+               if ((dst_reg->type == PTR_TO_PACKET &&
+                    src_reg->type == PTR_TO_PACKET_END) ||
+                   (dst_reg->type == PTR_TO_PACKET_META &&
+                    reg_is_init_pkt_pointer(src_reg, PTR_TO_PACKET))) {
+                       /* pkt_data' > pkt_end, pkt_meta' > pkt_data */
+                       find_good_pkt_pointers(this_branch, dst_reg,
+                                              dst_reg->type, false);
+               } else if ((dst_reg->type == PTR_TO_PACKET_END &&
+                           src_reg->type == PTR_TO_PACKET) ||
+                          (reg_is_init_pkt_pointer(dst_reg, PTR_TO_PACKET) &&
+                           src_reg->type == PTR_TO_PACKET_META)) {
+                       /* pkt_end > pkt_data', pkt_data > pkt_meta' */
+                       find_good_pkt_pointers(other_branch, src_reg,
+                                              src_reg->type, true);
+               } else {
+                       return false;
+               }
+               break;
+       case BPF_JLT:
+               if ((dst_reg->type == PTR_TO_PACKET &&
+                    src_reg->type == PTR_TO_PACKET_END) ||
+                   (dst_reg->type == PTR_TO_PACKET_META &&
+                    reg_is_init_pkt_pointer(src_reg, PTR_TO_PACKET))) {
+                       /* pkt_data' < pkt_end, pkt_meta' < pkt_data */
+                       find_good_pkt_pointers(other_branch, dst_reg,
+                                              dst_reg->type, true);
+               } else if ((dst_reg->type == PTR_TO_PACKET_END &&
+                           src_reg->type == PTR_TO_PACKET) ||
+                          (reg_is_init_pkt_pointer(dst_reg, PTR_TO_PACKET) &&
+                           src_reg->type == PTR_TO_PACKET_META)) {
+                       /* pkt_end < pkt_data', pkt_data > pkt_meta' */
+                       find_good_pkt_pointers(this_branch, src_reg,
+                                              src_reg->type, false);
+               } else {
+                       return false;
+               }
+               break;
+       case BPF_JGE:
+               if ((dst_reg->type == PTR_TO_PACKET &&
+                    src_reg->type == PTR_TO_PACKET_END) ||
+                   (dst_reg->type == PTR_TO_PACKET_META &&
+                    reg_is_init_pkt_pointer(src_reg, PTR_TO_PACKET))) {
+                       /* pkt_data' >= pkt_end, pkt_meta' >= pkt_data */
+                       find_good_pkt_pointers(this_branch, dst_reg,
+                                              dst_reg->type, true);
+               } else if ((dst_reg->type == PTR_TO_PACKET_END &&
+                           src_reg->type == PTR_TO_PACKET) ||
+                          (reg_is_init_pkt_pointer(dst_reg, PTR_TO_PACKET) &&
+                           src_reg->type == PTR_TO_PACKET_META)) {
+                       /* pkt_end >= pkt_data', pkt_data >= pkt_meta' */
+                       find_good_pkt_pointers(other_branch, src_reg,
+                                              src_reg->type, false);
+               } else {
+                       return false;
+               }
+               break;
+       case BPF_JLE:
+               if ((dst_reg->type == PTR_TO_PACKET &&
+                    src_reg->type == PTR_TO_PACKET_END) ||
+                   (dst_reg->type == PTR_TO_PACKET_META &&
+                    reg_is_init_pkt_pointer(src_reg, PTR_TO_PACKET))) {
+                       /* pkt_data' <= pkt_end, pkt_meta' <= pkt_data */
+                       find_good_pkt_pointers(other_branch, dst_reg,
+                                              dst_reg->type, false);
+               } else if ((dst_reg->type == PTR_TO_PACKET_END &&
+                           src_reg->type == PTR_TO_PACKET) ||
+                          (reg_is_init_pkt_pointer(dst_reg, PTR_TO_PACKET) &&
+                           src_reg->type == PTR_TO_PACKET_META)) {
+                       /* pkt_end <= pkt_data', pkt_data <= pkt_meta' */
+                       find_good_pkt_pointers(this_branch, src_reg,
+                                              src_reg->type, true);
+               } else {
+                       return false;
+               }
+               break;
+       default:
+               return false;
+       }
+
+       return true;
+}
+
 static int check_cond_jmp_op(struct bpf_verifier_env *env,
                             struct bpf_insn *insn, int *insn_idx)
 {
@@ -2890,75 +2986,9 @@ static int check_cond_jmp_op(struct bpf_verifier_env *env,
                 */
                mark_map_regs(this_branch, insn->dst_reg, opcode == BPF_JNE);
                mark_map_regs(other_branch, insn->dst_reg, opcode == BPF_JEQ);
-       } else if (BPF_SRC(insn->code) == BPF_X && opcode == BPF_JGT &&
-                  dst_reg->type == PTR_TO_PACKET &&
-                  regs[insn->src_reg].type == PTR_TO_PACKET_END) {
-               /* pkt_data' > pkt_end */
-               find_good_pkt_pointers(this_branch, dst_reg,
-                                      PTR_TO_PACKET, false);
-       } else if (BPF_SRC(insn->code) == BPF_X && opcode == BPF_JGT &&
-                  dst_reg->type == PTR_TO_PACKET_END &&
-                  regs[insn->src_reg].type == PTR_TO_PACKET) {
-               /* pkt_end > pkt_data' */
-               find_good_pkt_pointers(other_branch, &regs[insn->src_reg],
-                                      PTR_TO_PACKET, true);
-       } else if (BPF_SRC(insn->code) == BPF_X && opcode == BPF_JLT &&
-                  dst_reg->type == PTR_TO_PACKET &&
-                  regs[insn->src_reg].type == PTR_TO_PACKET_END) {
-               /* pkt_data' < pkt_end */
-               find_good_pkt_pointers(other_branch, dst_reg, PTR_TO_PACKET,
-                                      true);
-       } else if (BPF_SRC(insn->code) == BPF_X && opcode == BPF_JLT &&
-                  dst_reg->type == PTR_TO_PACKET_END &&
-                  regs[insn->src_reg].type == PTR_TO_PACKET) {
-               /* pkt_end < pkt_data' */
-               find_good_pkt_pointers(this_branch, &regs[insn->src_reg],
-                                      PTR_TO_PACKET, false);
-       } else if (BPF_SRC(insn->code) == BPF_X && opcode == BPF_JGE &&
-                  dst_reg->type == PTR_TO_PACKET &&
-                  regs[insn->src_reg].type == PTR_TO_PACKET_END) {
-               /* pkt_data' >= pkt_end */
-               find_good_pkt_pointers(this_branch, dst_reg,
-                                      PTR_TO_PACKET, true);
-       } else if (BPF_SRC(insn->code) == BPF_X && opcode == BPF_JGE &&
-                  dst_reg->type == PTR_TO_PACKET_END &&
-                  regs[insn->src_reg].type == PTR_TO_PACKET) {
-               /* pkt_end >= pkt_data' */
-               find_good_pkt_pointers(other_branch, &regs[insn->src_reg],
-                                      PTR_TO_PACKET, false);
-       } else if (BPF_SRC(insn->code) == BPF_X && opcode == BPF_JLE &&
-                  dst_reg->type == PTR_TO_PACKET &&
-                  regs[insn->src_reg].type == PTR_TO_PACKET_END) {
-               /* pkt_data' <= pkt_end */
-               find_good_pkt_pointers(other_branch, dst_reg,
-                                      PTR_TO_PACKET, false);
-       } else if (BPF_SRC(insn->code) == BPF_X && opcode == BPF_JLE &&
-                  dst_reg->type == PTR_TO_PACKET_END &&
-                  regs[insn->src_reg].type == PTR_TO_PACKET) {
-               /* pkt_end <= pkt_data' */
-               find_good_pkt_pointers(this_branch, &regs[insn->src_reg],
-                                      PTR_TO_PACKET, true);
-       } else if (BPF_SRC(insn->code) == BPF_X && opcode == BPF_JGT &&
-                  dst_reg->type == PTR_TO_PACKET_META &&
-                  reg_is_init_pkt_pointer(&regs[insn->src_reg], PTR_TO_PACKET)) {
-               find_good_pkt_pointers(this_branch, dst_reg,
-                                      PTR_TO_PACKET_META, false);
-       } else if (BPF_SRC(insn->code) == BPF_X && opcode == BPF_JLT &&
-                  dst_reg->type == PTR_TO_PACKET_META &&
-                  reg_is_init_pkt_pointer(&regs[insn->src_reg], PTR_TO_PACKET)) {
-               find_good_pkt_pointers(other_branch, dst_reg,
-                                      PTR_TO_PACKET_META, false);
-       } else if (BPF_SRC(insn->code) == BPF_X && opcode == BPF_JGE &&
-                  reg_is_init_pkt_pointer(dst_reg, PTR_TO_PACKET) &&
-                  regs[insn->src_reg].type == PTR_TO_PACKET_META) {
-               find_good_pkt_pointers(other_branch, &regs[insn->src_reg],
-                                      PTR_TO_PACKET_META, false);
-       } else if (BPF_SRC(insn->code) == BPF_X && opcode == BPF_JLE &&
-                  reg_is_init_pkt_pointer(dst_reg, PTR_TO_PACKET) &&
-                  regs[insn->src_reg].type == PTR_TO_PACKET_META) {
-               find_good_pkt_pointers(this_branch, &regs[insn->src_reg],
-                                      PTR_TO_PACKET_META, false);
-       } else if (is_pointer_value(env, insn->dst_reg)) {
+       } else if (!try_match_pkt_pointers(insn, dst_reg, &regs[insn->src_reg],
+                                          this_branch, other_branch) &&
+                  is_pointer_value(env, insn->dst_reg)) {
                verbose(env, "R%d pointer comparison prohibited\n",
                        insn->dst_reg);
                return -EACCES;
@@ -3641,7 +3671,7 @@ static int is_state_visited(struct bpf_verifier_env *env, int insn_idx)
        struct bpf_verifier_state_list *new_sl;
        struct bpf_verifier_state_list *sl;
        struct bpf_verifier_state *cur = env->cur_state;
-       int i;
+       int i, err;
 
        sl = env->explored_states[insn_idx];
        if (!sl)
@@ -3679,7 +3709,12 @@ static int is_state_visited(struct bpf_verifier_env *env, int insn_idx)
                return -ENOMEM;
 
        /* add new state to the head of linked list */
-       copy_verifier_state(&new_sl->state, cur);
+       err = copy_verifier_state(&new_sl->state, cur);
+       if (err) {
+               free_verifier_state(&new_sl->state, false);
+               kfree(new_sl);
+               return err;
+       }
        new_sl->next = env->explored_states[insn_idx];
        env->explored_states[insn_idx] = new_sl;
        /* connect new state to parentage chain */
@@ -4424,6 +4459,7 @@ static void free_states(struct bpf_verifier_env *env)
                if (sl)
                        while (sl != STATE_LIST_MARK) {
                                sln = sl->next;
+                               free_verifier_state(&sl->state, false);
                                kfree(sl);
                                sl = sln;
                        }
@@ -4438,6 +4474,10 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr)
        struct bpf_verifer_log *log;
        int ret = -EINVAL;
 
+       /* no program is valid */
+       if (ARRAY_SIZE(bpf_verifier_ops) == 0)
+               return -EINVAL;
+
        /* 'struct bpf_verifier_env' can be global, but since it's not small,
         * allocate/free it every time bpf_check() is called
         */
@@ -4494,7 +4534,7 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr)
        env->allow_ptr_leaks = capable(CAP_SYS_ADMIN);
 
        ret = do_check(env);
-       free_verifier_state(env->cur_state);
+       free_verifier_state(env->cur_state, true);
        env->cur_state = NULL;
 
 skip_full_check:
@@ -4552,8 +4592,10 @@ err_free_env:
 }
 
 static const struct bpf_verifier_ops * const bpf_analyzer_ops[] = {
+#ifdef CONFIG_NET
        [BPF_PROG_TYPE_XDP]             = &xdp_analyzer_ops,
        [BPF_PROG_TYPE_SCHED_CLS]       = &tc_cls_act_analyzer_ops,
+#endif
 };
 
 int bpf_analyzer(struct bpf_prog *prog, const struct bpf_ext_analyzer_ops *ops,
@@ -4601,7 +4643,7 @@ int bpf_analyzer(struct bpf_prog *prog, const struct bpf_ext_analyzer_ops *ops,
        env->allow_ptr_leaks = capable(CAP_SYS_ADMIN);
 
        ret = do_check(env);
-       free_verifier_state(env->cur_state);
+       free_verifier_state(env->cur_state, true);
        env->cur_state = NULL;
 
 skip_full_check: