bzip2: fix two crashes on corrupted archives
As it turns out, longjmp'ing into freed stack is not healthy... function old new delta unpack_usage_messages - 97 +97 unpack_bz2_stream 369 409 +40 get_next_block 1667 1677 +10 get_bits 156 155 -1 start_bunzip 212 183 -29 bb_show_usage 181 120 -61 ------------------------------------------------------------------------------ (add/remove: 1/0 grow/shrink: 2/3 up/down: 147/-91) Total: 56 bytes Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
This commit is contained in:
parent
8e2174e9bd
commit
38ccd6af8a
@ -100,7 +100,7 @@ struct bunzip_data {
|
|||||||
unsigned dbufSize;
|
unsigned dbufSize;
|
||||||
|
|
||||||
/* For I/O error handling */
|
/* For I/O error handling */
|
||||||
jmp_buf jmpbuf;
|
jmp_buf *jmpbuf;
|
||||||
|
|
||||||
/* Big things go last (register-relative addressing can be larger for big offsets) */
|
/* Big things go last (register-relative addressing can be larger for big offsets) */
|
||||||
uint32_t crc32Table[256];
|
uint32_t crc32Table[256];
|
||||||
@ -127,7 +127,7 @@ static unsigned get_bits(bunzip_data *bd, int bits_wanted)
|
|||||||
/* if "no input fd" case: in_fd == -1, read fails, we jump */
|
/* if "no input fd" case: in_fd == -1, read fails, we jump */
|
||||||
bd->inbufCount = read(bd->in_fd, bd->inbuf, IOBUF_SIZE);
|
bd->inbufCount = read(bd->in_fd, bd->inbuf, IOBUF_SIZE);
|
||||||
if (bd->inbufCount <= 0)
|
if (bd->inbufCount <= 0)
|
||||||
longjmp(bd->jmpbuf, RETVAL_UNEXPECTED_INPUT_EOF);
|
longjmp(*bd->jmpbuf, RETVAL_UNEXPECTED_INPUT_EOF);
|
||||||
bd->inbufPos = 0;
|
bd->inbufPos = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -151,12 +151,12 @@ static unsigned get_bits(bunzip_data *bd, int bits_wanted)
|
|||||||
|
|
||||||
return bits;
|
return bits;
|
||||||
}
|
}
|
||||||
|
//#define get_bits(bd, n) (dbg("%d:get_bits()", __LINE__), get_bits(bd, n))
|
||||||
|
|
||||||
/* Unpacks the next block and sets up for the inverse Burrows-Wheeler step. */
|
/* Unpacks the next block and sets up for the inverse Burrows-Wheeler step. */
|
||||||
static int get_next_block(bunzip_data *bd)
|
static int get_next_block(bunzip_data *bd)
|
||||||
{
|
{
|
||||||
struct group_data *hufGroup;
|
int groupCount, selector,
|
||||||
int groupCount, *base, *limit, selector,
|
|
||||||
i, j, symCount, symTotal, nSelectors, byteCount[256];
|
i, j, symCount, symTotal, nSelectors, byteCount[256];
|
||||||
uint8_t uc, symToByte[256], mtfSymbol[256], *selectors;
|
uint8_t uc, symToByte[256], mtfSymbol[256], *selectors;
|
||||||
uint32_t *dbuf;
|
uint32_t *dbuf;
|
||||||
@ -179,15 +179,19 @@ static int get_next_block(bunzip_data *bd)
|
|||||||
i = get_bits(bd, 24);
|
i = get_bits(bd, 24);
|
||||||
j = get_bits(bd, 24);
|
j = get_bits(bd, 24);
|
||||||
bd->headerCRC = get_bits(bd, 32);
|
bd->headerCRC = get_bits(bd, 32);
|
||||||
if ((i == 0x177245) && (j == 0x385090)) return RETVAL_LAST_BLOCK;
|
if ((i == 0x177245) && (j == 0x385090))
|
||||||
if ((i != 0x314159) || (j != 0x265359)) return RETVAL_NOT_BZIP_DATA;
|
return RETVAL_LAST_BLOCK;
|
||||||
|
if ((i != 0x314159) || (j != 0x265359))
|
||||||
|
return RETVAL_NOT_BZIP_DATA;
|
||||||
|
|
||||||
/* We can add support for blockRandomised if anybody complains. There was
|
/* We can add support for blockRandomised if anybody complains. There was
|
||||||
some code for this in busybox 1.0.0-pre3, but nobody ever noticed that
|
some code for this in busybox 1.0.0-pre3, but nobody ever noticed that
|
||||||
it didn't actually work. */
|
it didn't actually work. */
|
||||||
if (get_bits(bd, 1)) return RETVAL_OBSOLETE_INPUT;
|
if (get_bits(bd, 1))
|
||||||
|
return RETVAL_OBSOLETE_INPUT;
|
||||||
origPtr = get_bits(bd, 24);
|
origPtr = get_bits(bd, 24);
|
||||||
if (origPtr > bd->dbufSize) return RETVAL_DATA_ERROR;
|
if (origPtr > bd->dbufSize)
|
||||||
|
return RETVAL_DATA_ERROR;
|
||||||
|
|
||||||
/* mapping table: if some byte values are never used (encoding things
|
/* mapping table: if some byte values are never used (encoding things
|
||||||
like ascii text), the compression code removes the gaps to have fewer
|
like ascii text), the compression code removes the gaps to have fewer
|
||||||
@ -231,13 +235,21 @@ static int get_next_block(bunzip_data *bd)
|
|||||||
/* Get next value */
|
/* Get next value */
|
||||||
int n = 0;
|
int n = 0;
|
||||||
while (get_bits(bd, 1)) {
|
while (get_bits(bd, 1)) {
|
||||||
if (n >= groupCount) return RETVAL_DATA_ERROR;
|
if (n >= groupCount)
|
||||||
|
return RETVAL_DATA_ERROR;
|
||||||
n++;
|
n++;
|
||||||
}
|
}
|
||||||
/* Decode MTF to get the next selector */
|
/* Decode MTF to get the next selector */
|
||||||
tmp_byte = mtfSymbol[n];
|
tmp_byte = mtfSymbol[n];
|
||||||
while (--n >= 0)
|
while (--n >= 0)
|
||||||
mtfSymbol[n + 1] = mtfSymbol[n];
|
mtfSymbol[n + 1] = mtfSymbol[n];
|
||||||
|
//We catch it later, in the second loop where we use selectors[i].
|
||||||
|
//Maybe this is a better place, though?
|
||||||
|
// if (tmp_byte >= groupCount) {
|
||||||
|
// dbg("%d: selectors[%d]:%d groupCount:%d",
|
||||||
|
// __LINE__, i, tmp_byte, groupCount);
|
||||||
|
// return RETVAL_DATA_ERROR;
|
||||||
|
// }
|
||||||
mtfSymbol[0] = selectors[i] = tmp_byte;
|
mtfSymbol[0] = selectors[i] = tmp_byte;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -248,6 +260,8 @@ static int get_next_block(bunzip_data *bd)
|
|||||||
uint8_t length[MAX_SYMBOLS];
|
uint8_t length[MAX_SYMBOLS];
|
||||||
/* 8 bits is ALMOST enough for temp[], see below */
|
/* 8 bits is ALMOST enough for temp[], see below */
|
||||||
unsigned temp[MAX_HUFCODE_BITS+1];
|
unsigned temp[MAX_HUFCODE_BITS+1];
|
||||||
|
struct group_data *hufGroup;
|
||||||
|
int *base, *limit;
|
||||||
int minLen, maxLen, pp, len_m1;
|
int minLen, maxLen, pp, len_m1;
|
||||||
|
|
||||||
/* Read Huffman code lengths for each symbol. They're stored in
|
/* Read Huffman code lengths for each symbol. They're stored in
|
||||||
@ -283,8 +297,10 @@ static int get_next_block(bunzip_data *bd)
|
|||||||
/* Find largest and smallest lengths in this group */
|
/* Find largest and smallest lengths in this group */
|
||||||
minLen = maxLen = length[0];
|
minLen = maxLen = length[0];
|
||||||
for (i = 1; i < symCount; i++) {
|
for (i = 1; i < symCount; i++) {
|
||||||
if (length[i] > maxLen) maxLen = length[i];
|
if (length[i] > maxLen)
|
||||||
else if (length[i] < minLen) minLen = length[i];
|
maxLen = length[i];
|
||||||
|
else if (length[i] < minLen)
|
||||||
|
minLen = length[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Calculate permute[], base[], and limit[] tables from length[].
|
/* Calculate permute[], base[], and limit[] tables from length[].
|
||||||
@ -320,7 +336,8 @@ static int get_next_block(bunzip_data *bd)
|
|||||||
/* Count symbols coded for at each bit length */
|
/* Count symbols coded for at each bit length */
|
||||||
/* NB: in pathological cases, temp[8] can end ip being 256.
|
/* NB: in pathological cases, temp[8] can end ip being 256.
|
||||||
* That's why uint8_t is too small for temp[]. */
|
* That's why uint8_t is too small for temp[]. */
|
||||||
for (i = 0; i < symCount; i++) temp[length[i]]++;
|
for (i = 0; i < symCount; i++)
|
||||||
|
temp[length[i]]++;
|
||||||
|
|
||||||
/* Calculate limit[] (the largest symbol-coding value at each bit
|
/* Calculate limit[] (the largest symbol-coding value at each bit
|
||||||
* length, which is (previous limit<<1)+symbols at this level), and
|
* length, which is (previous limit<<1)+symbols at this level), and
|
||||||
@ -363,12 +380,22 @@ static int get_next_block(bunzip_data *bd)
|
|||||||
|
|
||||||
runPos = dbufCount = selector = 0;
|
runPos = dbufCount = selector = 0;
|
||||||
for (;;) {
|
for (;;) {
|
||||||
|
struct group_data *hufGroup;
|
||||||
|
int *base, *limit;
|
||||||
int nextSym;
|
int nextSym;
|
||||||
|
uint8_t ngrp;
|
||||||
|
|
||||||
/* Fetch next Huffman coding group from list. */
|
/* Fetch next Huffman coding group from list. */
|
||||||
symCount = GROUP_SIZE - 1;
|
symCount = GROUP_SIZE - 1;
|
||||||
if (selector >= nSelectors) return RETVAL_DATA_ERROR;
|
if (selector >= nSelectors)
|
||||||
hufGroup = bd->groups + selectors[selector++];
|
return RETVAL_DATA_ERROR;
|
||||||
|
ngrp = selectors[selector++];
|
||||||
|
if (ngrp >= groupCount) {
|
||||||
|
dbg("%d selectors[%d]:%d groupCount:%d",
|
||||||
|
__LINE__, selector-1, ngrp, groupCount);
|
||||||
|
return RETVAL_DATA_ERROR;
|
||||||
|
}
|
||||||
|
hufGroup = bd->groups + ngrp;
|
||||||
base = hufGroup->base - 1;
|
base = hufGroup->base - 1;
|
||||||
limit = hufGroup->limit - 1;
|
limit = hufGroup->limit - 1;
|
||||||
|
|
||||||
@ -403,7 +430,8 @@ static int get_next_block(bunzip_data *bd)
|
|||||||
}
|
}
|
||||||
/* Figure how many bits are in next symbol and unget extras */
|
/* Figure how many bits are in next symbol and unget extras */
|
||||||
i = hufGroup->minLen;
|
i = hufGroup->minLen;
|
||||||
while (nextSym > limit[i]) ++i;
|
while (nextSym > limit[i])
|
||||||
|
++i;
|
||||||
j = hufGroup->maxLen - i;
|
j = hufGroup->maxLen - i;
|
||||||
if (j < 0)
|
if (j < 0)
|
||||||
return RETVAL_DATA_ERROR;
|
return RETVAL_DATA_ERROR;
|
||||||
@ -671,7 +699,10 @@ int FAST_FUNC read_bunzip(bunzip_data *bd, char *outbuf, int len)
|
|||||||
/* Because bunzip2 is used for help text unpacking, and because bb_show_usage()
|
/* Because bunzip2 is used for help text unpacking, and because bb_show_usage()
|
||||||
should work for NOFORK applets too, we must be extremely careful to not leak
|
should work for NOFORK applets too, we must be extremely careful to not leak
|
||||||
any allocations! */
|
any allocations! */
|
||||||
int FAST_FUNC start_bunzip(bunzip_data **bdp, int in_fd,
|
int FAST_FUNC start_bunzip(
|
||||||
|
void *jmpbuf,
|
||||||
|
bunzip_data **bdp,
|
||||||
|
int in_fd,
|
||||||
const void *inbuf, int len)
|
const void *inbuf, int len)
|
||||||
{
|
{
|
||||||
bunzip_data *bd;
|
bunzip_data *bd;
|
||||||
@ -683,11 +714,14 @@ int FAST_FUNC start_bunzip(bunzip_data **bdp, int in_fd,
|
|||||||
|
|
||||||
/* Figure out how much data to allocate */
|
/* Figure out how much data to allocate */
|
||||||
i = sizeof(bunzip_data);
|
i = sizeof(bunzip_data);
|
||||||
if (in_fd != -1) i += IOBUF_SIZE;
|
if (in_fd != -1)
|
||||||
|
i += IOBUF_SIZE;
|
||||||
|
|
||||||
/* Allocate bunzip_data. Most fields initialize to zero. */
|
/* Allocate bunzip_data. Most fields initialize to zero. */
|
||||||
bd = *bdp = xzalloc(i);
|
bd = *bdp = xzalloc(i);
|
||||||
|
|
||||||
|
bd->jmpbuf = jmpbuf;
|
||||||
|
|
||||||
/* Setup input buffer */
|
/* Setup input buffer */
|
||||||
bd->in_fd = in_fd;
|
bd->in_fd = in_fd;
|
||||||
if (-1 == in_fd) {
|
if (-1 == in_fd) {
|
||||||
@ -702,10 +736,6 @@ int FAST_FUNC start_bunzip(bunzip_data **bdp, int in_fd,
|
|||||||
/* Init the CRC32 table (big endian) */
|
/* Init the CRC32 table (big endian) */
|
||||||
crc32_filltable(bd->crc32Table, 1);
|
crc32_filltable(bd->crc32Table, 1);
|
||||||
|
|
||||||
/* Setup for I/O error handling via longjmp */
|
|
||||||
i = setjmp(bd->jmpbuf);
|
|
||||||
if (i) return i;
|
|
||||||
|
|
||||||
/* Ensure that file starts with "BZh['1'-'9']." */
|
/* Ensure that file starts with "BZh['1'-'9']." */
|
||||||
/* Update: now caller verifies 1st two bytes, makes .gz/.bz2
|
/* Update: now caller verifies 1st two bytes, makes .gz/.bz2
|
||||||
* integration easier */
|
* integration easier */
|
||||||
@ -752,8 +782,12 @@ unpack_bz2_stream(transformer_state_t *xstate)
|
|||||||
outbuf = xmalloc(IOBUF_SIZE);
|
outbuf = xmalloc(IOBUF_SIZE);
|
||||||
len = 0;
|
len = 0;
|
||||||
while (1) { /* "Process one BZ... stream" loop */
|
while (1) { /* "Process one BZ... stream" loop */
|
||||||
|
jmp_buf jmpbuf;
|
||||||
|
|
||||||
i = start_bunzip(&bd, xstate->src_fd, outbuf + 2, len);
|
/* Setup for I/O error handling via longjmp */
|
||||||
|
i = setjmp(jmpbuf);
|
||||||
|
if (i == 0)
|
||||||
|
i = start_bunzip(&jmpbuf, &bd, xstate->src_fd, outbuf + 2, len);
|
||||||
|
|
||||||
if (i == 0) {
|
if (i == 0) {
|
||||||
while (1) { /* "Produce some output bytes" loop */
|
while (1) { /* "Produce some output bytes" loop */
|
||||||
|
@ -32,7 +32,6 @@
|
|||||||
*
|
*
|
||||||
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
|
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
|
||||||
*/
|
*/
|
||||||
#include <setjmp.h>
|
|
||||||
#include "libbb.h"
|
#include "libbb.h"
|
||||||
#include "bb_archive.h"
|
#include "bb_archive.h"
|
||||||
|
|
||||||
|
@ -76,7 +76,6 @@
|
|||||||
//usage: "1\n"
|
//usage: "1\n"
|
||||||
|
|
||||||
#include "libbb.h"
|
#include "libbb.h"
|
||||||
#include <setjmp.h>
|
|
||||||
|
|
||||||
/* This is a NOFORK applet. Be very careful! */
|
/* This is a NOFORK applet. Be very careful! */
|
||||||
|
|
||||||
|
@ -210,7 +210,7 @@ const llist_t *find_list_entry2(const llist_t *list, const char *filename) FAST_
|
|||||||
|
|
||||||
/* A bit of bunzip2 internals are exposed for compressed help support: */
|
/* A bit of bunzip2 internals are exposed for compressed help support: */
|
||||||
typedef struct bunzip_data bunzip_data;
|
typedef struct bunzip_data bunzip_data;
|
||||||
int start_bunzip(bunzip_data **bdp, int in_fd, const void *inbuf, int len) FAST_FUNC;
|
int start_bunzip(void *, bunzip_data **bdp, int in_fd, const void *inbuf, int len) FAST_FUNC;
|
||||||
/* NB: read_bunzip returns < 0 on error, or the number of *unfilled* bytes
|
/* NB: read_bunzip returns < 0 on error, or the number of *unfilled* bytes
|
||||||
* in outbuf. IOW: on EOF returns len ("all bytes are not filled"), not 0: */
|
* in outbuf. IOW: on EOF returns len ("all bytes are not filled"), not 0: */
|
||||||
int read_bunzip(bunzip_data *bd, char *outbuf, int len) FAST_FUNC;
|
int read_bunzip(bunzip_data *bd, char *outbuf, int len) FAST_FUNC;
|
||||||
|
@ -102,14 +102,21 @@ static const char *unpack_usage_messages(void)
|
|||||||
char *outbuf = NULL;
|
char *outbuf = NULL;
|
||||||
bunzip_data *bd;
|
bunzip_data *bd;
|
||||||
int i;
|
int i;
|
||||||
|
jmp_buf jmpbuf;
|
||||||
|
|
||||||
i = start_bunzip(&bd,
|
/* Setup for I/O error handling via longjmp */
|
||||||
|
i = setjmp(jmpbuf);
|
||||||
|
if (i == 0) {
|
||||||
|
i = start_bunzip(&jmpbuf,
|
||||||
|
&bd,
|
||||||
/* src_fd: */ -1,
|
/* src_fd: */ -1,
|
||||||
/* inbuf: */ packed_usage,
|
/* inbuf: */ packed_usage,
|
||||||
/* len: */ sizeof(packed_usage));
|
/* len: */ sizeof(packed_usage)
|
||||||
/* read_bunzip can longjmp to start_bunzip, and ultimately
|
);
|
||||||
* end up here with i != 0 on read data errors! Not trivial */
|
}
|
||||||
if (!i) {
|
/* read_bunzip can longjmp and end up here with i != 0
|
||||||
|
* on read data errors! Not trivial */
|
||||||
|
if (i == 0) {
|
||||||
/* Cannot use xmalloc: will leak bd in NOFORK case! */
|
/* Cannot use xmalloc: will leak bd in NOFORK case! */
|
||||||
outbuf = malloc_or_warn(sizeof(UNPACKED_USAGE));
|
outbuf = malloc_or_warn(sizeof(UNPACKED_USAGE));
|
||||||
if (outbuf)
|
if (outbuf)
|
||||||
|
@ -44,13 +44,22 @@ int bbconfig_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
|
|||||||
{
|
{
|
||||||
#if ENABLE_FEATURE_COMPRESS_BBCONFIG
|
#if ENABLE_FEATURE_COMPRESS_BBCONFIG
|
||||||
bunzip_data *bd;
|
bunzip_data *bd;
|
||||||
int i = start_bunzip(&bd,
|
int i;
|
||||||
|
jmp_buf jmpbuf;
|
||||||
|
|
||||||
|
/* Setup for I/O error handling via longjmp */
|
||||||
|
i = setjmp(jmpbuf);
|
||||||
|
if (i == 0) {
|
||||||
|
i = start_bunzip(&jmpbuf,
|
||||||
|
&bd,
|
||||||
/* src_fd: */ -1,
|
/* src_fd: */ -1,
|
||||||
/* inbuf: */ bbconfig_config_bz2,
|
/* inbuf: */ bbconfig_config_bz2,
|
||||||
/* len: */ sizeof(bbconfig_config_bz2));
|
/* len: */ sizeof(bbconfig_config_bz2)
|
||||||
/* read_bunzip can longjmp to start_bunzip, and ultimately
|
);
|
||||||
* end up here with i != 0 on read data errors! Not trivial */
|
}
|
||||||
if (!i) {
|
/* read_bunzip can longjmp and end up here with i != 0
|
||||||
|
* on read data errors! Not trivial */
|
||||||
|
if (i == 0) {
|
||||||
/* Cannot use xmalloc: will leak bd in NOFORK case! */
|
/* Cannot use xmalloc: will leak bd in NOFORK case! */
|
||||||
char *outbuf = malloc_or_warn(sizeof(bbconfig_config));
|
char *outbuf = malloc_or_warn(sizeof(bbconfig_config));
|
||||||
if (outbuf) {
|
if (outbuf) {
|
||||||
|
@ -177,7 +177,6 @@
|
|||||||
|
|
||||||
#define JOBS ENABLE_ASH_JOB_CONTROL
|
#define JOBS ENABLE_ASH_JOB_CONTROL
|
||||||
|
|
||||||
#include <setjmp.h>
|
|
||||||
#include <fnmatch.h>
|
#include <fnmatch.h>
|
||||||
#include <sys/times.h>
|
#include <sys/times.h>
|
||||||
#include <sys/utsname.h> /* for setting $HOSTNAME */
|
#include <sys/utsname.h> /* for setting $HOSTNAME */
|
||||||
|
@ -552,6 +552,22 @@ if test "${0##*/}" = "bunzip2.tests"; then
|
|||||||
echo "FAIL: $unpack: pbzip_4m_zeros file"
|
echo "FAIL: $unpack: pbzip_4m_zeros file"
|
||||||
FAILCOUNT=$((FAILCOUNT + 1))
|
FAILCOUNT=$((FAILCOUNT + 1))
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
errout="`${bb}bunzip2 <bz2_issue_11.bz2 2>&1 >/dev/null`"
|
||||||
|
if test x"$errout:$?" = x"bunzip2: bunzip error -5:1"; then
|
||||||
|
echo "PASS: $unpack: bz2_issue_11.bz2 corrupted example"
|
||||||
|
else
|
||||||
|
echo "FAIL: $unpack: bz2_issue_11.bz2 corrupted example"
|
||||||
|
FAILCOUNT=$((FAILCOUNT + 1))
|
||||||
|
fi
|
||||||
|
|
||||||
|
errout="`${bb}bunzip2 <bz2_issue_12.bz2 2>&1 >/dev/null`"
|
||||||
|
if test x"$errout:$?" = x"bunzip2: bunzip error -3:1"; then
|
||||||
|
echo "PASS: $unpack: bz2_issue_12.bz2 corrupted example"
|
||||||
|
else
|
||||||
|
echo "FAIL: $unpack: bz2_issue_12.bz2 corrupted example"
|
||||||
|
FAILCOUNT=$((FAILCOUNT + 1))
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
exit $((FAILCOUNT <= 255 ? FAILCOUNT : 255))
|
exit $((FAILCOUNT <= 255 ? FAILCOUNT : 255))
|
||||||
|
BIN
testsuite/bz2_issue_11.bz2
Normal file
BIN
testsuite/bz2_issue_11.bz2
Normal file
Binary file not shown.
BIN
testsuite/bz2_issue_12.bz2
Normal file
BIN
testsuite/bz2_issue_12.bz2
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user