u-boot/lib/lzma/LzmaTools.c
Simon Glass f6eec89fa3 lzma: fix buffer bound check error further
Commit 4d3b8a0d fixed a problem with lzma decompress where it would
run out of bytes to decompress. The algorithm needs to know how many
uncompressed bytes it is expected to produce.

However, the fix introduced a potential buffer overrun, and causes
the compression test to fail (test_compression command in sandbox).

The correct fix seems to be to use the minimum of the expected number
of uncompressed bytes and the amount of output space available. That
way things work normally when there is enough space, and return an
error (without overrunning available space) when there is not.

Signed-off-by: Antonios Vamporakis <ant@area128.com>
CC: Kees Cook <keescook@chromium.org>
CC: Simon Glass <sjg@chromium.org>
CC: Daniel Schwierzeck <daniel.schwierzeck@gmail.com>
CC: Luka Perkov <luka@openwrt.org>

Signed-off-by: Simon Glass <sjg@chromium.org>
2015-01-14 11:35:43 -05:00

125 lines
3.5 KiB
C

/*
* Usefuls routines based on the LzmaTest.c file from LZMA SDK 4.65
*
* Copyright (C) 2007-2009 Industrie Dial Face S.p.A.
* Luigi 'Comio' Mantellini (luigi.mantellini@idf-hit.com)
*
* Copyright (C) 1999-2005 Igor Pavlov
*
* SPDX-License-Identifier: GPL-2.0+
*/
/*
* LZMA_Alone stream format:
*
* uchar Properties[5]
* uint64 Uncompressed size
* uchar data[*]
*
*/
#include <config.h>
#include <common.h>
#include <watchdog.h>
#ifdef CONFIG_LZMA
#define LZMA_PROPERTIES_OFFSET 0
#define LZMA_SIZE_OFFSET LZMA_PROPS_SIZE
#define LZMA_DATA_OFFSET LZMA_SIZE_OFFSET+sizeof(uint64_t)
#include "LzmaTools.h"
#include "LzmaDec.h"
#include <linux/string.h>
#include <malloc.h>
static void *SzAlloc(void *p, size_t size) { return malloc(size); }
static void SzFree(void *p, void *address) { free(address); }
int lzmaBuffToBuffDecompress (unsigned char *outStream, SizeT *uncompressedSize,
unsigned char *inStream, SizeT length)
{
int res = SZ_ERROR_DATA;
int i;
ISzAlloc g_Alloc;
SizeT outSizeFull = 0xFFFFFFFF; /* 4GBytes limit */
SizeT outProcessed;
SizeT outSize;
SizeT outSizeHigh;
ELzmaStatus state;
SizeT compressedSize = (SizeT)(length - LZMA_PROPS_SIZE);
debug ("LZMA: Image address............... 0x%p\n", inStream);
debug ("LZMA: Properties address.......... 0x%p\n", inStream + LZMA_PROPERTIES_OFFSET);
debug ("LZMA: Uncompressed size address... 0x%p\n", inStream + LZMA_SIZE_OFFSET);
debug ("LZMA: Compressed data address..... 0x%p\n", inStream + LZMA_DATA_OFFSET);
debug ("LZMA: Destination address......... 0x%p\n", outStream);
memset(&state, 0, sizeof(state));
outSize = 0;
outSizeHigh = 0;
/* Read the uncompressed size */
for (i = 0; i < 8; i++) {
unsigned char b = inStream[LZMA_SIZE_OFFSET + i];
if (i < 4) {
outSize += (UInt32)(b) << (i * 8);
} else {
outSizeHigh += (UInt32)(b) << ((i - 4) * 8);
}
}
outSizeFull = (SizeT)outSize;
if (sizeof(SizeT) >= 8) {
/*
* SizeT is a 64 bit uint => We can manage files larger than 4GB!
*
*/
outSizeFull |= (((SizeT)outSizeHigh << 16) << 16);
} else if (outSizeHigh != 0 || (UInt32)(SizeT)outSize != outSize) {
/*
* SizeT is a 32 bit uint => We cannot manage files larger than
* 4GB! Assume however that all 0xf values is "unknown size" and
* not actually a file of 2^64 bits.
*
*/
if (outSizeHigh != (SizeT)-1 || outSize != (SizeT)-1) {
debug ("LZMA: 64bit support not enabled.\n");
return SZ_ERROR_DATA;
}
}
debug("LZMA: Uncompresed size............ 0x%zx\n", outSizeFull);
debug("LZMA: Compresed size.............. 0x%zx\n", compressedSize);
g_Alloc.Alloc = SzAlloc;
g_Alloc.Free = SzFree;
/* Short-circuit early if we know the buffer can't hold the results. */
if (outSizeFull != (SizeT)-1 && *uncompressedSize < outSizeFull)
return SZ_ERROR_OUTPUT_EOF;
/* Decompress */
outProcessed = min(outSizeFull, *uncompressedSize);
WATCHDOG_RESET();
res = LzmaDecode(
outStream, &outProcessed,
inStream + LZMA_DATA_OFFSET, &compressedSize,
inStream, LZMA_PROPS_SIZE, LZMA_FINISH_END, &state, &g_Alloc);
*uncompressedSize = outProcessed;
debug("LZMA: Uncompressed ............... 0x%zx\n", outProcessed);
if (res != SZ_OK) {
return res;
}
return res;
}
#endif