fork of https://github.com/rust-rocksdb/rust-rocksdb for nextgraph
				
			
			
		
			You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							302 lines
						
					
					
						
							9.7 KiB
						
					
					
				
			
		
		
	
	
							302 lines
						
					
					
						
							9.7 KiB
						
					
					
				| /**
 | |
|  * This fuzz target performs a lz4 streaming round-trip test
 | |
|  * (compress & decompress), compares the result with the original, and calls
 | |
|  * abort() on corruption.
 | |
|  */
 | |
| 
 | |
| #include <stddef.h>
 | |
| #include <stdint.h>
 | |
| #include <stdlib.h>
 | |
| #include <string.h>
 | |
| 
 | |
| #include "fuzz_helpers.h"
 | |
| #define LZ4_STATIC_LINKING_ONLY
 | |
| #include "lz4.h"
 | |
| #define LZ4_HC_STATIC_LINKING_ONLY
 | |
| #include "lz4hc.h"
 | |
| 
 | |
| typedef struct {
 | |
|   char const* buf;
 | |
|   size_t size;
 | |
|   size_t pos;
 | |
| } const_cursor_t;
 | |
| 
 | |
| typedef struct {
 | |
|   char* buf;
 | |
|   size_t size;
 | |
|   size_t pos;
 | |
| } cursor_t;
 | |
| 
 | |
| typedef struct {
 | |
|   LZ4_stream_t* cstream;
 | |
|   LZ4_streamHC_t* cstreamHC;
 | |
|   LZ4_streamDecode_t* dstream;
 | |
|   const_cursor_t data;
 | |
|   cursor_t compressed;
 | |
|   cursor_t roundTrip;
 | |
|   uint32_t seed;
 | |
|   int level;
 | |
| } state_t;
 | |
| 
 | |
| cursor_t cursor_create(size_t size)
 | |
| {
 | |
|   cursor_t cursor;
 | |
|   cursor.buf = (char*)malloc(size);
 | |
|   cursor.size = size;
 | |
|   cursor.pos = 0;
 | |
|   FUZZ_ASSERT(cursor.buf);
 | |
|   return cursor;
 | |
| }
 | |
| 
 | |
| typedef void (*round_trip_t)(state_t* state);
 | |
| 
 | |
| void cursor_free(cursor_t cursor)
 | |
| {
 | |
|     free(cursor.buf);
 | |
| }
 | |
| 
 | |
| state_t state_create(char const* data, size_t size, uint32_t seed)
 | |
| {
 | |
|     state_t state;
 | |
| 
 | |
|     state.seed = seed;
 | |
| 
 | |
|     state.data.buf = (char const*)data;
 | |
|     state.data.size = size;
 | |
|     state.data.pos = 0;
 | |
| 
 | |
|     /* Extra margin because we are streaming. */
 | |
|     state.compressed = cursor_create(1024 + 2 * LZ4_compressBound(size));
 | |
|     state.roundTrip = cursor_create(size);
 | |
| 
 | |
|     state.cstream = LZ4_createStream();
 | |
|     FUZZ_ASSERT(state.cstream);
 | |
|     state.cstreamHC = LZ4_createStreamHC();
 | |
|     FUZZ_ASSERT(state.cstream);
 | |
|     state.dstream = LZ4_createStreamDecode();
 | |
|     FUZZ_ASSERT(state.dstream);
 | |
| 
 | |
|     return state;
 | |
| }
 | |
| 
 | |
| void state_free(state_t state)
 | |
| {
 | |
|     cursor_free(state.compressed);
 | |
|     cursor_free(state.roundTrip);
 | |
|     LZ4_freeStream(state.cstream);
 | |
|     LZ4_freeStreamHC(state.cstreamHC);
 | |
|     LZ4_freeStreamDecode(state.dstream);
 | |
| }
 | |
| 
 | |
| static void state_reset(state_t* state, uint32_t seed)
 | |
| {
 | |
|     state->level = FUZZ_rand32(&seed, LZ4HC_CLEVEL_MIN, LZ4HC_CLEVEL_MAX);
 | |
|     LZ4_resetStream_fast(state->cstream);
 | |
|     LZ4_resetStreamHC_fast(state->cstreamHC, state->level);
 | |
|     LZ4_setStreamDecode(state->dstream, NULL, 0);
 | |
|     state->data.pos = 0;
 | |
|     state->compressed.pos = 0;
 | |
|     state->roundTrip.pos = 0;
 | |
|     state->seed = seed;
 | |
| }
 | |
| 
 | |
| static void state_decompress(state_t* state, char const* src, int srcSize)
 | |
| {
 | |
|     char* dst = state->roundTrip.buf + state->roundTrip.pos;
 | |
|     int const dstCapacity = state->roundTrip.size - state->roundTrip.pos;
 | |
|     int const dSize = LZ4_decompress_safe_continue(state->dstream, src, dst,
 | |
|                                                    srcSize, dstCapacity);
 | |
|     FUZZ_ASSERT(dSize >= 0);
 | |
|     state->roundTrip.pos += dSize;
 | |
| }
 | |
| 
 | |
| static void state_checkRoundTrip(state_t const* state)
 | |
| {
 | |
|     char const* data = state->data.buf;
 | |
|     size_t const size = state->data.size;
 | |
|     FUZZ_ASSERT_MSG(size == state->roundTrip.pos, "Incorrect size!");
 | |
|     FUZZ_ASSERT_MSG(!memcmp(data, state->roundTrip.buf, size), "Corruption!");
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Picks a dictionary size and trims the dictionary off of the data.
 | |
|  * We copy the dictionary to the roundTrip so our validation passes.
 | |
|  */
 | |
| static size_t state_trimDict(state_t* state)
 | |
| {
 | |
|     /* 64 KB is the max dict size, allow slightly beyond that to test trim. */
 | |
|     uint32_t maxDictSize = MIN(70 * 1024, state->data.size);
 | |
|     size_t const dictSize = FUZZ_rand32(&state->seed, 0, maxDictSize);
 | |
|     DEBUGLOG(2, "dictSize = %zu", dictSize);
 | |
|     FUZZ_ASSERT(state->data.pos == 0);
 | |
|     FUZZ_ASSERT(state->roundTrip.pos == 0);
 | |
|     memcpy(state->roundTrip.buf, state->data.buf, dictSize);
 | |
|     state->data.pos += dictSize;
 | |
|     state->roundTrip.pos += dictSize;
 | |
|     return dictSize;
 | |
| }
 | |
| 
 | |
| static void state_prefixRoundTrip(state_t* state)
 | |
| {
 | |
|     while (state->data.pos != state->data.size) {
 | |
|         char const* src = state->data.buf + state->data.pos;
 | |
|         char* dst = state->compressed.buf + state->compressed.pos;
 | |
|         int const srcRemaining = state->data.size - state->data.pos;
 | |
|         int const srcSize = FUZZ_rand32(&state->seed, 0, srcRemaining);
 | |
|         int const dstCapacity = state->compressed.size - state->compressed.pos;
 | |
|         int const cSize = LZ4_compress_fast_continue(state->cstream, src, dst,
 | |
|                                                      srcSize, dstCapacity, 0);
 | |
|         FUZZ_ASSERT(cSize > 0);
 | |
|         state->data.pos += srcSize;
 | |
|         state->compressed.pos += cSize;
 | |
|         state_decompress(state, dst, cSize);
 | |
|     }
 | |
| }
 | |
| 
 | |
| static void state_extDictRoundTrip(state_t* state)
 | |
| {
 | |
|     int i = 0;
 | |
|     cursor_t data2 = cursor_create(state->data.size);
 | |
|     memcpy(data2.buf, state->data.buf, state->data.size);
 | |
|     while (state->data.pos != state->data.size) {
 | |
|         char const* data = (i++ & 1) ? state->data.buf : data2.buf;
 | |
|         char const* src = data + state->data.pos;
 | |
|         char* dst = state->compressed.buf + state->compressed.pos;
 | |
|         int const srcRemaining = state->data.size - state->data.pos;
 | |
|         int const srcSize = FUZZ_rand32(&state->seed, 0, srcRemaining);
 | |
|         int const dstCapacity = state->compressed.size - state->compressed.pos;
 | |
|         int const cSize = LZ4_compress_fast_continue(state->cstream, src, dst,
 | |
|                                                      srcSize, dstCapacity, 0);
 | |
|         FUZZ_ASSERT(cSize > 0);
 | |
|         state->data.pos += srcSize;
 | |
|         state->compressed.pos += cSize;
 | |
|         state_decompress(state, dst, cSize);
 | |
|     }
 | |
|     cursor_free(data2);
 | |
| }
 | |
| 
 | |
| static void state_randomRoundTrip(state_t* state, round_trip_t rt0,
 | |
|                                   round_trip_t rt1)
 | |
| {
 | |
|     if (FUZZ_rand32(&state->seed, 0, 1)) {
 | |
|       rt0(state);
 | |
|     } else {
 | |
|       rt1(state);
 | |
|     }
 | |
| }
 | |
| 
 | |
| static void state_loadDictRoundTrip(state_t* state)
 | |
| {
 | |
|     char const* dict = state->data.buf;
 | |
|     size_t const dictSize = state_trimDict(state);
 | |
|     LZ4_loadDict(state->cstream, dict, dictSize);
 | |
|     LZ4_setStreamDecode(state->dstream, dict, dictSize);
 | |
|     state_randomRoundTrip(state, state_prefixRoundTrip, state_extDictRoundTrip);
 | |
| }
 | |
| 
 | |
| static void state_attachDictRoundTrip(state_t* state)
 | |
| {
 | |
|     char const* dict = state->data.buf;
 | |
|     size_t const dictSize = state_trimDict(state);
 | |
|     LZ4_stream_t* dictStream = LZ4_createStream();
 | |
|     LZ4_loadDict(dictStream, dict, dictSize);
 | |
|     LZ4_attach_dictionary(state->cstream, dictStream);
 | |
|     LZ4_setStreamDecode(state->dstream, dict, dictSize);
 | |
|     state_randomRoundTrip(state, state_prefixRoundTrip, state_extDictRoundTrip);
 | |
|     LZ4_freeStream(dictStream);
 | |
| }
 | |
| 
 | |
| static void state_prefixHCRoundTrip(state_t* state)
 | |
| {
 | |
|     while (state->data.pos != state->data.size) {
 | |
|         char const* src = state->data.buf + state->data.pos;
 | |
|         char* dst = state->compressed.buf + state->compressed.pos;
 | |
|         int const srcRemaining = state->data.size - state->data.pos;
 | |
|         int const srcSize = FUZZ_rand32(&state->seed, 0, srcRemaining);
 | |
|         int const dstCapacity = state->compressed.size - state->compressed.pos;
 | |
|         int const cSize = LZ4_compress_HC_continue(state->cstreamHC, src, dst,
 | |
|                                                    srcSize, dstCapacity);
 | |
|         FUZZ_ASSERT(cSize > 0);
 | |
|         state->data.pos += srcSize;
 | |
|         state->compressed.pos += cSize;
 | |
|         state_decompress(state, dst, cSize);
 | |
|     }
 | |
| }
 | |
| 
 | |
| static void state_extDictHCRoundTrip(state_t* state)
 | |
| {
 | |
|     int i = 0;
 | |
|     cursor_t data2 = cursor_create(state->data.size);
 | |
|     DEBUGLOG(2, "extDictHC");
 | |
|     memcpy(data2.buf, state->data.buf, state->data.size);
 | |
|     while (state->data.pos != state->data.size) {
 | |
|         char const* data = (i++ & 1) ? state->data.buf : data2.buf;
 | |
|         char const* src = data + state->data.pos;
 | |
|         char* dst = state->compressed.buf + state->compressed.pos;
 | |
|         int const srcRemaining = state->data.size - state->data.pos;
 | |
|         int const srcSize = FUZZ_rand32(&state->seed, 0, srcRemaining);
 | |
|         int const dstCapacity = state->compressed.size - state->compressed.pos;
 | |
|         int const cSize = LZ4_compress_HC_continue(state->cstreamHC, src, dst,
 | |
|                                                    srcSize, dstCapacity);
 | |
|         FUZZ_ASSERT(cSize > 0);
 | |
|         DEBUGLOG(2, "srcSize = %d", srcSize);
 | |
|         state->data.pos += srcSize;
 | |
|         state->compressed.pos += cSize;
 | |
|         state_decompress(state, dst, cSize);
 | |
|     }
 | |
|     cursor_free(data2);
 | |
| }
 | |
| 
 | |
| static void state_loadDictHCRoundTrip(state_t* state)
 | |
| {
 | |
|     char const* dict = state->data.buf;
 | |
|     size_t const dictSize = state_trimDict(state);
 | |
|     LZ4_loadDictHC(state->cstreamHC, dict, dictSize);
 | |
|     LZ4_setStreamDecode(state->dstream, dict, dictSize);
 | |
|     state_randomRoundTrip(state, state_prefixHCRoundTrip,
 | |
|                           state_extDictHCRoundTrip);
 | |
| }
 | |
| 
 | |
| static void state_attachDictHCRoundTrip(state_t* state)
 | |
| {
 | |
|     char const* dict = state->data.buf;
 | |
|     size_t const dictSize = state_trimDict(state);
 | |
|     LZ4_streamHC_t* dictStream = LZ4_createStreamHC();
 | |
|     LZ4_setCompressionLevel(dictStream, state->level);
 | |
|     LZ4_loadDictHC(dictStream, dict, dictSize);
 | |
|     LZ4_attach_HC_dictionary(state->cstreamHC, dictStream);
 | |
|     LZ4_setStreamDecode(state->dstream, dict, dictSize);
 | |
|     state_randomRoundTrip(state, state_prefixHCRoundTrip,
 | |
|                           state_extDictHCRoundTrip);
 | |
|     LZ4_freeStreamHC(dictStream);
 | |
| }
 | |
| 
 | |
| round_trip_t roundTrips[] = {
 | |
|   &state_prefixRoundTrip,
 | |
|   &state_extDictRoundTrip,
 | |
|   &state_loadDictRoundTrip,
 | |
|   &state_attachDictRoundTrip,
 | |
|   &state_prefixHCRoundTrip,
 | |
|   &state_extDictHCRoundTrip,
 | |
|   &state_loadDictHCRoundTrip,
 | |
|   &state_attachDictHCRoundTrip,
 | |
| };
 | |
| 
 | |
| int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
 | |
| {
 | |
|     uint32_t seed = FUZZ_seed(&data, &size);
 | |
|     state_t state = state_create((char const*)data, size, seed);
 | |
|     const int n = sizeof(roundTrips) / sizeof(round_trip_t);
 | |
|     int i;
 | |
| 
 | |
|     for (i = 0; i < n; ++i) {
 | |
|         DEBUGLOG(2, "Round trip %d", i);
 | |
|         state_reset(&state, seed);
 | |
|         roundTrips[i](&state);
 | |
|         state_checkRoundTrip(&state);
 | |
|     }
 | |
| 
 | |
|     state_free(state);
 | |
| 
 | |
|     return 0;
 | |
| }
 | |
| 
 |