libpcap/0195-Plug-some-memory-leaks.patch

320 lines
9.8 KiB
Diff
Raw Normal View History

2019-09-30 10:57:32 -04:00
From 947f2be1e0345bbd6f66f6c945ad51eb7f074e8a Mon Sep 17 00:00:00 2001
From: Guy Harris <guy@alum.mit.edu>
Date: Wed, 17 Oct 2018 13:29:19 -0700
Subject: [PATCH 195/470] 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.
---
gencode.c | 25 +++++++++++++---
gencode.h | 5 +++-
optimize.c | 99 ++++++++++++++++++++++++++++++++++++++++++++++++++++----------
3 files changed, 108 insertions(+), 21 deletions(-)
diff --git a/gencode.c b/gencode.c
index 7aa9638..2b18a32 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 e97e90f..a819900 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_syntax_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 0d2fcca..2258a3c 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;
@@ -1972,6 +1974,22 @@ opt_cleanup(opt_state_t *opt_state)
}
/*
+ * 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'.
*/
static u_int
@@ -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;
@@ -2372,6 +2422,23 @@ icode_to_fcode(compiler_state_t *cstate, struct icode *ic,
}
/*
+ * 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".
*
--
1.8.3.1