From 920bf21714f99cc20d3b8bcc28060136a390354a Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Fri, 4 Nov 2016 19:59:50 -0700 Subject: Fix LZ4_decompress_fast_continue() bug It specified the external dictionary location incorrectly. Add tests that expose this bug with both normal compilation and ASAN. --- lib/lz4.c | 2 +- tests/Makefile | 8 +++- tests/fasttest.c | 138 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 146 insertions(+), 2 deletions(-) create mode 100644 tests/fasttest.c diff --git a/lib/lz4.c b/lib/lz4.c index 6db4c0b..740ae64 100644 --- a/lib/lz4.c +++ b/lib/lz4.c @@ -1366,7 +1366,7 @@ int LZ4_decompress_fast_continue (LZ4_streamDecode_t* LZ4_streamDecode, const ch lz4sd->prefixEnd += originalSize; } else { lz4sd->extDictSize = lz4sd->prefixSize; - lz4sd->externalDict = (BYTE*)dest - lz4sd->extDictSize; + lz4sd->externalDict = lz4sd->prefixEnd - lz4sd->extDictSize; result = LZ4_decompress_generic(source, dest, 0, originalSize, endOnOutputSize, full, 0, usingExtDict, (BYTE*)dest, lz4sd->externalDict, lz4sd->extDictSize); diff --git a/tests/Makefile b/tests/Makefile index 0dd8a59..2da6408 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -97,6 +97,9 @@ frametest: $(LZ4DIR)/lz4frame.o $(LZ4DIR)/lz4.o $(LZ4DIR)/lz4hc.o $(LZ4DIR)/xxha frametest32: $(LZ4DIR)/lz4frame.c $(LZ4DIR)/lz4.c $(LZ4DIR)/lz4hc.c $(LZ4DIR)/xxhash.c frametest.c $(CC) -m32 $(FLAGS) $^ -o $@$(EXT) +fasttest: $(LZ4DIR)/lz4.o fasttest.c + $(CC) $(FLAGS) $^ -o $@$(EXT) + datagen : $(PRGDIR)/datagen.c datagencli.c $(CC) $(FLAGS) -I$(PRGDIR) $^ -o $@$(EXT) @@ -119,7 +122,7 @@ versionsTest: #FreeBSD targets ifneq (,$(filter $(shell uname),Linux Darwin GNU/kFreeBSD GNU FreeBSD)) -test: test-lz4 test-lz4c test-frametest test-fullbench test-fuzzer test-mem +test: test-lz4 test-lz4c test-fasttest test-frametest test-fullbench test-fuzzer test-mem test32: test-lz4c32 test-frametest32 test-fullbench32 test-fuzzer32 test-mem32 @@ -267,6 +270,9 @@ test-frametest: frametest test-frametest32: frametest32 ./frametest32 $(FUZZER_TIME) +test-fasttest: fasttest + ./fasttest + test-mem: lz4 datagen fuzzer frametest fullbench @echo "\n ---- valgrind tests : memory analyzer ----" valgrind --leak-check=yes --error-exitcode=1 ./datagen -g50M > $(VOID) diff --git a/tests/fasttest.c b/tests/fasttest.c new file mode 100644 index 0000000..a405542 --- /dev/null +++ b/tests/fasttest.c @@ -0,0 +1,138 @@ +/************************************** + * Compiler Options + **************************************/ +#ifdef _MSC_VER /* Visual Studio */ +# define _CRT_SECURE_NO_WARNINGS // for MSVC +# define snprintf sprintf_s +#endif +#ifdef __GNUC__ +# pragma GCC diagnostic ignored "-Wmissing-braces" /* GCC bug 53119 : doesn't accept { 0 } as initializer (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=53119) */ +#endif + + +/************************************** + * Includes + **************************************/ +#include +#include +#include +#include +#include "lz4.h" + + +/* Returns non-zero on failure. */ +int test_compress(const char *input, int inSize, char *output, int outSize) +{ + LZ4_stream_t lz4Stream_body = { 0 }; + LZ4_stream_t* lz4Stream = &lz4Stream_body; + + int inOffset = 0; + int outOffset = 0; + + if (inSize & 3) return -1; + + while (inOffset < inSize) { + const int length = inSize >> 2; + if (inSize > 1024) return -2; + if (outSize - (outOffset + 8) < LZ4_compressBound(length)) return -3; + { + const int outBytes = LZ4_compress_continue( + lz4Stream, input + inOffset, output + outOffset + 8, length); + if(outBytes <= 0) return -4; + memcpy(output + outOffset, &length, 4); /* input length */ + memcpy(output + outOffset + 4, &outBytes, 4); /* output length */ + inOffset += length; + outOffset += outBytes + 8; + } + } + if (outOffset + 8 > outSize) return -5; + memset(output + outOffset, 0, 4); + memset(output + outOffset + 4, 0, 4); + return 0; +} + +void swap(void **a, void **b) { + void *tmp = *a; + *a = *b; + *b = tmp; +} + +/* Returns non-zero on failure. Not a safe function. */ +int test_decompress(const char *uncompressed, const char *compressed) +{ + char outBufferA[1024]; + char spacing; /* So prefixEnd != dest */ + char outBufferB[1024]; + char *output = outBufferA; + char *lastOutput = outBufferB; + LZ4_streamDecode_t lz4StreamDecode_body = { 0 }; + LZ4_streamDecode_t* lz4StreamDecode = &lz4StreamDecode_body; + int offset = 0; + int unOffset = 0; + int lastBytes = 0; + + (void)spacing; + + for(;;) { + int32_t bytes; + int32_t unBytes; + /* Read uncompressed size and compressed size */ + memcpy(&unBytes, compressed + offset, 4); + memcpy(&bytes, compressed + offset + 4, 4); + offset += 8; + /* Check if we reached end of stream or error */ + if(bytes == 0 && unBytes == 0) return 0; + if(bytes <= 0 || unBytes <= 0 || unBytes > 1024) return 1; + + /* Put the last output in the dictionary */ + LZ4_setStreamDecode(lz4StreamDecode, lastOutput, lastBytes); + /* Decompress */ + bytes = LZ4_decompress_fast_continue( + lz4StreamDecode, compressed + offset, output, unBytes); + if(bytes <= 0) return 2; + /* Check result */ + { + int r = memcmp(uncompressed + unOffset, output, unBytes); + if (r) return 3; + } + swap((void**)&output, (void**)&lastOutput); + offset += bytes; + unOffset += unBytes; + lastBytes = unBytes; + } +} + + +int main(int argc, char **argv) +{ + char input[] = + "Hello Hello Hello Hello Hello Hello Hello Hello!" + "Hello Hello Hello Hello Hello Hello Hello Hello!" + "Hello Hello Hello Hello Hello Hello Hello Hello!" + "Hello Hello Hello Hello Hello Hello Hello Hello!" + "Hello Hello Hello Hello Hello Hello Hello Hello!" + "Hello Hello Hello Hello Hello Hello Hello Hello!" + "Hello Hello Hello Hello Hello Hello Hello Hello!" + "Hello Hello Hello Hello Hello Hello Hello Hello!" + "Hello Hello Hello Hello Hello Hello Hello Hello!" + "Hello Hello Hello Hello Hello Hello Hello Hello!" + "Hello Hello Hello Hello Hello Hello Hello Hello!" + "Hello Hello Hello Hello Hello Hello Hello Hello!" + "Hello Hello Hello Hello Hello Hello Hello Hello!" + "Hello Hello Hello Hello Hello Hello Hello Hello!" + "Hello Hello Hello Hello Hello Hello Hello Hello!" + "Hello Hello Hello Hello Hello Hello Hello Hello"; + char output[LZ4_COMPRESSBOUND(4096)]; + int r; + + (void)argc; + (void)argv; + + if ((r = test_compress(input, sizeof(input), output, sizeof(output)))) { + return r; + } + if ((r = test_decompress(input, output))) { + return r; + } + return 0; +} -- cgit v0.12