From: Guy Harris Date: Wed, 17 Oct 2018 20:29:19 +0000 (-0700) Subject: Plug some memory leaks. X-Git-Tag: libpcap-1.10-bp~768 X-Git-Url: https://git.tcpdump.org/libpcap/commitdiff_plain/947f2be1e0345bbd6f66f6c945ad51eb7f074e8a Plug some memory leaks. 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. --- diff --git a/gencode.c b/gencode.c index 7aa96387..2b18a327 100644 --- 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 */ } diff --git a/gencode.h b/gencode.h index e97e90fe..a8199002 100644 --- 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); diff --git a/optimize.c b/optimize.c index 0d2fcca2..2258a3c9 100644 --- a/optimize.c +++ b/optimize.c @@ -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".