summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNick Terrell <terrelln@fb.com>2016-11-05 02:59:50 (GMT)
committerNick Terrell <terrelln@fb.com>2016-11-05 03:01:23 (GMT)
commit920bf21714f99cc20d3b8bcc28060136a390354a (patch)
tree6057917cbd5ac8599b22736ef2db83ad48ec9654
parent8195ba8f7bbf0153a1dc01d2ada8823a29462afa (diff)
downloadlz4-920bf21714f99cc20d3b8bcc28060136a390354a.zip
lz4-920bf21714f99cc20d3b8bcc28060136a390354a.tar.gz
lz4-920bf21714f99cc20d3b8bcc28060136a390354a.tar.bz2
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.
-rw-r--r--lib/lz4.c2
-rw-r--r--tests/Makefile8
-rw-r--r--tests/fasttest.c138
3 files changed, 146 insertions, 2 deletions
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 <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#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;
+}