diff options
author | David Oberhollenzer <david.oberhollenzer@sigma-star.at> | 2019-12-12 11:41:50 +0100 |
---|---|---|
committer | David Oberhollenzer <david.oberhollenzer@sigma-star.at> | 2019-12-12 11:45:05 +0100 |
commit | 40075fe0751a06c3373f53f36d44a27e79c2cca7 (patch) | |
tree | 229a1cc6f8fd41a9d000df64211022ae8628cd5e | |
parent | bcb73d076b043c34ab88265c2bdddaff8b702cc1 (diff) |
Fix out of bounds writes in lzo compressor
The liblzo compression functions don't do any bounds checking,
instead they expect the destination buffer to be large enough
to hold the worst case encoding.
This commit modifies the lzo compressor to use a scratch buffer
for compression (with worst case size) and only copy to the actual
destination if the result fits.
Signed-off-by: David Oberhollenzer <david.oberhollenzer@sigma-star.at>
-rw-r--r-- | lib/common/comp_lzo.c | 42 |
1 files changed, 35 insertions, 7 deletions
diff --git a/lib/common/comp_lzo.c b/lib/common/comp_lzo.c index cf5a362..f371fb6 100644 --- a/lib/common/comp_lzo.c +++ b/lib/common/comp_lzo.c @@ -13,6 +13,8 @@ #include <lzo/lzo1x.h> +#define LZO_MAX_SIZE(size) (size + (size / 16) + 64 + 3) + #define LZO_NUM_ALGS (sizeof(lzo_algs) / sizeof(lzo_algs[0])) typedef int (*lzo_cb_t)(const lzo_bytep src, lzo_uint src_len, lzo_bytep dst, @@ -49,6 +51,9 @@ typedef struct { int algorithm; int level; + size_t buf_size; + size_t work_size; + sqfs_u8 buffer[]; } lzo_compressor_t; @@ -128,27 +133,33 @@ static sqfs_s32 lzo_comp_block(sqfs_compressor_t *base, const sqfs_u8 *in, sqfs_u32 size, sqfs_u8 *out, sqfs_u32 outsize) { lzo_compressor_t *lzo = (lzo_compressor_t *)base; - lzo_uint len = outsize; + void *scratch; + lzo_uint len; int ret; if (size >= 0x7FFFFFFF) return 0; + scratch = lzo->buffer + lzo->work_size; + len = lzo->buf_size - lzo->work_size; + if (lzo->algorithm == SQFS_LZO1X_999 && lzo->level != SQFS_LZO_DEFAULT_LEVEL) { - ret = lzo1x_999_compress_level(in, size, out, &len, + ret = lzo1x_999_compress_level(in, size, scratch, &len, lzo->buffer, NULL, 0, 0, lzo->level); } else { - ret = lzo_algs[lzo->algorithm].compress(in, size, out, + ret = lzo_algs[lzo->algorithm].compress(in, size, scratch, &len, lzo->buffer); } if (ret != LZO_E_OK) return SQFS_ERROR_COMPRESSOR; - if (len < size) + if (len < size && len <= outsize) { + memcpy(out, scratch, len); return len; + } return 0; } @@ -176,7 +187,7 @@ static sqfs_compressor_t *lzo_create_copy(sqfs_compressor_t *cmp) lzo_compressor_t *other = (lzo_compressor_t *)cmp; lzo_compressor_t *lzo; - lzo = calloc(1, sizeof(*lzo) + lzo_algs[other->algorithm].bufsize); + lzo = calloc(1, sizeof(*lzo) + other->buf_size); if (lzo == NULL) return NULL; @@ -193,6 +204,7 @@ sqfs_compressor_t *lzo_compressor_create(const sqfs_compressor_config_t *cfg) { sqfs_compressor_t *base; lzo_compressor_t *lzo; + size_t scratch_size; if (cfg->flags & ~SQFS_COMP_FLAG_GENERIC_ALL) return NULL; @@ -209,8 +221,22 @@ sqfs_compressor_t *lzo_compressor_create(const sqfs_compressor_config_t *cfg) return NULL; } - lzo = calloc(1, - sizeof(*lzo) + lzo_algs[cfg->opt.lzo.algorithm].bufsize); + /* XXX: liblzo does not do bounds checking internally, + we need our own internal scratch buffer at worst case size... */ + if (cfg->flags & SQFS_COMP_FLAG_UNCOMPRESS) { + scratch_size = 0; + } else { + scratch_size = cfg->block_size; + if (scratch_size < SQFS_META_BLOCK_SIZE) + scratch_size = SQFS_META_BLOCK_SIZE; + + scratch_size = LZO_MAX_SIZE(scratch_size); + } + + /* ...in addition to the LZO work space buffer of course */ + scratch_size += lzo_algs[cfg->opt.lzo.algorithm].bufsize; + + lzo = calloc(1, sizeof(*lzo) + scratch_size); base = (sqfs_compressor_t *)lzo; if (lzo == NULL) @@ -218,6 +244,8 @@ sqfs_compressor_t *lzo_compressor_create(const sqfs_compressor_config_t *cfg) lzo->algorithm = cfg->opt.lzo.algorithm; lzo->level = cfg->opt.lzo.level; + lzo->buf_size = scratch_size; + lzo->work_size = lzo_algs[cfg->opt.lzo.algorithm].bufsize; base->destroy = lzo_destroy; base->do_block = (cfg->flags & SQFS_COMP_FLAG_UNCOMPRESS) ? |