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),
12  * compares the result with the original, and calls abort() on corruption.
13  */
14 
15 #define ZSTD_STATIC_LINKING_ONLY
16 
17 #include <stddef.h>
18 #include <stdlib.h>
19 #include <stdio.h>
20 #include <string.h>
21 #include "fuzz_helpers.h"
22 #include "zstd_helpers.h"
23 
24 ZSTD_CCtx *cctx = NULL;
25 static ZSTD_DCtx *dctx = NULL;
26 static uint8_t* cBuf = NULL;
27 static uint8_t* rBuf = NULL;
28 static size_t bufSize = 0;
29 static uint32_t seed;
30 
makeOutBuffer(uint8_t * dst,size_t capacity)31 static ZSTD_outBuffer makeOutBuffer(uint8_t *dst, size_t capacity)
32 {
33     ZSTD_outBuffer buffer = { dst, 0, 0 };
34 
35     FUZZ_ASSERT(capacity > 0);
36     buffer.size = (FUZZ_rand(&seed) % capacity) + 1;
37     FUZZ_ASSERT(buffer.size <= capacity);
38 
39     return buffer;
40 }
41 
makeInBuffer(const uint8_t ** src,size_t * size)42 static ZSTD_inBuffer makeInBuffer(const uint8_t **src, size_t *size)
43 {
44     ZSTD_inBuffer buffer = { *src, 0, 0 };
45 
46     FUZZ_ASSERT(*size > 0);
47     buffer.size = (FUZZ_rand(&seed) % *size) + 1;
48     FUZZ_ASSERT(buffer.size <= *size);
49     *src += buffer.size;
50     *size -= buffer.size;
51 
52     return buffer;
53 }
54 
compress(uint8_t * dst,size_t capacity,const uint8_t * src,size_t srcSize)55 static size_t compress(uint8_t *dst, size_t capacity,
56                        const uint8_t *src, size_t srcSize)
57 {
58     size_t dstSize = 0;
59     ZSTD_CCtx_reset(cctx, ZSTD_reset_session_only);
60     FUZZ_setRandomParameters(cctx, srcSize, &seed);
61 
62     while (srcSize > 0) {
63         ZSTD_inBuffer in = makeInBuffer(&src, &srcSize);
64         /* Mode controls the action. If mode == -1 we pick a new mode */
65         int mode = -1;
66         while (in.pos < in.size || mode != -1) {
67             ZSTD_outBuffer out = makeOutBuffer(dst, capacity);
68             /* Previous action finished, pick a new mode. */
69             if (mode == -1) mode = FUZZ_rand(&seed) % 10;
70             switch (mode) {
71                 case 0: /* fall-though */
72                 case 1: /* fall-though */
73                 case 2: {
74                     size_t const ret =
75                         ZSTD_compressStream2(cctx, &out, &in, ZSTD_e_flush);
76                     FUZZ_ZASSERT(ret);
77                     if (ret == 0)
78                         mode = -1;
79                     break;
80                 }
81                 case 3: {
82                     size_t ret =
83                         ZSTD_compressStream2(cctx, &out, &in, ZSTD_e_end);
84                     FUZZ_ZASSERT(ret);
85                     /* Reset the compressor when the frame is finished */
86                     if (ret == 0) {
87                         ZSTD_CCtx_reset(cctx, ZSTD_reset_session_only);
88                         if ((FUZZ_rand(&seed) & 7) == 0) {
89                             size_t const remaining = in.size - in.pos;
90                             FUZZ_setRandomParameters(cctx, remaining, &seed);
91                         }
92                         mode = -1;
93                     }
94                     break;
95                 }
96                 default: {
97                     size_t const ret =
98                         ZSTD_compressStream2(cctx, &out, &in, ZSTD_e_continue);
99                     FUZZ_ZASSERT(ret);
100                     mode = -1;
101                 }
102             }
103             dst += out.pos;
104             dstSize += out.pos;
105             capacity -= out.pos;
106         }
107     }
108     for (;;) {
109         ZSTD_inBuffer in = {NULL, 0, 0};
110         ZSTD_outBuffer out = makeOutBuffer(dst, capacity);
111         size_t const ret = ZSTD_compressStream2(cctx, &out, &in, ZSTD_e_end);
112         FUZZ_ZASSERT(ret);
113 
114         dst += out.pos;
115         dstSize += out.pos;
116         capacity -= out.pos;
117         if (ret == 0)
118             break;
119     }
120     return dstSize;
121 }
122 
LLVMFuzzerTestOneInput(const uint8_t * src,size_t size)123 int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size)
124 {
125     size_t neededBufSize;
126 
127     seed = FUZZ_seed(&src, &size);
128     neededBufSize = ZSTD_compressBound(size) * 2;
129 
130     /* Allocate all buffers and contexts if not already allocated */
131     if (neededBufSize > bufSize) {
132         free(cBuf);
133         free(rBuf);
134         cBuf = (uint8_t*)malloc(neededBufSize);
135         rBuf = (uint8_t*)malloc(neededBufSize);
136         bufSize = neededBufSize;
137         FUZZ_ASSERT(cBuf && rBuf);
138     }
139     if (!cctx) {
140         cctx = ZSTD_createCCtx();
141         FUZZ_ASSERT(cctx);
142     }
143     if (!dctx) {
144         dctx = ZSTD_createDCtx();
145         FUZZ_ASSERT(dctx);
146     }
147 
148     {
149         size_t const cSize = compress(cBuf, neededBufSize, src, size);
150         size_t const rSize =
151             ZSTD_decompressDCtx(dctx, rBuf, neededBufSize, cBuf, cSize);
152         FUZZ_ZASSERT(rSize);
153         FUZZ_ASSERT_MSG(rSize == size, "Incorrect regenerated size");
154         FUZZ_ASSERT_MSG(!memcmp(src, rBuf, size), "Corruption!");
155     }
156 
157 #ifndef STATEFUL_FUZZING
158     ZSTD_freeCCtx(cctx); cctx = NULL;
159     ZSTD_freeDCtx(dctx); dctx = NULL;
160 #endif
161     return 0;
162 }
163