1 /*
2  * Copyright (c) 2016-present, Facebook, Inc.
3  * All rights reserved.
4  *
5  * This source code is licensed under both the BSD-style license (found in the
6  * LICENSE file in the root directory of this source tree) and the GPLv2 (found
7  * in the COPYING file in the root directory of this source tree).
8  */
9 
10 /**
11  * This fuzz target performs a zstd round-trip test (compress & decompress) with
12  * a dictionary, compares the result with the original, and calls abort() on
13  * corruption.
14  */
15 
16 #include <stddef.h>
17 #include <stdlib.h>
18 #include <stdio.h>
19 #include <string.h>
20 #include "fuzz_helpers.h"
21 #include "zstd_helpers.h"
22 #include "fuzz_data_producer.h"
23 
24 static ZSTD_CCtx *cctx = NULL;
25 static ZSTD_DCtx *dctx = NULL;
26 
roundTripTest(void * result,size_t resultCapacity,void * compressed,size_t compressedCapacity,const void * src,size_t srcSize,FUZZ_dataProducer_t * producer)27 static size_t roundTripTest(void *result, size_t resultCapacity,
28                             void *compressed, size_t compressedCapacity,
29                             const void *src, size_t srcSize,
30                             FUZZ_dataProducer_t *producer)
31 {
32     ZSTD_dictContentType_e dictContentType = ZSTD_dct_auto;
33     FUZZ_dict_t dict = FUZZ_train(src, srcSize, producer);
34     size_t cSize;
35     if (FUZZ_dataProducer_uint32Range(producer, 0, 15) == 0) {
36         int const cLevel = FUZZ_dataProducer_int32Range(producer, kMinClevel, kMaxClevel);
37 
38         cSize = ZSTD_compress_usingDict(cctx,
39                 compressed, compressedCapacity,
40                 src, srcSize,
41                 dict.buff, dict.size,
42                 cLevel);
43     } else {
44         dictContentType = FUZZ_dataProducer_uint32Range(producer, 0, 2);
45         FUZZ_setRandomParameters(cctx, srcSize, producer);
46         /* Disable checksum so we can use sizes smaller than compress bound. */
47         FUZZ_ZASSERT(ZSTD_CCtx_setParameter(cctx, ZSTD_c_checksumFlag, 0));
48         FUZZ_ZASSERT(ZSTD_CCtx_loadDictionary_advanced(
49                 cctx, dict.buff, dict.size,
50                 (ZSTD_dictLoadMethod_e)FUZZ_dataProducer_uint32Range(producer, 0, 1),
51                 dictContentType));
52         cSize = ZSTD_compress2(cctx, compressed, compressedCapacity, src, srcSize);
53     }
54     FUZZ_ZASSERT(cSize);
55     FUZZ_ZASSERT(ZSTD_DCtx_loadDictionary_advanced(
56         dctx, dict.buff, dict.size,
57         (ZSTD_dictLoadMethod_e)FUZZ_dataProducer_uint32Range(producer, 0, 1),
58         dictContentType));
59     {
60         size_t const ret = ZSTD_decompressDCtx(
61                 dctx, result, resultCapacity, compressed, cSize);
62         free(dict.buff);
63         return ret;
64     }
65 }
66 
LLVMFuzzerTestOneInput(const uint8_t * src,size_t size)67 int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size)
68 {
69     /* Give a random portion of src data to the producer, to use for
70     parameter generation. The rest will be used for (de)compression */
71     FUZZ_dataProducer_t *producer = FUZZ_dataProducer_create(src, size);
72     size = FUZZ_dataProducer_reserveDataPrefix(producer);
73 
74     size_t const rBufSize = size;
75     void* rBuf = malloc(rBufSize);
76     size_t cBufSize = ZSTD_compressBound(size);
77     void *cBuf;
78     /* Half of the time fuzz with a 1 byte smaller output size.
79      * This will still succeed because we force the checksum to be disabled,
80      * giving us 4 bytes of overhead.
81      */
82     cBufSize -= FUZZ_dataProducer_uint32Range(producer, 0, 1);
83     cBuf = malloc(cBufSize);
84 
85     if (!cctx) {
86         cctx = ZSTD_createCCtx();
87         FUZZ_ASSERT(cctx);
88     }
89     if (!dctx) {
90         dctx = ZSTD_createDCtx();
91         FUZZ_ASSERT(dctx);
92     }
93 
94     {
95         size_t const result =
96             roundTripTest(rBuf, rBufSize, cBuf, cBufSize, src, size, producer);
97         FUZZ_ZASSERT(result);
98         FUZZ_ASSERT_MSG(result == size, "Incorrect regenerated size");
99         FUZZ_ASSERT_MSG(!memcmp(src, rBuf, size), "Corruption!");
100     }
101     free(rBuf);
102     free(cBuf);
103     FUZZ_dataProducer_free(producer);
104 #ifndef STATEFUL_FUZZING
105     ZSTD_freeCCtx(cctx); cctx = NULL;
106     ZSTD_freeDCtx(dctx); dctx = NULL;
107 #endif
108     return 0;
109 }
110