Skip to content

Commit 57e1b45

Browse files
authored
Merge pull request #3551 from embg/seq_prod_fuzz
Provide an interface for fuzzing sequence producer plugins
2 parents abb3585 + a810e1e commit 57e1b45

21 files changed

+297
-14
lines changed

tests/fuzz/Makefile

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,13 @@ ZSTDDIR = ../../lib
3434
PRGDIR = ../../programs
3535
CONTRIBDIR = ../../contrib
3636

37-
# TODO(embg) make it possible to plug in an arbitrary matchfinder as a .o file
38-
MATCHFINDER_DIR = $(CONTRIBDIR)/externalSequenceProducer
39-
MATCHFINDER_SRC = $(MATCHFINDER_DIR)/sequence_producer.c
37+
DEFAULT_SEQ_PROD_DIR = $(CONTRIBDIR)/externalSequenceProducer
38+
DEFAULT_SEQ_PROD_SRC = $(DEFAULT_SEQ_PROD_DIR)/sequence_producer.c
39+
THIRD_PARTY_SEQ_PROD_OBJ ?=
4040

4141
FUZZ_CPPFLAGS := -I$(ZSTDDIR) -I$(ZSTDDIR)/common -I$(ZSTDDIR)/compress \
4242
-I$(ZSTDDIR)/dictBuilder -I$(ZSTDDIR)/deprecated -I$(ZSTDDIR)/legacy \
43-
-I$(CONTRIBDIR)/seekable_format -I$(PRGDIR) -I$(MATCHFINDER_DIR) \
43+
-I$(CONTRIBDIR)/seekable_format -I$(PRGDIR) -I$(DEFAULT_SEQ_PROD_DIR) \
4444
-DZSTD_MULTITHREAD -DZSTD_LEGACY_SUPPORT=1 $(CPPFLAGS)
4545
FUZZ_EXTRA_FLAGS := -Wall -Wextra -Wcast-qual -Wcast-align -Wshadow \
4646
-Wstrict-aliasing=1 -Wswitch-enum -Wdeclaration-after-statement \
@@ -75,7 +75,7 @@ FUZZ_SRC := \
7575
$(ZSTDCOMP_SRC) \
7676
$(ZSTDDICT_SRC) \
7777
$(ZSTDLEGACY_SRC) \
78-
$(MATCHFINDER_SRC)
78+
$(DEFAULT_SEQ_PROD_SRC)
7979
FUZZ_SRC := $(sort $(wildcard $(FUZZ_SRC)))
8080

8181
FUZZ_D_OBJ1 := $(subst $(ZSTDDIR)/common/,d_lib_common_,$(FUZZ_SRC))
@@ -84,21 +84,23 @@ FUZZ_D_OBJ3 := $(subst $(ZSTDDIR)/decompress/,d_lib_decompress_,$(FUZZ_D_OBJ2))
8484
FUZZ_D_OBJ4 := $(subst $(ZSTDDIR)/dictBuilder/,d_lib_dictBuilder_,$(FUZZ_D_OBJ3))
8585
FUZZ_D_OBJ5 := $(subst $(ZSTDDIR)/legacy/,d_lib_legacy_,$(FUZZ_D_OBJ4))
8686
FUZZ_D_OBJ6 := $(subst $(PRGDIR)/,d_prg_,$(FUZZ_D_OBJ5))
87-
FUZZ_D_OBJ7 := $(subst $(MATCHFINDER_DIR)/,d_matchfinder_,$(FUZZ_D_OBJ6))
87+
FUZZ_D_OBJ7 := $(subst $(DEFAULT_SEQ_PROD_DIR)/,d_default_seq_prod_,$(FUZZ_D_OBJ6))
8888
FUZZ_D_OBJ8 := $(subst $\./,d_fuzz_,$(FUZZ_D_OBJ7))
8989
FUZZ_D_OBJ9 := $(FUZZ_D_OBJ8:.c=.o)
90-
FUZZ_DECOMPRESS_OBJ := $(FUZZ_D_OBJ9:.S=.o)
90+
FUZZ_D_OBJ10 := $(THIRD_PARTY_SEQ_PROD_OBJ) $(FUZZ_D_OBJ9)
91+
FUZZ_DECOMPRESS_OBJ := $(FUZZ_D_OBJ10:.S=.o)
9192

9293
FUZZ_RT_OBJ1 := $(subst $(ZSTDDIR)/common/,rt_lib_common_,$(FUZZ_SRC))
9394
FUZZ_RT_OBJ2 := $(subst $(ZSTDDIR)/compress/,rt_lib_compress_,$(FUZZ_RT_OBJ1))
9495
FUZZ_RT_OBJ3 := $(subst $(ZSTDDIR)/decompress/,rt_lib_decompress_,$(FUZZ_RT_OBJ2))
9596
FUZZ_RT_OBJ4 := $(subst $(ZSTDDIR)/dictBuilder/,rt_lib_dictBuilder_,$(FUZZ_RT_OBJ3))
9697
FUZZ_RT_OBJ5 := $(subst $(ZSTDDIR)/legacy/,rt_lib_legacy_,$(FUZZ_RT_OBJ4))
9798
FUZZ_RT_OBJ6 := $(subst $(PRGDIR)/,rt_prg_,$(FUZZ_RT_OBJ5))
98-
FUZZ_RT_OBJ7 := $(subst $(MATCHFINDER_DIR)/,rt_matchfinder_,$(FUZZ_RT_OBJ6))
99+
FUZZ_RT_OBJ7 := $(subst $(DEFAULT_SEQ_PROD_DIR)/,rt_default_seq_prod_,$(FUZZ_RT_OBJ6))
99100
FUZZ_RT_OBJ8 := $(subst $\./,rt_fuzz_,$(FUZZ_RT_OBJ7))
100101
FUZZ_RT_OBJ9 := $(FUZZ_RT_OBJ8:.c=.o)
101-
FUZZ_ROUND_TRIP_OBJ := $(FUZZ_RT_OBJ9:.S=.o)
102+
FUZZ_RT_OBJ10 := $(THIRD_PARTY_SEQ_PROD_OBJ) $(FUZZ_RT_OBJ9)
103+
FUZZ_ROUND_TRIP_OBJ := $(FUZZ_RT_OBJ10:.S=.o)
102104

103105
.PHONY: default all clean cleanall
104106

@@ -151,7 +153,7 @@ rt_prg_%.o: $(PRGDIR)/%.c
151153
rt_fuzz_%.o: %.c
152154
$(CC) $(FUZZ_CPPFLAGS) $(FUZZ_CFLAGS) $(FUZZ_ROUND_TRIP_FLAGS) $< -c -o $@
153155

154-
rt_matchfinder_%.o: $(MATCHFINDER_DIR)/%.c
156+
rt_default_seq_prod_%.o: $(DEFAULT_SEQ_PROD_DIR)/%.c
155157
$(CC) $(FUZZ_CPPFLAGS) $(FUZZ_CFLAGS) $(FUZZ_ROUND_TRIP_FLAGS) $< -c -o $@
156158

157159
d_lib_common_%.o: $(ZSTDDIR)/common/%.c
@@ -178,7 +180,7 @@ d_prg_%.o: $(PRGDIR)/%.c
178180
d_fuzz_%.o: %.c
179181
$(CC) $(FUZZ_CPPFLAGS) $(FUZZ_CFLAGS) $< -c -o $@
180182

181-
d_matchfinder_%.o: $(MATCHFINDER_DIR)/%.c
183+
d_default_seq_prod_%.o: $(DEFAULT_SEQ_PROD_DIR)/%.c
182184
$(CC) $(FUZZ_CPPFLAGS) $(FUZZ_CFLAGS) $< -c -o $@
183185

184186
simple_round_trip: $(FUZZ_HEADERS) $(FUZZ_ROUND_TRIP_OBJ) rt_fuzz_simple_round_trip.o

tests/fuzz/README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ Alternatively, you can fuzz all targets in parallel, using one core per target:
6161
```
6262
python3 ./fuzz.py list | xargs -P$(python3 ./fuzz.py list | wc -l) -I__ sh -c "python3 ./fuzz.py libfuzzer __ 2>&1 | tee __.log"
6363
```
64-
Either way, to double-check that no crashes were found, run `ls corpora/*crash`.
64+
Either way, to double-check that no crashes were found, run `ls corpora/*crash`.
6565
If any crashes were found, you can use the hashes to reproduce them.
6666

6767
## LibFuzzer
@@ -113,3 +113,7 @@ CC=clang CXX=clang++ ./fuzz.py build all --enable-asan --enable-ubsan
113113
CC=clang CXX=clang++ ./fuzz.py build all --enable-msan
114114
./fuzz.py regression all
115115
```
116+
117+
## Fuzzing a custom sequence producer plugin
118+
Sequence producer plugin authors can use the zstd fuzzers to stress-test their code.
119+
See the documentation in `fuzz_third_party_seq_prod.h` for details.

tests/fuzz/block_round_trip.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include "zstd.h"
2424
#include "zstd_helpers.h"
2525
#include "fuzz_data_producer.h"
26+
#include "fuzz_third_party_seq_prod.h"
2627

2728
static ZSTD_CCtx *cctx = NULL;
2829
static ZSTD_DCtx *dctx = NULL;
@@ -54,6 +55,8 @@ static size_t roundTripTest(void *result, size_t resultCapacity,
5455

5556
int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size)
5657
{
58+
FUZZ_SEQ_PROD_SETUP();
59+
5760
/* Give a random portion of src data to the producer, to use for
5861
parameter generation. The rest will be used for (de)compression */
5962
FUZZ_dataProducer_t *producer = FUZZ_dataProducer_create(src, size);
@@ -95,5 +98,6 @@ int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size)
9598
ZSTD_freeCCtx(cctx); cctx = NULL;
9699
ZSTD_freeDCtx(dctx); dctx = NULL;
97100
#endif
101+
FUZZ_SEQ_PROD_TEARDOWN();
98102
return 0;
99103
}

tests/fuzz/decompress_dstSize_tooSmall.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,15 @@
2222
#include "zstd_errors.h"
2323
#include "zstd_helpers.h"
2424
#include "fuzz_data_producer.h"
25+
#include "fuzz_third_party_seq_prod.h"
2526

2627
static ZSTD_CCtx *cctx = NULL;
2728
static ZSTD_DCtx *dctx = NULL;
2829

2930
int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size)
3031
{
32+
FUZZ_SEQ_PROD_SETUP();
33+
3134
/* Give a random portion of src data to the producer, to use for
3235
parameter generation. The rest will be used for (de)compression */
3336
FUZZ_dataProducer_t *producer = FUZZ_dataProducer_create(src, size);
@@ -66,5 +69,6 @@ int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size)
6669
ZSTD_freeCCtx(cctx); cctx = NULL;
6770
ZSTD_freeDCtx(dctx); dctx = NULL;
6871
#endif
72+
FUZZ_SEQ_PROD_TEARDOWN();
6973
return 0;
7074
}

tests/fuzz/dictionary_decompress.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,14 @@
2020
#include "fuzz_helpers.h"
2121
#include "zstd_helpers.h"
2222
#include "fuzz_data_producer.h"
23+
#include "fuzz_third_party_seq_prod.h"
2324

2425
static ZSTD_DCtx *dctx = NULL;
2526

2627
int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size)
2728
{
29+
FUZZ_SEQ_PROD_SETUP();
30+
2831
/* Give a random portion of src data to the producer, to use for
2932
parameter generation. The rest will be used for (de)compression */
3033
FUZZ_dataProducer_t *producer = FUZZ_dataProducer_create(src, size);
@@ -69,5 +72,6 @@ int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size)
6972
#ifndef STATEFUL_FUZZING
7073
ZSTD_freeDCtx(dctx); dctx = NULL;
7174
#endif
75+
FUZZ_SEQ_PROD_TEARDOWN();
7276
return 0;
7377
}

tests/fuzz/dictionary_loader.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "fuzz_helpers.h"
2121
#include "zstd_helpers.h"
2222
#include "fuzz_data_producer.h"
23+
#include "fuzz_third_party_seq_prod.h"
2324

2425
/**
2526
* Compresses the data and returns the compressed size or an error.
@@ -35,7 +36,7 @@ static size_t compress(void* compressed, size_t compressedCapacity,
3536
if (refPrefix)
3637
FUZZ_ZASSERT(ZSTD_CCtx_refPrefix_advanced(
3738
cctx, dict, dictSize, dictContentType));
38-
else
39+
else
3940
FUZZ_ZASSERT(ZSTD_CCtx_loadDictionary_advanced(
4041
cctx, dict, dictSize, dictLoadMethod, dictContentType));
4142
size_t const compressedSize = ZSTD_compress2(
@@ -67,6 +68,7 @@ static size_t decompress(void* result, size_t resultCapacity,
6768

6869
int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size)
6970
{
71+
FUZZ_SEQ_PROD_SETUP();
7072
FUZZ_dataProducer_t *producer = FUZZ_dataProducer_create(src, size);
7173
int const refPrefix = FUZZ_dataProducer_uint32Range(producer, 0, 1) != 0;
7274
ZSTD_dictLoadMethod_e const dlm =
@@ -99,5 +101,6 @@ int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size)
99101
free(cBuf);
100102
free(rBuf);
101103
FUZZ_dataProducer_free(producer);
104+
FUZZ_SEQ_PROD_TEARDOWN();
102105
return 0;
103106
}

tests/fuzz/dictionary_round_trip.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "fuzz_helpers.h"
2222
#include "zstd_helpers.h"
2323
#include "fuzz_data_producer.h"
24+
#include "fuzz_third_party_seq_prod.h"
2425

2526
static ZSTD_CCtx *cctx = NULL;
2627
static ZSTD_DCtx *dctx = NULL;
@@ -108,6 +109,8 @@ static size_t roundTripTest(void *result, size_t resultCapacity,
108109

109110
int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size)
110111
{
112+
FUZZ_SEQ_PROD_SETUP();
113+
111114
/* Give a random portion of src data to the producer, to use for
112115
parameter generation. The rest will be used for (de)compression */
113116
FUZZ_dataProducer_t *producer = FUZZ_dataProducer_create(src, size);
@@ -147,5 +150,6 @@ int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size)
147150
ZSTD_freeCCtx(cctx); cctx = NULL;
148151
ZSTD_freeDCtx(dctx); dctx = NULL;
149152
#endif
153+
FUZZ_SEQ_PROD_TEARDOWN();
150154
return 0;
151155
}

tests/fuzz/dictionary_stream_round_trip.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include "fuzz_helpers.h"
2323
#include "zstd_helpers.h"
2424
#include "fuzz_data_producer.h"
25+
#include "fuzz_third_party_seq_prod.h"
2526

2627
ZSTD_CCtx *cctx = NULL;
2728
static ZSTD_DCtx *dctx = NULL;
@@ -147,6 +148,7 @@ static size_t compress(uint8_t *dst, size_t capacity,
147148

148149
int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size)
149150
{
151+
FUZZ_SEQ_PROD_SETUP();
150152
size_t neededBufSize;
151153

152154
/* Give a random portion of src data to the producer, to use for
@@ -202,5 +204,6 @@ int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size)
202204
ZSTD_freeCCtx(cctx); cctx = NULL;
203205
ZSTD_freeDCtx(dctx); dctx = NULL;
204206
#endif
207+
FUZZ_SEQ_PROD_TEARDOWN();
205208
return 0;
206209
}

tests/fuzz/fuzz.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,11 @@
3131
* This is the canonical flag to enable deterministic builds for fuzzing.
3232
* Changes to zstd for fuzzing are gated behind this define.
3333
* It is recommended to define this when building zstd for fuzzing.
34+
* @param FUZZ_THIRD_PARTY_SEQ_PROD
35+
* This flag allows sequence producer plugin authors to replace the built-in
36+
* default sequence producer with their own code. If you are not a plugin
37+
* author, you should not define this flag. See the docs at
38+
* fuzz_third_party_seq_prod.h for more information.
3439
*/
3540

3641
#ifndef FUZZ_H

tests/fuzz/fuzz.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ def __init__(self, input_type, frame_type=FrameType.ZSTD):
7878
CXXFLAGS = os.environ.get('CXXFLAGS', CFLAGS)
7979
LDFLAGS = os.environ.get('LDFLAGS', '')
8080
MFLAGS = os.environ.get('MFLAGS', '-j')
81+
THIRD_PARTY_SEQ_PROD_OBJ = os.environ.get('THIRD_PARTY_SEQ_PROD_OBJ', '')
8182

8283
# Fuzzing environment variables
8384
LIB_FUZZING_ENGINE = os.environ.get('LIB_FUZZING_ENGINE', 'libregression.a')
@@ -319,6 +320,12 @@ def build_parser(args):
319320
dest='stateful_fuzzing',
320321
action='store_true',
321322
help='Reuse contexts between runs (makes reproduction impossible)')
323+
parser.add_argument(
324+
'--custom-seq-prod',
325+
dest='third_party_seq_prod_obj',
326+
type=str,
327+
default=THIRD_PARTY_SEQ_PROD_OBJ,
328+
help='Path to an object file with symbols for fuzzing your sequence producer plugin.')
322329
parser.add_argument(
323330
'--cc',
324331
dest='cc',
@@ -450,6 +457,10 @@ def build(args):
450457
if args.stateful_fuzzing:
451458
cppflags += ['-DSTATEFUL_FUZZING']
452459

460+
if args.third_party_seq_prod_obj:
461+
cppflags += ['-DFUZZ_THIRD_PARTY_SEQ_PROD']
462+
mflags += ['THIRD_PARTY_SEQ_PROD_OBJ={}'.format(args.third_party_seq_prod_obj)]
463+
453464
if args.fuzzing_mode:
454465
cppflags += ['-DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION']
455466

0 commit comments

Comments
 (0)