2
* This fuzz target performs a lz4 streaming round-trip test
3
* (compress & decompress), compares the result with the original, and calls
4
* abort() on corruption.
12
#include "fuzz_helpers.h"
13
#define LZ4_STATIC_LINKING_ONLY
15
#define LZ4_HC_STATIC_LINKING_ONLY
31
LZ4_stream_t* cstream;
32
LZ4_streamHC_t* cstreamHC;
33
LZ4_streamDecode_t* dstream;
41
cursor_t cursor_create(size_t size)
44
cursor.buf = (char*)malloc(size);
47
FUZZ_ASSERT(cursor.buf);
51
typedef void (*round_trip_t)(state_t* state);
53
void cursor_free(cursor_t cursor)
58
state_t state_create(char const* data, size_t size, uint32_t seed)
64
state.data.buf = (char const*)data;
65
state.data.size = size;
68
/* Extra margin because we are streaming. */
69
state.compressed = cursor_create(1024 + 2 * LZ4_compressBound(size));
70
state.roundTrip = cursor_create(size);
72
state.cstream = LZ4_createStream();
73
FUZZ_ASSERT(state.cstream);
74
state.cstreamHC = LZ4_createStreamHC();
75
FUZZ_ASSERT(state.cstream);
76
state.dstream = LZ4_createStreamDecode();
77
FUZZ_ASSERT(state.dstream);
82
void state_free(state_t state)
84
cursor_free(state.compressed);
85
cursor_free(state.roundTrip);
86
LZ4_freeStream(state.cstream);
87
LZ4_freeStreamHC(state.cstreamHC);
88
LZ4_freeStreamDecode(state.dstream);
91
static void state_reset(state_t* state, uint32_t seed)
93
state->level = FUZZ_rand32(&seed, LZ4HC_CLEVEL_MIN, LZ4HC_CLEVEL_MAX);
94
LZ4_resetStream_fast(state->cstream);
95
LZ4_resetStreamHC_fast(state->cstreamHC, state->level);
96
LZ4_setStreamDecode(state->dstream, NULL, 0);
98
state->compressed.pos = 0;
99
state->roundTrip.pos = 0;
103
static void state_decompress(state_t* state, char const* src, int srcSize)
105
char* dst = state->roundTrip.buf + state->roundTrip.pos;
106
int const dstCapacity = state->roundTrip.size - state->roundTrip.pos;
107
int const dSize = LZ4_decompress_safe_continue(state->dstream, src, dst,
108
srcSize, dstCapacity);
109
FUZZ_ASSERT(dSize >= 0);
110
state->roundTrip.pos += dSize;
113
static void state_checkRoundTrip(state_t const* state)
115
char const* data = state->data.buf;
116
size_t const size = state->data.size;
117
FUZZ_ASSERT_MSG(size == state->roundTrip.pos, "Incorrect size!");
118
FUZZ_ASSERT_MSG(!memcmp(data, state->roundTrip.buf, size), "Corruption!");
122
* Picks a dictionary size and trims the dictionary off of the data.
123
* We copy the dictionary to the roundTrip so our validation passes.
125
static size_t state_trimDict(state_t* state)
127
/* 64 KB is the max dict size, allow slightly beyond that to test trim. */
128
uint32_t maxDictSize = MIN(70 * 1024, state->data.size);
129
size_t const dictSize = FUZZ_rand32(&state->seed, 0, maxDictSize);
130
DEBUGLOG(2, "dictSize = %zu", dictSize);
131
FUZZ_ASSERT(state->data.pos == 0);
132
FUZZ_ASSERT(state->roundTrip.pos == 0);
133
memcpy(state->roundTrip.buf, state->data.buf, dictSize);
134
state->data.pos += dictSize;
135
state->roundTrip.pos += dictSize;
139
static void state_prefixRoundTrip(state_t* state)
141
while (state->data.pos != state->data.size) {
142
char const* src = state->data.buf + state->data.pos;
143
char* dst = state->compressed.buf + state->compressed.pos;
144
int const srcRemaining = state->data.size - state->data.pos;
145
int const srcSize = FUZZ_rand32(&state->seed, 0, srcRemaining);
146
int const dstCapacity = state->compressed.size - state->compressed.pos;
147
int const cSize = LZ4_compress_fast_continue(state->cstream, src, dst,
148
srcSize, dstCapacity, 0);
149
FUZZ_ASSERT(cSize > 0);
150
state->data.pos += srcSize;
151
state->compressed.pos += cSize;
152
state_decompress(state, dst, cSize);
156
static void state_extDictRoundTrip(state_t* state)
159
cursor_t data2 = cursor_create(state->data.size);
160
memcpy(data2.buf, state->data.buf, state->data.size);
161
while (state->data.pos != state->data.size) {
162
char const* data = (i++ & 1) ? state->data.buf : data2.buf;
163
char const* src = data + state->data.pos;
164
char* dst = state->compressed.buf + state->compressed.pos;
165
int const srcRemaining = state->data.size - state->data.pos;
166
int const srcSize = FUZZ_rand32(&state->seed, 0, srcRemaining);
167
int const dstCapacity = state->compressed.size - state->compressed.pos;
168
int const cSize = LZ4_compress_fast_continue(state->cstream, src, dst,
169
srcSize, dstCapacity, 0);
170
FUZZ_ASSERT(cSize > 0);
171
state->data.pos += srcSize;
172
state->compressed.pos += cSize;
173
state_decompress(state, dst, cSize);
178
static void state_randomRoundTrip(state_t* state, round_trip_t rt0,
181
if (FUZZ_rand32(&state->seed, 0, 1)) {
188
static void state_loadDictRoundTrip(state_t* state)
190
char const* dict = state->data.buf;
191
size_t const dictSize = state_trimDict(state);
192
LZ4_loadDict(state->cstream, dict, dictSize);
193
LZ4_setStreamDecode(state->dstream, dict, dictSize);
194
state_randomRoundTrip(state, state_prefixRoundTrip, state_extDictRoundTrip);
197
static void state_attachDictRoundTrip(state_t* state)
199
char const* dict = state->data.buf;
200
size_t const dictSize = state_trimDict(state);
201
LZ4_stream_t* dictStream = LZ4_createStream();
202
LZ4_loadDict(dictStream, dict, dictSize);
203
LZ4_attach_dictionary(state->cstream, dictStream);
204
LZ4_setStreamDecode(state->dstream, dict, dictSize);
205
state_randomRoundTrip(state, state_prefixRoundTrip, state_extDictRoundTrip);
206
LZ4_freeStream(dictStream);
209
static void state_prefixHCRoundTrip(state_t* state)
211
while (state->data.pos != state->data.size) {
212
char const* src = state->data.buf + state->data.pos;
213
char* dst = state->compressed.buf + state->compressed.pos;
214
int const srcRemaining = state->data.size - state->data.pos;
215
int const srcSize = FUZZ_rand32(&state->seed, 0, srcRemaining);
216
int const dstCapacity = state->compressed.size - state->compressed.pos;
217
int const cSize = LZ4_compress_HC_continue(state->cstreamHC, src, dst,
218
srcSize, dstCapacity);
219
FUZZ_ASSERT(cSize > 0);
220
state->data.pos += srcSize;
221
state->compressed.pos += cSize;
222
state_decompress(state, dst, cSize);
226
static void state_extDictHCRoundTrip(state_t* state)
229
cursor_t data2 = cursor_create(state->data.size);
230
DEBUGLOG(2, "extDictHC");
231
memcpy(data2.buf, state->data.buf, state->data.size);
232
while (state->data.pos != state->data.size) {
233
char const* data = (i++ & 1) ? state->data.buf : data2.buf;
234
char const* src = data + state->data.pos;
235
char* dst = state->compressed.buf + state->compressed.pos;
236
int const srcRemaining = state->data.size - state->data.pos;
237
int const srcSize = FUZZ_rand32(&state->seed, 0, srcRemaining);
238
int const dstCapacity = state->compressed.size - state->compressed.pos;
239
int const cSize = LZ4_compress_HC_continue(state->cstreamHC, src, dst,
240
srcSize, dstCapacity);
241
FUZZ_ASSERT(cSize > 0);
242
DEBUGLOG(2, "srcSize = %d", srcSize);
243
state->data.pos += srcSize;
244
state->compressed.pos += cSize;
245
state_decompress(state, dst, cSize);
250
static void state_loadDictHCRoundTrip(state_t* state)
252
char const* dict = state->data.buf;
253
size_t const dictSize = state_trimDict(state);
254
LZ4_loadDictHC(state->cstreamHC, dict, dictSize);
255
LZ4_setStreamDecode(state->dstream, dict, dictSize);
256
state_randomRoundTrip(state, state_prefixHCRoundTrip,
257
state_extDictHCRoundTrip);
260
static void state_attachDictHCRoundTrip(state_t* state)
262
char const* dict = state->data.buf;
263
size_t const dictSize = state_trimDict(state);
264
LZ4_streamHC_t* dictStream = LZ4_createStreamHC();
265
LZ4_setCompressionLevel(dictStream, state->level);
266
LZ4_loadDictHC(dictStream, dict, dictSize);
267
LZ4_attach_HC_dictionary(state->cstreamHC, dictStream);
268
LZ4_setStreamDecode(state->dstream, dict, dictSize);
269
state_randomRoundTrip(state, state_prefixHCRoundTrip,
270
state_extDictHCRoundTrip);
271
LZ4_freeStreamHC(dictStream);
274
round_trip_t roundTrips[] = {
275
&state_prefixRoundTrip,
276
&state_extDictRoundTrip,
277
&state_loadDictRoundTrip,
278
&state_attachDictRoundTrip,
279
&state_prefixHCRoundTrip,
280
&state_extDictHCRoundTrip,
281
&state_loadDictHCRoundTrip,
282
&state_attachDictHCRoundTrip,
285
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
287
uint32_t seed = FUZZ_seed(&data, &size);
288
state_t state = state_create((char const*)data, size, seed);
289
const int n = sizeof(roundTrips) / sizeof(round_trip_t);
292
for (i = 0; i < n; ++i) {
293
DEBUGLOG(2, "Round trip %d", i);
294
state_reset(&state, seed);
295
roundTrips[i](&state);
296
state_checkRoundTrip(&state);