/src/zstd/tests/fuzz/simple_round_trip.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (c) Meta Platforms, Inc. and affiliates. |
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 | | #include "fuzz_third_party_seq_prod.h" |
26 | | |
27 | | static ZSTD_CCtx *cctx = NULL; |
28 | | static ZSTD_DCtx *dctx = NULL; |
29 | | |
30 | | static size_t getDecompressionMargin(void const* compressed, size_t cSize, size_t srcSize, int hasSmallBlocks, int maxBlockSize) |
31 | 20.6k | { |
32 | 20.6k | size_t margin = ZSTD_decompressionMargin(compressed, cSize); |
33 | 20.6k | if (!hasSmallBlocks) { |
34 | | /* The macro should be correct in this case, but it may be smaller |
35 | | * because of e.g. block splitting, so take the smaller of the two. |
36 | | */ |
37 | 7.91k | ZSTD_FrameHeader zfh; |
38 | 7.91k | size_t marginM; |
39 | 7.91k | FUZZ_ZASSERT(ZSTD_getFrameHeader(&zfh, compressed, cSize)); |
40 | 7.91k | if (maxBlockSize == 0) { |
41 | 5.51k | maxBlockSize = zfh.blockSizeMax; |
42 | 5.51k | } else { |
43 | 2.40k | maxBlockSize = MIN(maxBlockSize, (int)zfh.blockSizeMax); |
44 | 2.40k | } |
45 | 7.91k | marginM = ZSTD_DECOMPRESSION_MARGIN(srcSize, maxBlockSize); |
46 | 7.91k | if (marginM < margin) |
47 | 513 | margin = marginM; |
48 | 7.91k | } |
49 | 20.6k | return margin; |
50 | 20.6k | } |
51 | | |
52 | | static size_t roundTripTest(void *result, size_t resultCapacity, |
53 | | void *compressed, size_t compressedCapacity, |
54 | | const void *src, size_t srcSize, |
55 | | FUZZ_dataProducer_t *producer) |
56 | 20.6k | { |
57 | 20.6k | size_t cSize; |
58 | 20.6k | size_t dSize; |
59 | 20.6k | int targetCBlockSize = 0; |
60 | 20.6k | int maxBlockSize = 0; |
61 | 20.6k | if (FUZZ_dataProducer_uint32Range(producer, 0, 1)) { |
62 | 15.1k | size_t const remainingBytes = FUZZ_dataProducer_remainingBytes(producer); |
63 | 15.1k | FUZZ_setRandomParameters(cctx, srcSize, producer); |
64 | 15.1k | cSize = ZSTD_compress2(cctx, compressed, compressedCapacity, src, srcSize); |
65 | 15.1k | FUZZ_ZASSERT(cSize); |
66 | 15.1k | FUZZ_ZASSERT(ZSTD_CCtx_getParameter(cctx, ZSTD_c_targetCBlockSize, &targetCBlockSize)); |
67 | 15.1k | FUZZ_ZASSERT(ZSTD_CCtx_getParameter(cctx, ZSTD_c_maxBlockSize, &maxBlockSize)); |
68 | | // Compress a second time and check for determinism |
69 | 15.1k | { |
70 | 15.1k | size_t const cSize0 = cSize; |
71 | 15.1k | XXH64_hash_t const hash0 = XXH64(compressed, cSize, 0); |
72 | 15.1k | FUZZ_dataProducer_rollBack(producer, remainingBytes); |
73 | 15.1k | FUZZ_setRandomParameters(cctx, srcSize, producer); |
74 | 15.1k | cSize = ZSTD_compress2(cctx, compressed, compressedCapacity, src, srcSize); |
75 | 15.1k | FUZZ_ASSERT(cSize == cSize0); |
76 | 15.1k | FUZZ_ASSERT(XXH64(compressed, cSize, 0) == hash0); |
77 | 15.1k | } |
78 | 15.1k | } else { |
79 | 5.51k | int const cLevel = FUZZ_dataProducer_int32Range(producer, kMinClevel, kMaxClevel); |
80 | 5.51k | cSize = ZSTD_compressCCtx( |
81 | 5.51k | cctx, compressed, compressedCapacity, src, srcSize, cLevel); |
82 | 5.51k | FUZZ_ZASSERT(cSize); |
83 | | // Compress a second time and check for determinism |
84 | 5.51k | { |
85 | 5.51k | size_t const cSize0 = cSize; |
86 | 5.51k | XXH64_hash_t const hash0 = XXH64(compressed, cSize, 0); |
87 | 5.51k | cSize = ZSTD_compressCCtx( |
88 | 5.51k | cctx, compressed, compressedCapacity, src, srcSize, cLevel); |
89 | 5.51k | FUZZ_ASSERT(cSize == cSize0); |
90 | 5.51k | FUZZ_ASSERT(XXH64(compressed, cSize, 0) == hash0); |
91 | 5.51k | } |
92 | 5.51k | } |
93 | 20.6k | if (FUZZ_dataProducer_uint32Range(producer, 0, 1)) { |
94 | 3.38k | FUZZ_ZASSERT(ZSTD_DCtx_setParameter(dctx, ZSTD_d_maxBlockSize, maxBlockSize)); |
95 | 3.38k | } |
96 | 20.6k | dSize = ZSTD_decompressDCtx(dctx, result, resultCapacity, compressed, cSize); |
97 | 20.6k | FUZZ_ZASSERT(dSize); |
98 | 20.6k | FUZZ_ASSERT_MSG(dSize == srcSize, "Incorrect regenerated size"); |
99 | 20.6k | FUZZ_ASSERT_MSG(!FUZZ_memcmp(src, result, dSize), "Corruption!"); |
100 | | |
101 | 20.6k | { |
102 | 20.6k | size_t margin = getDecompressionMargin(compressed, cSize, srcSize, targetCBlockSize, maxBlockSize); |
103 | 20.6k | size_t const outputSize = srcSize + margin; |
104 | 20.6k | char* const output = (char*)FUZZ_malloc(outputSize); |
105 | 20.6k | char* const input = output + outputSize - cSize; |
106 | 20.6k | FUZZ_ASSERT(outputSize >= cSize); |
107 | 20.6k | memcpy(input, compressed, cSize); |
108 | | |
109 | 20.6k | dSize = ZSTD_decompressDCtx(dctx, output, outputSize, input, cSize); |
110 | 20.6k | FUZZ_ZASSERT(dSize); |
111 | 20.6k | FUZZ_ASSERT_MSG(dSize == srcSize, "Incorrect regenerated size"); |
112 | 20.6k | FUZZ_ASSERT_MSG(!FUZZ_memcmp(src, output, srcSize), "Corruption!"); |
113 | | |
114 | 20.6k | free(output); |
115 | 20.6k | } |
116 | | |
117 | | /* When superblock is enabled make sure we don't expand the block more than expected. |
118 | | * NOTE: This test is currently disabled because superblock mode can arbitrarily |
119 | | * expand the block in the worst case. Once superblock mode has been improved we can |
120 | | * re-enable this test. |
121 | | */ |
122 | 20.6k | if (0 && targetCBlockSize != 0) { |
123 | 0 | size_t normalCSize; |
124 | 0 | FUZZ_ZASSERT(ZSTD_CCtx_setParameter(cctx, ZSTD_c_targetCBlockSize, 0)); |
125 | 0 | normalCSize = ZSTD_compress2(cctx, compressed, compressedCapacity, src, srcSize); |
126 | 0 | FUZZ_ZASSERT(normalCSize); |
127 | 0 | { |
128 | 0 | size_t const bytesPerBlock = 3 /* block header */ |
129 | 0 | + 5 /* Literal header */ |
130 | 0 | + 6 /* Huffman jump table */ |
131 | 0 | + 3 /* number of sequences */ |
132 | 0 | + 1 /* symbol compression modes */; |
133 | 0 | size_t const expectedExpansion = bytesPerBlock * (1 + (normalCSize / MAX(1, targetCBlockSize))); |
134 | 0 | size_t const allowedExpansion = (srcSize >> 3) + 5 * expectedExpansion + 10; |
135 | 0 | FUZZ_ASSERT(cSize <= normalCSize + allowedExpansion); |
136 | 0 | } |
137 | 0 | } |
138 | 20.6k | return dSize; |
139 | 20.6k | } |
140 | | |
141 | | int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size) |
142 | 20.6k | { |
143 | 20.6k | FUZZ_SEQ_PROD_SETUP(); |
144 | | |
145 | 20.6k | size_t const rBufSize = size; |
146 | 20.6k | void* rBuf = FUZZ_malloc(rBufSize); |
147 | 20.6k | size_t cBufSize = ZSTD_compressBound(size); |
148 | 20.6k | void* cBuf; |
149 | | |
150 | | /* Give a random portion of src data to the producer, to use for |
151 | | parameter generation. The rest will be used for (de)compression */ |
152 | 20.6k | FUZZ_dataProducer_t *producer = FUZZ_dataProducer_create(src, size); |
153 | 20.6k | size = FUZZ_dataProducer_reserveDataPrefix(producer); |
154 | | |
155 | | /* Half of the time fuzz with a 1 byte smaller output size. |
156 | | * This will still succeed because we don't use a dictionary, so the dictID |
157 | | * field is empty, giving us 4 bytes of overhead. |
158 | | */ |
159 | 20.6k | cBufSize -= FUZZ_dataProducer_uint32Range(producer, 0, 1); |
160 | | |
161 | 20.6k | cBuf = FUZZ_malloc(cBufSize); |
162 | | |
163 | 20.6k | if (!cctx) { |
164 | 20.6k | cctx = ZSTD_createCCtx(); |
165 | 20.6k | FUZZ_ASSERT(cctx); |
166 | 20.6k | } |
167 | 20.6k | if (!dctx) { |
168 | 20.6k | dctx = ZSTD_createDCtx(); |
169 | 20.6k | FUZZ_ASSERT(dctx); |
170 | 20.6k | } |
171 | | |
172 | 20.6k | roundTripTest(rBuf, rBufSize, cBuf, cBufSize, src, size, producer); |
173 | 20.6k | free(rBuf); |
174 | 20.6k | free(cBuf); |
175 | 20.6k | FUZZ_dataProducer_free(producer); |
176 | 20.6k | #ifndef STATEFUL_FUZZING |
177 | 20.6k | ZSTD_freeCCtx(cctx); cctx = NULL; |
178 | 20.6k | ZSTD_freeDCtx(dctx); dctx = NULL; |
179 | 20.6k | #endif |
180 | 20.6k | FUZZ_SEQ_PROD_TEARDOWN(); |
181 | 20.6k | return 0; |
182 | 20.6k | } |