From: Bhargava Shastry <bshas...@sect.tu-berlin.de>

This patch addresses the following comments by Lasse Collin:
  - Disable CRC check and add memory limit during decoder initialization
  - Check for LZMA_PROG_ERROR
  - Remove superflous comments
  - Use the flag "FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION" provided by
    oss-fuzz infra to disable debug prints but retain them for offline
(non-fuzzing debug) use.

---
 tests/ossfuzz/Makefile         |   4 +
 tests/ossfuzz/config/fuzz.dict |   2 +
 tests/ossfuzz/fuzz.c           | 162 +++++++++++++++++++++++++++++++++
 3 files changed, 168 insertions(+)
 create mode 100644 tests/ossfuzz/Makefile
 create mode 100644 tests/ossfuzz/config/fuzz.dict
 create mode 100644 tests/ossfuzz/fuzz.c

diff --git a/tests/ossfuzz/Makefile b/tests/ossfuzz/Makefile
new file mode 100644
index 0000000..242625b
--- /dev/null
+++ b/tests/ossfuzz/Makefile
@@ -0,0 +1,4 @@
+fuzz: fuzz.c
+       $(CC) $(CFLAGS) -c fuzz.c -I ../../src/liblzma/api/
+       $(CXX) $(CXXFLAGS) -lFuzzingEngine fuzz.o -o $(OUT)/fuzz \
+             ../../src/liblzma/.libs/liblzma.a 
diff --git a/tests/ossfuzz/config/fuzz.dict b/tests/ossfuzz/config/fuzz.dict
new file mode 100644
index 0000000..932d67c
--- /dev/null
+++ b/tests/ossfuzz/config/fuzz.dict
@@ -0,0 +1,2 @@
+"\xFD7zXZ\x00"
+"YZ"
diff --git a/tests/ossfuzz/fuzz.c b/tests/ossfuzz/fuzz.c
new file mode 100644
index 0000000..3479095
--- /dev/null
+++ b/tests/ossfuzz/fuzz.c
@@ -0,0 +1,162 @@
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+#include <lzma.h>
+
+static bool
+init_decoder(lzma_stream *strm)
+{
+    /* Initialize lzma stream decoder, setting a memory limit of 500 MB,
+     *  and setting the LZMA_IGNORE_CHECK flag which instructs the
+     *  decoder to disable CRC checks on compressed data.
+     */
+    lzma_ret ret = lzma_stream_decoder(
+            strm, /* memory limit */ 500 << 20,
+            LZMA_CONCATENATED | LZMA_IGNORE_CHECK);
+
+    if (ret == LZMA_OK)
+        return true;
+
+    /* The flag "FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION" is defined
+     * by the fuzzer build script. We use it here to disable debug
+     * messages. Disabling debug messages in fuzzer test harnesses seems
+     *  to be the norm for oss-fuzz targets.
+     */
+#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
+    // Something went wrong, print an informative debug message
+    const char *msg;
+    switch (ret) {
+    case LZMA_MEM_ERROR:
+        msg = "Memory allocation failed";
+        break;
+
+    case LZMA_OPTIONS_ERROR:
+        msg = "Unsupported decompressor flags";
+        break;
+
+    default:
+        msg = "Unknown error, possibly a bug";
+        break;
+    }
+
+    fprintf(stderr, "Error initializing the decoder: %s (error code %u)\n",
+            msg, ret);
+#endif
+    return false;
+}
+
+static bool
+decompress(lzma_stream *strm, const uint8_t *inbuf, size_t inlen,
+           uint8_t *outbuf, size_t outlen)
+{
+    size_t remainlen = inlen;
+
+    lzma_action action = LZMA_RUN;
+
+    strm->next_in = NULL;
+    strm->avail_in = 0;
+    strm->next_out = outbuf;
+    strm->avail_out = outlen;
+
+    // Decode BUFSIZ==8192 bytes of inbuf at a time
+    while (true) {
+
+        // TODO: We invoke lzma_code twice when remainlen == 0.
+        // Is this okay?
+
+        if (strm->avail_in == 0 && remainlen != 0) {
+            strm->next_in = inbuf;
+            strm->avail_in = (remainlen > BUFSIZ) ? BUFSIZ : remainlen;
+            remainlen -= strm->avail_in;
+
+            if (remainlen == 0)
+                action = LZMA_FINISH;
+        }
+
+        lzma_ret ret = lzma_code(strm, action);
+        /* LZMA_PROG_ERROR should be rarely, if ever, happen
+         * The assertion codifies this expectation.
+         */
+        assert(ret != LZMA_PROG_ERROR);
+
+        // TODO: Is this code trying to overwrite outbuf when outlen
+        // is exhausted? If so, is that okay?
+        if (strm->avail_out == 0 || ret == LZMA_STREAM_END) {
+            strm->next_out = outbuf;
+            strm->avail_out = outlen;
+        }
+
+        if (ret != LZMA_OK) {
+            if (ret == LZMA_STREAM_END)
+                return true;
+
+#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
+            const char *msg;
+            switch (ret) {
+            case LZMA_MEM_ERROR:
+                msg = "Memory allocation failed";
+                break;
+
+            case LZMA_FORMAT_ERROR:
+                // .xz magic bytes weren't found.
+                msg = "The input is not in the .xz format";
+                break;
+
+            case LZMA_OPTIONS_ERROR:
+                msg = "Unsupported compression options";
+                break;
+
+            case LZMA_DATA_ERROR:
+                msg = "Compressed file is corrupt";
+                break;
+
+            case LZMA_BUF_ERROR:
+                msg = "Compressed file is truncated or "
+                        "otherwise corrupt";
+                break;
+
+            default:
+                msg = "Unknown error, possibly a bug";
+                break;
+            }
+
+            fprintf(stderr, "%s: Decoder error: "
+                    "%s (error code %u)\n",
+                    inname, msg, ret);
+#endif
+            return false;
+        }
+    }
+}
+
+int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+
+    lzma_stream strm = LZMA_STREAM_INIT;
+
+    // Null data is uninteresting
+    if (size == 0) {
+        return 0;
+    }
+
+    // Init decoder.
+    if (!init_decoder(&strm)) {
+        // Decoder initialization failed. There's no point
+        // retrying, so bail out.
+        return 0;
+    }
+
+    uint8_t outbuf[BUFSIZ];
+
+    if (!decompress(&strm, data, size, outbuf, BUFSIZ)) {
+#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
+        fprintf(stderr, "Decode failure\n");
+#endif
+    }
+
+    // Free the memory allocated for the decoder.
+    lzma_end(&strm);
+    return 0;
+}
-- 
2.17.1


Reply via email to