]> The Tcpdump Group git mirrors - libpcap/commitdiff
Plug some memory leaks.
authorGuy Harris <[email protected]>
Wed, 17 Oct 2018 20:29:19 +0000 (13:29 -0700)
committerGuy Harris <[email protected]>
Wed, 17 Oct 2018 20:29:19 +0000 (13:29 -0700)
The optimizer and code emitter (icode_to_fcode()) allocate their own
memory that must be cleaned up if an error is thrown, so we can't use
bpf_error(), we need to add our own error routines for them.

In some cases, just free up the memory before calling bpf_error().

Credit to OSS-Fuzz for finding this issue.

gencode.c
gencode.h
optimize.c

index 7aa9638746d8dc4e603798756213fe714b4df2ab..2b18a327650ba1cabb187d36918adfa01931fb04 100644 (file)
--- a/gencode.c
+++ b/gencode.c
@@ -433,6 +433,25 @@ bpf_parser_error(compiler_state_t *cstate, const char *msg)
        /* NOTREACHED */
 }
 
+/*
+ * For use by the optimizer, which needs to do its *own* cleanup before
+ * delivering a longjmp-based exception.
+ */
+void
+bpf_vset_error(compiler_state_t *cstate, const char *fmt, va_list ap)
+{
+       if (cstate->bpf_pcap != NULL)
+               (void)pcap_vsnprintf(pcap_geterr(cstate->bpf_pcap),
+                   PCAP_ERRBUF_SIZE, fmt, ap);
+}
+
+void PCAP_NORETURN
+bpf_abort_compilation(compiler_state_t *cstate)
+{
+       longjmp(cstate->top_ctx, 1);
+       /* NOTREACHED */
+}
+
 /* VARARGS */
 void PCAP_NORETURN
 bpf_error(compiler_state_t *cstate, const char *fmt, ...)
@@ -440,11 +459,9 @@ bpf_error(compiler_state_t *cstate, const char *fmt, ...)
        va_list ap;
 
        va_start(ap, fmt);
-       if (cstate->bpf_pcap != NULL)
-               (void)pcap_vsnprintf(pcap_geterr(cstate->bpf_pcap),
-                   PCAP_ERRBUF_SIZE, fmt, ap);
+       bpf_vset_error(cstate, fmt, ap);
        va_end(ap);
-       longjmp(cstate->top_ctx, 1);
+       bpf_abort_compilation(cstate);
        /* NOTREACHED */
 }
 
index e97e90fee4e19fab221a3253211fae53ef7879a2..a81990026e908b300648349f6942144890b0efad 100644 (file)
--- a/gencode.h
+++ b/gencode.h
@@ -384,8 +384,11 @@ struct icode {
        int cur_mark;
 };
 
-void bpf_optimize(compiler_state_t *, struct icode *ic);
+void bpf_optimize(compiler_state_t *, struct icode *);
 void PCAP_NORETURN bpf_parser_error(compiler_state_t *, const char *);
+void bpf_vset_error(compiler_state_t *, const char *, va_list)
+    PCAP_PRINTFLIKE(2, 0);
+void PCAP_NORETURN bpf_abort_compilation(compiler_state_t *);
 void PCAP_NORETURN bpf_error(compiler_state_t *, const char *, ...)
     PCAP_PRINTFLIKE(2, 3);
 
index 0d2fcca28d566bc8e707bf133ca60442b1a20b79..2258a3c9dcca0256d83a434d8fa4188bae0709b4 100644 (file)
@@ -323,6 +323,8 @@ typedef struct {
 
 static void opt_init(compiler_state_t *, opt_state_t *, struct icode *);
 static void opt_cleanup(opt_state_t *);
+static void PCAP_NORETURN opt_error(compiler_state_t *, opt_state_t *, const char *, ...)
+    PCAP_PRINTFLIKE(3, 4);
 
 static void intern_blocks(opt_state_t *, struct icode *);
 
@@ -722,13 +724,13 @@ fold_op(compiler_state_t *cstate, opt_state_t *opt_state,
 
        case BPF_DIV:
                if (b == 0)
-                       bpf_error(cstate, "division by zero");
+                       opt_error(cstate, opt_state, "division by zero");
                a /= b;
                break;
 
        case BPF_MOD:
                if (b == 0)
-                       bpf_error(cstate, "modulus by zero");
+                       opt_error(cstate, opt_state, "modulus by zero");
                a %= b;
                break;
 
@@ -1971,6 +1973,22 @@ opt_cleanup(opt_state_t *opt_state)
        free((void *)opt_state->blocks);
 }
 
+/*
+ * Like bpf_error(), but also cleans up the optimizer state.
+ */
+static void PCAP_NORETURN
+opt_error(compiler_state_t *cstate, opt_state_t *opt_state, const char *fmt, ...)
+{
+       va_list ap;
+
+       opt_cleanup(opt_state);
+       va_start(ap, fmt);
+       bpf_vset_error(cstate, fmt, ap);
+       va_end(ap);
+       bpf_abort_compilation(cstate);
+       /* NOTREACHED */
+}
+
 /*
  * Return the number of stmts in 's'.
  */
@@ -2075,15 +2093,20 @@ opt_init(compiler_state_t *cstate, opt_state_t *opt_state, struct icode *ic)
 
        opt_state->n_edges = 2 * opt_state->n_blocks;
        opt_state->edges = (struct edge **)calloc(opt_state->n_edges, sizeof(*opt_state->edges));
-       if (opt_state->edges == NULL)
+       if (opt_state->edges == NULL) {
+               free(opt_state->blocks);
                bpf_error(cstate, "malloc");
+       }
 
        /*
         * The number of levels is bounded by the number of nodes.
         */
        opt_state->levels = (struct block **)calloc(opt_state->n_blocks, sizeof(*opt_state->levels));
-       if (opt_state->levels == NULL)
+       if (opt_state->levels == NULL) {
+               free(opt_state->edges);
+               free(opt_state->blocks);
                bpf_error(cstate, "malloc");
+       }
 
        opt_state->edgewords = opt_state->n_edges / (8 * sizeof(bpf_u_int32)) + 1;
        opt_state->nodewords = opt_state->n_blocks / (8 * sizeof(bpf_u_int32)) + 1;
@@ -2091,8 +2114,12 @@ opt_init(compiler_state_t *cstate, opt_state_t *opt_state, struct icode *ic)
        /* XXX */
        opt_state->space = (bpf_u_int32 *)malloc(2 * opt_state->n_blocks * opt_state->nodewords * sizeof(*opt_state->space)
                                 + opt_state->n_edges * opt_state->edgewords * sizeof(*opt_state->space));
-       if (opt_state->space == NULL)
+       if (opt_state->space == NULL) {
+               free(opt_state->levels);
+               free(opt_state->edges);
+               free(opt_state->blocks);
                bpf_error(cstate, "malloc");
+       }
        p = opt_state->space;
        opt_state->all_dom_sets = p;
        for (i = 0; i < n; ++i) {
@@ -2129,9 +2156,22 @@ opt_init(compiler_state_t *cstate, opt_state_t *opt_state, struct icode *ic)
         */
        opt_state->maxval = 3 * max_stmts;
        opt_state->vmap = (struct vmapinfo *)calloc(opt_state->maxval, sizeof(*opt_state->vmap));
+       if (opt_state->vmap == NULL) {
+               free(opt_state->space);
+               free(opt_state->levels);
+               free(opt_state->edges);
+               free(opt_state->blocks);
+               bpf_error(cstate, "malloc");
+       }
        opt_state->vnode_base = (struct valnode *)calloc(opt_state->maxval, sizeof(*opt_state->vnode_base));
-       if (opt_state->vmap == NULL || opt_state->vnode_base == NULL)
+       if (opt_state->vnode_base == NULL) {
+               free(opt_state->vmap);
+               free(opt_state->space);
+               free(opt_state->levels);
+               free(opt_state->edges);
+               free(opt_state->blocks);
                bpf_error(cstate, "malloc");
+       }
 }
 
 /*
@@ -2143,6 +2183,9 @@ opt_init(compiler_state_t *cstate, opt_state_t *opt_state, struct icode *ic)
 int bids[NBIDS];
 #endif
 
+static void PCAP_NORETURN conv_error(compiler_state_t *, conv_state_t *, const char *, ...)
+    PCAP_PRINTFLIKE(3, 4);
+
 /*
  * Returns true if successful.  Returns false if a branch has
  * an offset that is too large.  If so, we have marked that
@@ -2179,7 +2222,7 @@ convert_code_r(compiler_state_t *cstate, conv_state_t *conv_state,
        if (slen) {
                offset = (struct slist **)calloc(slen, sizeof(struct slist *));
                if (!offset) {
-                       bpf_error(cstate, "not enough core");
+                       conv_error(cstate, conv_state, "not enough core");
                        /*NOTREACHED*/
                }
        }
@@ -2203,7 +2246,8 @@ convert_code_r(compiler_state_t *cstate, conv_state_t *conv_state,
                if (BPF_CLASS(src->s.code) != BPF_JMP || src->s.code == (BPF_JMP|BPF_JA)) {
 #if 0
                        if (src->s.jt || src->s.jf) {
-                               bpf_error(cstate, "illegal jmp destination");
+                               free(offset);
+                               conv_error(cstate, conv_state, "illegal jmp destination");
                                /*NOTREACHED*/
                        }
 #endif
@@ -2223,7 +2267,8 @@ convert_code_r(compiler_state_t *cstate, conv_state_t *conv_state,
 #endif
 
                if (!src->s.jt || !src->s.jf) {
-                       bpf_error(cstate, ljerr, "no jmp destination", off);
+                       free(offset);
+                       conv_error(cstate, conv_state, ljerr, "no jmp destination", off);
                        /*NOTREACHED*/
                }
 
@@ -2231,12 +2276,14 @@ convert_code_r(compiler_state_t *cstate, conv_state_t *conv_state,
                for (i = 0; i < slen; i++) {
                        if (offset[i] == src->s.jt) {
                                if (jt) {
-                                       bpf_error(cstate, ljerr, "multiple matches", off);
+                                       free(offset);
+                                       conv_error(cstate, conv_state, ljerr, "multiple matches", off);
                                        /*NOTREACHED*/
                                }
 
                                if (i - off - 1 >= 256) {
-                                       bpf_error(cstate, ljerr, "out-of-range jump", off);
+                                       free(offset);
+                                       conv_error(cstate, conv_state, ljerr, "out-of-range jump", off);
                                        /*NOTREACHED*/
                                }
                                dst->jt = (u_char)(i - off - 1);
@@ -2244,11 +2291,13 @@ convert_code_r(compiler_state_t *cstate, conv_state_t *conv_state,
                        }
                        if (offset[i] == src->s.jf) {
                                if (jf) {
-                                       bpf_error(cstate, ljerr, "multiple matches", off);
+                                       free(offset);
+                                       conv_error(cstate, conv_state, ljerr, "multiple matches", off);
                                        /*NOTREACHED*/
                                }
                                if (i - off - 1 >= 256) {
-                                       bpf_error(cstate, ljerr, "out-of-range jump", off);
+                                       free(offset);
+                                       conv_error(cstate, conv_state, ljerr, "out-of-range jump", off);
                                        /*NOTREACHED*/
                                }
                                dst->jf = (u_char)(i - off - 1);
@@ -2256,7 +2305,8 @@ convert_code_r(compiler_state_t *cstate, conv_state_t *conv_state,
                        }
                }
                if (!jt || !jf) {
-                       bpf_error(cstate, ljerr, "no destination found", off);
+                       free(offset);
+                       conv_error(cstate, conv_state, ljerr, "no destination found", off);
                        /*NOTREACHED*/
                }
            }
@@ -2285,7 +2335,7 @@ filled:
                    }
                    /* branch if T to following jump */
                    if (extrajmps >= 256) {
-                       bpf_error(cstate, "too many extra jumps");
+                       conv_error(cstate, conv_state, "too many extra jumps");
                        /*NOTREACHED*/
                    }
                    dst->jt = (u_char)extrajmps;
@@ -2306,7 +2356,7 @@ filled:
                    /* branch if F to following jump */
                    /* if two jumps are inserted, F goes to second one */
                    if (extrajmps >= 256) {
-                       bpf_error(cstate, "too many extra jumps");
+                       conv_error(cstate, conv_state, "too many extra jumps");
                        /*NOTREACHED*/
                    }
                    dst->jf = (u_char)extrajmps;
@@ -2371,6 +2421,23 @@ icode_to_fcode(compiler_state_t *cstate, struct icode *ic,
        return fp;
 }
 
+/*
+ * Like bpf_error(), but also frees the array into which we're putting
+ * the generated BPF code.
+ */
+static void PCAP_NORETURN
+conv_error(compiler_state_t *cstate, conv_state_t *conv_state, const char *fmt, ...)
+{
+       va_list ap;
+
+       free(conv_state->fstart);
+       va_start(ap, fmt);
+       bpf_vset_error(cstate, fmt, ap);
+       va_end(ap);
+       bpf_abort_compilation(cstate);
+       /* NOTREACHED */
+}
+
 /*
  * Make a copy of a BPF program and put it in the "fcode" member of
  * a "pcap_t".