bpf: fix verifier memory leaks
authorAlexei Starovoitov <ast@fb.com>
Wed, 3 Apr 2019 18:39:02 +0000 (18:39 +0000)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sat, 20 Apr 2019 07:15:08 +0000 (09:15 +0200)
commit 1969db47f8d0e800397abd4ee4e8d27d2b578587 upstream.

fix verifier memory leaks

Fixes: 638f5b90d460 ("bpf: reduce verifier memory consumption")
Signed-off-by: Alexei Starovoitov <ast@fb.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Balbir Singh <sblbir@amzn.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
kernel/bpf/verifier.c

index 805bc218150d95d749cf621d1b557ec72cb784b5..8293fc2f452a8e5a792292fd5a460df8ae722568 100644 (file)
@@ -491,10 +491,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
@@ -532,6 +534,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--;
@@ -549,14 +552,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("BPF program is too complex\n");
                goto err;
@@ -3812,7 +3815,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)
@@ -3850,7 +3853,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 */
@@ -4692,6 +4700,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;
                        }
@@ -4768,7 +4777,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:
@@ -4878,7 +4887,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: