• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 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  * You may select, at your option, one of the above-listed licenses.
9  */
10 
11 /**
12  * This fuzz target performs a zstd round-trip test (compress & decompress),
13  * compares the result with the original, and calls abort() on corruption.
14  */
15 
16 #define ZSTD_STATIC_LINKING_ONLY
17 
18 #include <stddef.h>
19 #include <stdlib.h>
20 #include <stdio.h>
21 #include <string.h>
22 #include "fuzz_helpers.h"
23 #include "zstd_helpers.h"
24 #include "fuzz_data_producer.h"
25 
26 ZSTD_CCtx *cctx = NULL;
27 static ZSTD_DCtx *dctx = NULL;
28 static uint8_t* cBuf = NULL;
29 static uint8_t* rBuf = NULL;
30 static size_t bufSize = 0;
31 
makeOutBuffer(uint8_t * dst,size_t capacity,FUZZ_dataProducer_t * producer)32 static ZSTD_outBuffer makeOutBuffer(uint8_t *dst, size_t capacity,
33                                     FUZZ_dataProducer_t *producer)
34 {
35     ZSTD_outBuffer buffer = { dst, 0, 0 };
36 
37     FUZZ_ASSERT(capacity > 0);
38     buffer.size = (FUZZ_dataProducer_uint32Range(producer, 1, capacity));
39     FUZZ_ASSERT(buffer.size <= capacity);
40 
41     return buffer;
42 }
43 
makeInBuffer(const uint8_t ** src,size_t * size,FUZZ_dataProducer_t * producer)44 static ZSTD_inBuffer makeInBuffer(const uint8_t **src, size_t *size,
45                                   FUZZ_dataProducer_t *producer)
46 {
47     ZSTD_inBuffer buffer = { *src, 0, 0 };
48 
49     FUZZ_ASSERT(*size > 0);
50     buffer.size = (FUZZ_dataProducer_uint32Range(producer, 1, *size));
51     FUZZ_ASSERT(buffer.size <= *size);
52     *src += buffer.size;
53     *size -= buffer.size;
54 
55     return buffer;
56 }
57 
compress(uint8_t * dst,size_t capacity,const uint8_t * src,size_t srcSize,const uint8_t * dict,size_t dictSize,FUZZ_dataProducer_t * producer,int refPrefix,ZSTD_dictContentType_e dictContentType)58 static size_t compress(uint8_t *dst, size_t capacity,
59                         const uint8_t *src, size_t srcSize,
60                         const uint8_t* dict, size_t dictSize,
61                         FUZZ_dataProducer_t *producer, int refPrefix,
62                         ZSTD_dictContentType_e dictContentType)
63 {
64     size_t dstSize = 0;
65     ZSTD_CCtx_reset(cctx, ZSTD_reset_session_only);
66     FUZZ_setRandomParameters(cctx, srcSize, producer);
67 
68     /* Disable checksum so we can use sizes smaller than compress bound. */
69     FUZZ_ZASSERT(ZSTD_CCtx_setParameter(cctx, ZSTD_c_checksumFlag, 0));
70     if (refPrefix)
71         FUZZ_ZASSERT(ZSTD_CCtx_refPrefix_advanced(
72             cctx, dict, dictSize,
73             dictContentType));
74     else
75         FUZZ_ZASSERT(ZSTD_CCtx_loadDictionary_advanced(
76             cctx, dict, dictSize,
77             (ZSTD_dictLoadMethod_e)FUZZ_dataProducer_uint32Range(producer, 0, 1),
78             dictContentType));
79 
80     while (srcSize > 0) {
81         ZSTD_inBuffer in = makeInBuffer(&src, &srcSize, producer);
82         /* Mode controls the action. If mode == -1 we pick a new mode */
83         int mode = -1;
84         while (in.pos < in.size || mode != -1) {
85             ZSTD_outBuffer out = makeOutBuffer(dst, capacity, producer);
86             /* Previous action finished, pick a new mode. */
87             if (mode == -1) mode = FUZZ_dataProducer_uint32Range(producer, 0, 9);
88             switch (mode) {
89                 case 0: /* fall-through */
90                 case 1: /* fall-through */
91                 case 2: {
92                     size_t const ret =
93                         ZSTD_compressStream2(cctx, &out, &in, ZSTD_e_flush);
94                     FUZZ_ZASSERT(ret);
95                     if (ret == 0)
96                         mode = -1;
97                     break;
98                 }
99                 case 3: {
100                     size_t ret =
101                         ZSTD_compressStream2(cctx, &out, &in, ZSTD_e_end);
102                     FUZZ_ZASSERT(ret);
103                     /* Reset the compressor when the frame is finished */
104                     if (ret == 0) {
105                         ZSTD_CCtx_reset(cctx, ZSTD_reset_session_only);
106                         if (FUZZ_dataProducer_uint32Range(producer, 0, 7) == 0) {
107                             size_t const remaining = in.size - in.pos;
108                             FUZZ_setRandomParameters(cctx, remaining, producer);
109                         }
110                         mode = -1;
111                     }
112                     break;
113                 }
114                 case 4: {
115                     ZSTD_inBuffer nullIn = { NULL, 0, 0 };
116                     ZSTD_outBuffer nullOut = { NULL, 0, 0 };
117                     size_t const ret = ZSTD_compressStream2(cctx, &nullOut, &nullIn, ZSTD_e_continue);
118                     FUZZ_ZASSERT(ret);
119                 }
120                 /* fall-through */
121                 default: {
122                     size_t const ret =
123                         ZSTD_compressStream2(cctx, &out, &in, ZSTD_e_continue);
124                     FUZZ_ZASSERT(ret);
125                     mode = -1;
126                 }
127             }
128             dst += out.pos;
129             dstSize += out.pos;
130             capacity -= out.pos;
131         }
132     }
133     for (;;) {
134         ZSTD_inBuffer in = {NULL, 0, 0};
135         ZSTD_outBuffer out = makeOutBuffer(dst, capacity, producer);
136         size_t const ret = ZSTD_compressStream2(cctx, &out, &in, ZSTD_e_end);
137         FUZZ_ZASSERT(ret);
138 
139         dst += out.pos;
140         dstSize += out.pos;
141         capacity -= out.pos;
142         if (ret == 0)
143             break;
144     }
145     return dstSize;
146 }
147 
LLVMFuzzerTestOneInput(const uint8_t * src,size_t size)148 int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size)
149 {
150     size_t neededBufSize;
151 
152     /* Give a random portion of src data to the producer, to use for
153     parameter generation. The rest will be used for (de)compression */
154     FUZZ_dataProducer_t *producer = FUZZ_dataProducer_create(src, size);
155     size = FUZZ_dataProducer_reserveDataPrefix(producer);
156 
157     neededBufSize = ZSTD_compressBound(size) * 15;
158 
159     /* Allocate all buffers and contexts if not already allocated */
160     if (neededBufSize > bufSize) {
161         free(cBuf);
162         free(rBuf);
163         cBuf = (uint8_t*)FUZZ_malloc(neededBufSize);
164         rBuf = (uint8_t*)FUZZ_malloc(neededBufSize);
165         bufSize = neededBufSize;
166     }
167     if (!cctx) {
168         cctx = ZSTD_createCCtx();
169         FUZZ_ASSERT(cctx);
170     }
171     if (!dctx) {
172         dctx = ZSTD_createDCtx();
173         FUZZ_ASSERT(dctx);
174     }
175 
176     {
177         ZSTD_dictContentType_e dictContentType = FUZZ_dataProducer_uint32Range(producer, 0, 2);
178         FUZZ_dict_t dict = FUZZ_train(src, size, producer);
179         int const refPrefix = FUZZ_dataProducer_uint32Range(producer, 0, 1) != 0;
180 
181         size_t const cSize = compress(cBuf, neededBufSize, src, size, dict.buff, dict.size, producer, refPrefix, dictContentType);
182 
183         if (refPrefix)
184             FUZZ_ZASSERT(ZSTD_DCtx_refPrefix_advanced(
185                 dctx, dict.buff, dict.size,
186                 dictContentType));
187         else
188             FUZZ_ZASSERT(ZSTD_DCtx_loadDictionary_advanced(
189                 dctx, dict.buff, dict.size,
190                 (ZSTD_dictLoadMethod_e)FUZZ_dataProducer_uint32Range(producer, 0, 1),
191                 dictContentType));
192         size_t const rSize =
193             ZSTD_decompressDCtx(dctx, rBuf, neededBufSize, cBuf, cSize);
194         FUZZ_ZASSERT(rSize);
195         FUZZ_ASSERT_MSG(rSize == size, "Incorrect regenerated size");
196         FUZZ_ASSERT_MSG(!FUZZ_memcmp(src, rBuf, size), "Corruption!");
197         free(dict.buff);
198     }
199 
200     FUZZ_dataProducer_free(producer);
201 #ifndef STATEFUL_FUZZING
202     ZSTD_freeCCtx(cctx); cctx = NULL;
203     ZSTD_freeDCtx(dctx); dctx = NULL;
204 #endif
205     return 0;
206 }
207