Compare commits

...

16 commits

Author SHA1 Message Date
15608c066c tinf_inflate_block_data: Don't read outside of 'dest' buffer
.. given specially crafted input data, it was possible for lzOff to
point outside (specifically, before) the destination buffer.

this condition is possibly not quite stringent enough
2018-05-08 20:19:32 -05:00
5d7d08ab98 tgunzip: properly initialize destStart 2018-05-08 20:19:31 -05:00
b170fcb3c2 tinf_decode_trees: Fix out of bound access to 'lengths'
Many access to the lengths[] array could be out-of-range.  Guard
them all, even though I only have verified fuzzer cases for a subset of
them.
2018-05-08 20:19:31 -05:00
ce0721cb46 tinf_inflate_block_data: Don't write outside of destination buffer
Because this write to d->dest is not via TPUT, the check has to be
made manually.
2018-05-08 20:19:31 -05:00
6de173f708 tinf_inflate_block_data: Don't read past the end of length_bits[] or dist_bits[] arrays
tinflate.c:363:39: runtime error: index 30 out of bounds for type 'const unsigned char [30]'
tinflate.c:367:34: runtime error: index 30 out of bounds for type 'const unsigned char [30]'
2018-05-08 20:19:31 -05:00
f573bf7b97 tgunzip: don't crash on an impossibly small file 2018-05-08 20:19:31 -05:00
63459024ac uzlib_uncompress: propagate error from tinf_decode_trees 2018-05-08 20:19:31 -05:00
15ca6cf4a2 tinf_decode_symbol: don't traverse past end of t->trans 2018-05-08 20:19:31 -05:00
87e7ddbbb8 tinf_decode_symbol: don't traverse past end of t->table 2018-05-08 20:19:31 -05:00
f0159a0aea tinf_decode_trees: Prepare tinf_decode_trees to return error value
tinf_decode_trees can fail, specifically when it
calls tinf_decode_symbol.
2018-05-08 20:19:31 -05:00
0eb5744a29 TINF_PUT: don't write past the end of the destination buffer
.. instead returning TINF_DATA_ERROR in this case.
2018-05-08 20:19:31 -05:00
51bb07008b tinflate, tinfgzip: Don't read past the end of the input buffer 2018-05-08 20:18:34 -05:00
991ea7e770 tgunzip.c: explicitly give end of input and output buffers 2018-05-08 07:34:09 -05:00
b59b1bb361 tinf.h: add new fields for explicit end of source and destination buffers 2018-05-08 07:34:00 -05:00
84964fccdc tinf.h: remove unused 'destRemaining' field 2018-05-08 07:27:38 -05:00
0fd97e2dd3 tinf_get_le_uint32: Don't invoke undefined behavior left shifting
clang ubsan says:
tinflate.c:186:44: runtime error: left shift of 223 by 24 places cannot be represented in type 'int'

this shift is defined for uint32_t, but is undefined behavior for int32_t.
2018-05-08 07:27:22 -05:00
3 changed files with 41 additions and 8 deletions

View file

@ -87,6 +87,8 @@ int main(int argc, char *argv[])
fclose(fin);
if(len < 4) exit_error("impossibly small file");
/* -- get decompressed length -- */
dlen = source[len - 1];
@ -104,6 +106,7 @@ int main(int argc, char *argv[])
TINF_DATA d;
d.source = source;
d.esource = source + len;
res = uzlib_gzip_parse_header(&d);
if (res != TINF_OK) {
@ -114,7 +117,8 @@ int main(int argc, char *argv[])
// uzlib_uncompress_init(&d, malloc(32768), 32768);
uzlib_uncompress_init(&d, NULL, 0);
d.dest = dest;
d.dest = d.destStart = dest;
d.edest = dest + dlen;
/* decompress byte by byte; can be any other length */
d.destSize = 1;

View file

@ -49,6 +49,8 @@ typedef struct {
struct TINF_DATA;
typedef struct TINF_DATA {
const unsigned char *source;
/* end of source buffer */
const unsigned char *esource;
/* If source above is NULL, this function will be used to read
next byte from source stream */
unsigned char (*readSource)(struct TINF_DATA *data);
@ -62,12 +64,13 @@ typedef struct TINF_DATA {
unsigned int destSize;
/* Current pointer in buffer */
unsigned char *dest;
/* Remaining bytes in buffer */
unsigned int destRemaining;
/* end of destination buffer */
unsigned char *edest;
/* Accumulating checksum */
unsigned int checksum;
char checksum_type;
char eof;
int btype;
int bfinal;
@ -83,6 +86,7 @@ typedef struct TINF_DATA {
#define TINF_PUT(d, c) \
{ \
if(d->dest >= d->edest) return TINF_DATA_ERROR; \
*d->dest++ = c; \
if (d->dict_ring) { d->dict_ring[d->dict_idx++] = c; if (d->dict_idx == d->dict_size) d->dict_idx = 0; } \
}

View file

@ -172,6 +172,7 @@ static void tinf_build_tree(TINF_TREE *t, const unsigned char *lengths, unsigned
unsigned char uzlib_get_byte(TINF_DATA *d)
{
if (d->source) {
if(d->source >= d->esource) { d->eof = 1; return 0; }
return *d->source++;
}
return d->readSource(d);
@ -182,7 +183,7 @@ uint32_t tinf_get_le_uint32(TINF_DATA *d)
uint32_t val = 0;
int i;
for (i = 4; i--;) {
val = val >> 8 | uzlib_get_byte(d) << 24;
val = val >> 8 | ((uint32_t)uzlib_get_byte(d)) << 24;
}
return val;
}
@ -245,20 +246,23 @@ static int tinf_decode_symbol(TINF_DATA *d, TINF_TREE *t)
cur = 2*cur + tinf_getbit(d);
++len;
++len; if(len == 16) return TINF_DATA_ERROR;
sum += t->table[len];
cur -= t->table[len];
} while (cur >= 0);
return t->trans[sum + cur];
sum += cur;
if(sum < 0 || sum >= 288) return TINF_DATA_ERROR;
return t->trans[sum];
}
/* given a data stream, decode dynamic trees from it */
static void tinf_decode_trees(TINF_DATA *d, TINF_TREE *lt, TINF_TREE *dt)
static int tinf_decode_trees(TINF_DATA *d, TINF_TREE *lt, TINF_TREE *dt)
{
unsigned char lengths[288+32];
#define CHECK_INDEX(i) do { int tmp = (i); if(tmp < 0 || tmp >= 288+32) return TINF_DATA_ERROR; } while(0)
unsigned int hlit, hdist, hclen;
unsigned int i, num, length;
@ -279,6 +283,7 @@ static void tinf_decode_trees(TINF_DATA *d, TINF_TREE *lt, TINF_TREE *dt)
/* get 3 bits code length (0-7) */
unsigned int clen = tinf_read_bits(d, 3, 0);
// clcidx[i] are statically acceptable lengths
lengths[clcidx[i]] = clen;
}
@ -289,15 +294,18 @@ static void tinf_decode_trees(TINF_DATA *d, TINF_TREE *lt, TINF_TREE *dt)
for (num = 0; num < hlit + hdist; )
{
int sym = tinf_decode_symbol(d, lt);
if(sym < 0) return sym; // i.e., TINF_DATA_ERROR
switch (sym)
{
case 16:
/* copy previous code length 3-6 times (read 2 bits) */
{
CHECK_INDEX(num - 1);
unsigned char prev = lengths[num - 1];
for (length = tinf_read_bits(d, 2, 3); length; --length)
{
CHECK_INDEX(num);
lengths[num++] = prev;
}
}
@ -306,6 +314,7 @@ static void tinf_decode_trees(TINF_DATA *d, TINF_TREE *lt, TINF_TREE *dt)
/* repeat code length 0 for 3-10 times (read 3 bits) */
for (length = tinf_read_bits(d, 3, 3); length; --length)
{
CHECK_INDEX(num);
lengths[num++] = 0;
}
break;
@ -313,11 +322,13 @@ static void tinf_decode_trees(TINF_DATA *d, TINF_TREE *lt, TINF_TREE *dt)
/* repeat code length 0 for 11-138 times (read 7 bits) */
for (length = tinf_read_bits(d, 7, 11); length; --length)
{
CHECK_INDEX(num);
lengths[num++] = 0;
}
break;
default:
/* values 0-15 represent the actual code lengths */
CHECK_INDEX(num);
lengths[num++] = sym;
break;
}
@ -326,6 +337,7 @@ static void tinf_decode_trees(TINF_DATA *d, TINF_TREE *lt, TINF_TREE *dt)
/* build dynamic trees */
tinf_build_tree(lt, lengths, hlit);
tinf_build_tree(dt, lengths + hlit, hdist);
return TINF_OK;
}
/* ----------------------------- *
@ -339,6 +351,8 @@ static int tinf_inflate_block_data(TINF_DATA *d, TINF_TREE *lt, TINF_TREE *dt)
unsigned int offs;
int dist;
int sym = tinf_decode_symbol(d, lt);
if(d->eof) return TINF_DATA_ERROR;
//printf("huff sym: %02x\n", sym);
/* literal byte */
@ -355,9 +369,11 @@ static int tinf_inflate_block_data(TINF_DATA *d, TINF_TREE *lt, TINF_TREE *dt)
/* substring from sliding dictionary */
sym -= 257;
/* possibly get more bits from length code */
if(sym < 0 || sym >= 30) return TINF_DATA_ERROR;
d->curlen = tinf_read_bits(d, length_bits[sym], length_base[sym]);
dist = tinf_decode_symbol(d, dt);
if(dist < 0 || dist >= 30) return TINF_DATA_ERROR;
/* possibly get more bits from distance code */
offs = tinf_read_bits(d, dist_bits[dist], dist_base[dist]);
if (d->dict_ring) {
@ -371,6 +387,7 @@ static int tinf_inflate_block_data(TINF_DATA *d, TINF_TREE *lt, TINF_TREE *dt)
} else {
d->lzOff = -offs;
}
if(d->eof) return TINF_DATA_ERROR;
}
/* copy next byte from dict substring */
@ -380,6 +397,11 @@ static int tinf_inflate_block_data(TINF_DATA *d, TINF_TREE *lt, TINF_TREE *dt)
d->lzOff = 0;
}
} else {
if(d->dest >= d->edest) return TINF_DATA_ERROR;
if(d->lzOff >= 0) return TINF_DATA_ERROR;
// d->dest + d->lzOff >= d->destStart but without undefined behavior due to constructing a pointer to before the d->dest
// subtract d->dest from both sides
if(d->lzOff < d->destStart - d->dest) return TINF_DATA_ERROR;
d->dest[0] = d->dest[d->lzOff];
d->dest++;
}
@ -468,7 +490,8 @@ next_blk:
tinf_build_fixed_trees(&d->ltree, &d->dtree);
} else if (d->btype == 2) {
/* decode trees from stream */
tinf_decode_trees(d, &d->ltree, &d->dtree);
res = tinf_decode_trees(d, &d->ltree, &d->dtree);
if(res != TINF_OK) return res;
}
}
@ -501,6 +524,7 @@ next_blk:
} while (--d->destSize);
if (d->eof) return TINF_DATA_ERROR;
return TINF_OK;
}
@ -547,5 +571,6 @@ int uzlib_uncompress_chksum(TINF_DATA *d)
}
}
if (d->eof) res = TINF_DATA_ERROR;
return res;
}