aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Oberhollenzer <david.oberhollenzer@sigma-star.at>2019-12-12 11:41:50 +0100
committerDavid Oberhollenzer <david.oberhollenzer@sigma-star.at>2019-12-12 11:45:05 +0100
commit40075fe0751a06c3373f53f36d44a27e79c2cca7 (patch)
tree229a1cc6f8fd41a9d000df64211022ae8628cd5e
parentbcb73d076b043c34ab88265c2bdddaff8b702cc1 (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.c42
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) ?