diff options
Diffstat (limited to 'tests/ossfuzz')
-rw-r--r-- | tests/ossfuzz/Makefile | 18 | ||||
-rw-r--r-- | tests/ossfuzz/config/fuzz.dict | 2 | ||||
-rw-r--r-- | tests/ossfuzz/config/fuzz.options | 2 | ||||
-rw-r--r-- | tests/ossfuzz/config/fuzz_decode_alone.options | 5 | ||||
-rw-r--r-- | tests/ossfuzz/config/fuzz_decode_stream.options | 4 | ||||
-rw-r--r-- | tests/ossfuzz/config/fuzz_encode_stream.options | 4 | ||||
-rw-r--r-- | tests/ossfuzz/config/fuzz_lzma.dict | 22 | ||||
-rw-r--r-- | tests/ossfuzz/config/fuzz_xz.dict | 4 | ||||
-rw-r--r-- | tests/ossfuzz/fuzz.c | 82 | ||||
-rw-r--r-- | tests/ossfuzz/fuzz_common.h | 55 | ||||
-rw-r--r-- | tests/ossfuzz/fuzz_decode_alone.c | 41 | ||||
-rw-r--r-- | tests/ossfuzz/fuzz_decode_stream.c | 53 | ||||
-rw-r--r-- | tests/ossfuzz/fuzz_encode_stream.c | 86 |
13 files changed, 288 insertions, 90 deletions
diff --git a/tests/ossfuzz/Makefile b/tests/ossfuzz/Makefile index 747fb66..742e063 100644 --- a/tests/ossfuzz/Makefile +++ b/tests/ossfuzz/Makefile @@ -1,7 +1,17 @@ -fuzz: fuzz.c - $(CC) $(CFLAGS) -c fuzz.c -I ../../src/liblzma/api/ - $(CXX) $(CXXFLAGS) $(LIB_FUZZING_ENGINE) fuzz.o -o $(OUT)/fuzz \ - ../../src/liblzma/.libs/liblzma.a +# SPDX-License-Identifier: 0BSD +FUZZ_TARGET_SRCS = $(wildcard *.c) +FUZZ_TARGET_BINS = $(FUZZ_TARGET_SRCS:.c=) + +all: $(FUZZ_TARGET_BINS) + +%: %.c + $(CC) $(CFLAGS) -c $< -I ../../src/liblzma/api/ ; + $(CXX) $(CXXFLAGS) $(LIB_FUZZING_ENGINE) $(<:.c=.o) -o $(OUT)/$@ \ + ../../src/liblzma/.libs/liblzma.a ; + +# The generated binaries are not removed, just the object files. The +# binaries are created to the $(OUT) directory and must be removed by the +# fuzzing framework. clean: rm -f *.o diff --git a/tests/ossfuzz/config/fuzz.dict b/tests/ossfuzz/config/fuzz.dict deleted file mode 100644 index 932d67c..0000000 --- a/tests/ossfuzz/config/fuzz.dict +++ /dev/null @@ -1,2 +0,0 @@ -"\xFD7zXZ\x00" -"YZ" diff --git a/tests/ossfuzz/config/fuzz.options b/tests/ossfuzz/config/fuzz.options deleted file mode 100644 index d59dfc1..0000000 --- a/tests/ossfuzz/config/fuzz.options +++ /dev/null @@ -1,2 +0,0 @@ -[libfuzzer] -dict = fuzz.dict diff --git a/tests/ossfuzz/config/fuzz_decode_alone.options b/tests/ossfuzz/config/fuzz_decode_alone.options new file mode 100644 index 0000000..1351d96 --- /dev/null +++ b/tests/ossfuzz/config/fuzz_decode_alone.options @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: 0BSD + +[libfuzzer] +max_len = 4096 +dict = fuzz_lzma.dict diff --git a/tests/ossfuzz/config/fuzz_decode_stream.options b/tests/ossfuzz/config/fuzz_decode_stream.options new file mode 100644 index 0000000..bbf43ac --- /dev/null +++ b/tests/ossfuzz/config/fuzz_decode_stream.options @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: 0BSD + +[libfuzzer] +dict = fuzz_xz.dict diff --git a/tests/ossfuzz/config/fuzz_encode_stream.options b/tests/ossfuzz/config/fuzz_encode_stream.options new file mode 100644 index 0000000..86d4f0c --- /dev/null +++ b/tests/ossfuzz/config/fuzz_encode_stream.options @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: 0BSD + +[libfuzzer] +max_len = 4096 diff --git a/tests/ossfuzz/config/fuzz_lzma.dict b/tests/ossfuzz/config/fuzz_lzma.dict new file mode 100644 index 0000000..b9d5dff --- /dev/null +++ b/tests/ossfuzz/config/fuzz_lzma.dict @@ -0,0 +1,22 @@ +# SPDX-License-Identifier: 0BSD + +# first 5 header bytes of .lzma archives based on the info from +# /doc/lzma-file-format.txt + +# byte 0 is created by encoding LZMA property values (lc, lp, pb) +# using the algorithm described in the documentation above. + +# lc=3, lp=0, pb=2 and dictionary size = 0x00100000 +"\x5d\x00\x00\x10\x00" + +# lc=3, lp=1, pb=3 and dictionary size = 0x00100000 +"\x93\x00\x00\x10\x00" + +# lc=2, lp=2, pb=4 and dictionary size = 0x00100000 +"\xc8\x00\x00\x10\x00" + +# lc=1, lp=3, pb=1 and dictionary size = 0x00200000 +"\x49\x00\x00\x20\x00" + +# lc=0, lp=4, pb=0 and dictionary size = 0x00200000 +"\x24\x00\x00\x20\x00" diff --git a/tests/ossfuzz/config/fuzz_xz.dict b/tests/ossfuzz/config/fuzz_xz.dict new file mode 100644 index 0000000..6ba4f24 --- /dev/null +++ b/tests/ossfuzz/config/fuzz_xz.dict @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: 0BSD + +"\xFD7zXZ\x00" +"YZ" diff --git a/tests/ossfuzz/fuzz.c b/tests/ossfuzz/fuzz.c deleted file mode 100644 index 6d89930..0000000 --- a/tests/ossfuzz/fuzz.c +++ /dev/null @@ -1,82 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// -/// \file fuzz.c -/// \brief Fuzz test program for liblzma -// -// Author: Lasse Collin -// -// This file has been put into the public domain. -// You can do whatever you want with this file. -// -/////////////////////////////////////////////////////////////////////////////// - -#include <inttypes.h> -#include <stdlib.h> -#include <stdio.h> -#include "lzma.h" - - -// Output buffer for decompressed data. This is write only; nothing cares -// about the actual data written here. -static uint8_t outbuf[4096]; - - -extern int -LLVMFuzzerTestOneInput(const uint8_t *inbuf, size_t inbuf_size) -{ - // Some header values can make liblzma allocate a lot of RAM - // (up to about 4 GiB with liblzma 5.2.x). We set a limit here to - // prevent extreme allocations when fuzzing. - const uint64_t memlimit = 300 << 20; // 300 MiB - - // Initialize a .xz decoder using the above memory usage limit. - // Enable support for concatenated .xz files which is used when - // decompressing regular .xz files (instead of data embedded inside - // some other file format). Integrity checks on the uncompressed - // data are ignored to make fuzzing more effective (incorrect check - // values won't prevent the decoder from processing more input). - // - // The flag LZMA_IGNORE_CHECK doesn't disable verification of header - // CRC32 values. Those checks are disabled when liblzma is built - // with the #define FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION. - lzma_stream strm = LZMA_STREAM_INIT; - lzma_ret ret = lzma_stream_decoder(&strm, memlimit, - LZMA_CONCATENATED | LZMA_IGNORE_CHECK); - if (ret != LZMA_OK) { - // This should never happen unless the system has - // no free memory or address space to allow the small - // allocations that the initialization requires. - fprintf(stderr, "lzma_stream_decoder() failed (%d)\n", ret); - abort(); - } - - // Give the whole input buffer at once to liblzma. - // Output buffer isn't initialized as liblzma only writes to it. - strm.next_in = inbuf; - strm.avail_in = inbuf_size; - strm.next_out = outbuf; - strm.avail_out = sizeof(outbuf); - - while ((ret = lzma_code(&strm, LZMA_FINISH)) == LZMA_OK) { - if (strm.avail_out == 0) { - // outbuf became full. We don't care about the - // uncompressed data there, so we simply reuse - // the outbuf and overwrite the old data. - strm.next_out = outbuf; - strm.avail_out = sizeof(outbuf); - } - } - - // LZMA_PROG_ERROR should never happen as long as the code calling - // the liblzma functions is correct. Thus LZMA_PROG_ERROR is a sign - // of a bug in either this function or in liblzma. - if (ret == LZMA_PROG_ERROR) { - fprintf(stderr, "lzma_code() returned LZMA_PROG_ERROR\n"); - abort(); - } - - // Free the allocated memory. - lzma_end(&strm); - - return 0; -} diff --git a/tests/ossfuzz/fuzz_common.h b/tests/ossfuzz/fuzz_common.h new file mode 100644 index 0000000..4537f1b --- /dev/null +++ b/tests/ossfuzz/fuzz_common.h @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: 0BSD + +/////////////////////////////////////////////////////////////////////////////// +// +/// \file fuzz_common.h +/// \brief Common macros and functions needed by the fuzz targets +// +// Authors: Maksym Vatsyk +// Lasse Collin +// +/////////////////////////////////////////////////////////////////////////////// + +#include <inttypes.h> +#include <stdlib.h> +#include <stdio.h> +#include "lzma.h" + +// Some header values can make liblzma allocate a lot of RAM +// (up to about 4 GiB with liblzma 5.2.x). We set a limit here to +// prevent extreme allocations when fuzzing. +#define MEM_LIMIT (300 << 20) // 300 MiB + + +static void +fuzz_code(lzma_stream *stream, const uint8_t *inbuf, size_t inbuf_size) { + // Output buffer for decompressed data. This is write only; nothing + // cares about the actual data written here. + uint8_t outbuf[4096]; + + // Give the whole input buffer at once to liblzma. + // Output buffer isn't initialized as liblzma only writes to it. + stream->next_in = inbuf; + stream->avail_in = inbuf_size; + stream->next_out = outbuf; + stream->avail_out = sizeof(outbuf); + + lzma_ret ret; + while ((ret = lzma_code(stream, LZMA_FINISH)) == LZMA_OK) { + if (stream->avail_out == 0) { + // outbuf became full. We don't care about the + // uncompressed data there, so we simply reuse + // the outbuf and overwrite the old data. + stream->next_out = outbuf; + stream->avail_out = sizeof(outbuf); + } + } + + // LZMA_PROG_ERROR should never happen as long as the code calling + // the liblzma functions is correct. Thus LZMA_PROG_ERROR is a sign + // of a bug in either this function or in liblzma. + if (ret == LZMA_PROG_ERROR) { + fprintf(stderr, "lzma_code() returned LZMA_PROG_ERROR\n"); + abort(); + } +} diff --git a/tests/ossfuzz/fuzz_decode_alone.c b/tests/ossfuzz/fuzz_decode_alone.c new file mode 100644 index 0000000..1ef2f9e --- /dev/null +++ b/tests/ossfuzz/fuzz_decode_alone.c @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: 0BSD + +/////////////////////////////////////////////////////////////////////////////// +// +/// \file fuzz_decode_alone.c +/// \brief Fuzz test program for liblzma .lzma decoding +// +// Authors: Maksym Vatsyk +// Lasse Collin +// +/////////////////////////////////////////////////////////////////////////////// + +#include <inttypes.h> +#include <stdlib.h> +#include <stdio.h> +#include "lzma.h" +#include "fuzz_common.h" + + +extern int +LLVMFuzzerTestOneInput(const uint8_t *inbuf, size_t inbuf_size) +{ + lzma_stream strm = LZMA_STREAM_INIT; + // Initialize a LZMA alone decoder using the memory usage limit + // defined in fuzz_common.h + lzma_ret ret = lzma_alone_decoder(&strm, MEM_LIMIT); + + if (ret != LZMA_OK) { + // This should never happen unless the system has + // no free memory or address space to allow the small + // allocations that the initialization requires. + fprintf(stderr, "lzma_alone_decoder() failed (%d)\n", ret); + abort(); + } + + fuzz_code(&strm, inbuf, inbuf_size); + + // Free the allocated memory. + lzma_end(&strm); + return 0; +} diff --git a/tests/ossfuzz/fuzz_decode_stream.c b/tests/ossfuzz/fuzz_decode_stream.c new file mode 100644 index 0000000..d786061 --- /dev/null +++ b/tests/ossfuzz/fuzz_decode_stream.c @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: 0BSD + +/////////////////////////////////////////////////////////////////////////////// +// +/// \file fuzz_decode_stream.c +/// \brief Fuzz test program for single threaded .xz decoding +// +// Authors: Lasse Collin +// Maksym Vatsyk +// +/////////////////////////////////////////////////////////////////////////////// + +#include <inttypes.h> +#include <stdlib.h> +#include <stdio.h> +#include "lzma.h" +#include "fuzz_common.h" + + +extern int +LLVMFuzzerTestOneInput(const uint8_t *inbuf, size_t inbuf_size) +{ + lzma_stream strm = LZMA_STREAM_INIT; + // Initialize a .xz decoder using the memory usage limit + // defined in fuzz_common.h + // + // Enable support for concatenated .xz files which is used when + // decompressing regular .xz files (instead of data embedded inside + // some other file format). Integrity checks on the uncompressed + // data are ignored to make fuzzing more effective (incorrect check + // values won't prevent the decoder from processing more input). + // + // The flag LZMA_IGNORE_CHECK doesn't disable verification of + // header CRC32 values. Those checks are disabled when liblzma is + // built with the #define FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION. + lzma_ret ret = lzma_stream_decoder(&strm, MEM_LIMIT, + LZMA_CONCATENATED | LZMA_IGNORE_CHECK); + + if (ret != LZMA_OK) { + // This should never happen unless the system has + // no free memory or address space to allow the small + // allocations that the initialization requires. + fprintf(stderr, "lzma_stream_decoder() failed (%d)\n", ret); + abort(); + } + + fuzz_code(&strm, inbuf, inbuf_size); + + // Free the allocated memory. + lzma_end(&strm); + + return 0; +} diff --git a/tests/ossfuzz/fuzz_encode_stream.c b/tests/ossfuzz/fuzz_encode_stream.c new file mode 100644 index 0000000..9438263 --- /dev/null +++ b/tests/ossfuzz/fuzz_encode_stream.c @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: 0BSD + +/////////////////////////////////////////////////////////////////////////////// +// +/// \file fuzz_encode_stream.c +/// \brief Fuzz test program for .xz encoding +// +// Authors: Maksym Vatsyk +// Lasse Collin +// +/////////////////////////////////////////////////////////////////////////////// + +#include <inttypes.h> +#include <stdlib.h> +#include <stdio.h> +#include "lzma.h" +#include "fuzz_common.h" + + +extern int +LLVMFuzzerTestOneInput(const uint8_t *inbuf, size_t inbuf_size) +{ + if (inbuf_size == 0) { + fprintf(stderr, "no input data provided\n"); + return 0; + } + + // Set the LZMA options based on the first input byte. The fuzzer + // will learn through its mutational genetic algorithm with the + // code coverage feedback that the first byte must be one of the + // values with a switch case label. This allows us to have one fuzz + // target cover many critical code paths so the fuzz resources can + // be used efficiently. + uint32_t preset_level; + const uint8_t decider = inbuf[0]; + + switch (decider) { + case 0: + case 1: + case 5: + preset_level = (uint32_t)decider; + break; + case 6: + preset_level = 0 | LZMA_PRESET_EXTREME; + break; + case 7: + preset_level = 3 | LZMA_PRESET_EXTREME; + break; + default: + return 0; + } + + // Initialize lzma_options with the above preset level + lzma_options_lzma opt_lzma; + if (lzma_lzma_preset(&opt_lzma, preset_level)){ + fprintf(stderr, "lzma_lzma_preset() failed\n"); + abort(); + } + + // Set the filter chain as only LZMA2. + lzma_filter filters[2] = { + { + .id = LZMA_FILTER_LZMA2, + .options = &opt_lzma, + }, { + .id = LZMA_VLI_UNKNOWN, + } + }; + + // initialize empty LZMA stream + lzma_stream strm = LZMA_STREAM_INIT; + + // Initialize the stream encoder using the above + // stream, filter chain and CRC64. + lzma_ret ret = lzma_stream_encoder(&strm, filters, LZMA_CHECK_CRC64); + if (ret != LZMA_OK) { + fprintf(stderr, "lzma_stream_encoder() failed (%d)\n", ret); + abort(); + } + + fuzz_code(&strm, inbuf + 1, inbuf_size - 1); + + // Free the allocated memory. + lzma_end(&strm); + return 0; +} |