From: Bhargava Shastry <[email protected]>
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