xref: /aosp_15_r20/external/zstd/tests/fuzz/dictionary_round_trip.c (revision 01826a4963a0d8a59bc3812d29bdf0fb76416722)
1*01826a49SYabin Cui /*
2*01826a49SYabin Cui  * Copyright (c) Meta Platforms, Inc. and affiliates.
3*01826a49SYabin Cui  * All rights reserved.
4*01826a49SYabin Cui  *
5*01826a49SYabin Cui  * This source code is licensed under both the BSD-style license (found in the
6*01826a49SYabin Cui  * LICENSE file in the root directory of this source tree) and the GPLv2 (found
7*01826a49SYabin Cui  * in the COPYING file in the root directory of this source tree).
8*01826a49SYabin Cui  * You may select, at your option, one of the above-listed licenses.
9*01826a49SYabin Cui  */
10*01826a49SYabin Cui 
11*01826a49SYabin Cui /**
12*01826a49SYabin Cui  * This fuzz target performs a zstd round-trip test (compress & decompress) with
13*01826a49SYabin Cui  * a dictionary, compares the result with the original, and calls abort() on
14*01826a49SYabin Cui  * corruption.
15*01826a49SYabin Cui  */
16*01826a49SYabin Cui 
17*01826a49SYabin Cui #include <stddef.h>
18*01826a49SYabin Cui #include <stdlib.h>
19*01826a49SYabin Cui #include <stdio.h>
20*01826a49SYabin Cui #include <string.h>
21*01826a49SYabin Cui #include "fuzz_helpers.h"
22*01826a49SYabin Cui #include "zstd_helpers.h"
23*01826a49SYabin Cui #include "fuzz_data_producer.h"
24*01826a49SYabin Cui #include "fuzz_third_party_seq_prod.h"
25*01826a49SYabin Cui 
26*01826a49SYabin Cui static ZSTD_CCtx* cctx = NULL;
27*01826a49SYabin Cui static ZSTD_DCtx* dctx = NULL;
28*01826a49SYabin Cui 
roundTripTest(void * result,size_t resultCapacity,void * compressed,size_t compressedCapacity,const void * src,size_t srcSize,FUZZ_dataProducer_t * producer)29*01826a49SYabin Cui static size_t roundTripTest(void* result, size_t resultCapacity,
30*01826a49SYabin Cui                             void* compressed, size_t compressedCapacity,
31*01826a49SYabin Cui                             const void* src, size_t srcSize,
32*01826a49SYabin Cui                             FUZZ_dataProducer_t* producer)
33*01826a49SYabin Cui {
34*01826a49SYabin Cui     ZSTD_dictContentType_e dictContentType = ZSTD_dct_auto;
35*01826a49SYabin Cui     FUZZ_dict_t dict = FUZZ_train(src, srcSize, producer);
36*01826a49SYabin Cui     int const refPrefix = FUZZ_dataProducer_uint32Range(producer, 0, 1) != 0;
37*01826a49SYabin Cui     size_t cSize;
38*01826a49SYabin Cui     if (FUZZ_dataProducer_uint32Range(producer, 0, 15) == 0) {
39*01826a49SYabin Cui         int const cLevel = FUZZ_dataProducer_int32Range(producer, kMinClevel, kMaxClevel);
40*01826a49SYabin Cui 
41*01826a49SYabin Cui         cSize = ZSTD_compress_usingDict(cctx,
42*01826a49SYabin Cui                 compressed, compressedCapacity,
43*01826a49SYabin Cui                 src, srcSize,
44*01826a49SYabin Cui                 dict.buff, dict.size,
45*01826a49SYabin Cui                 cLevel);
46*01826a49SYabin Cui         FUZZ_ZASSERT(cSize);
47*01826a49SYabin Cui         // Compress a second time and check for determinism
48*01826a49SYabin Cui         {
49*01826a49SYabin Cui             size_t const cSize0 = cSize;
50*01826a49SYabin Cui             XXH64_hash_t const hash0 = XXH64(compressed, cSize, 0);
51*01826a49SYabin Cui             cSize = ZSTD_compress_usingDict(cctx,
52*01826a49SYabin Cui                     compressed, compressedCapacity,
53*01826a49SYabin Cui                     src, srcSize,
54*01826a49SYabin Cui                     dict.buff, dict.size,
55*01826a49SYabin Cui                     cLevel);
56*01826a49SYabin Cui             FUZZ_ASSERT(cSize == cSize0);
57*01826a49SYabin Cui             FUZZ_ASSERT(XXH64(compressed, cSize, 0) == hash0);
58*01826a49SYabin Cui         }
59*01826a49SYabin Cui     } else {
60*01826a49SYabin Cui         size_t remainingBytes;
61*01826a49SYabin Cui         dictContentType = FUZZ_dataProducer_uint32Range(producer, 0, 2);
62*01826a49SYabin Cui         remainingBytes = FUZZ_dataProducer_remainingBytes(producer);
63*01826a49SYabin Cui         FUZZ_setRandomParameters(cctx, srcSize, producer);
64*01826a49SYabin Cui         /* Disable checksum so we can use sizes smaller than compress bound. */
65*01826a49SYabin Cui         FUZZ_ZASSERT(ZSTD_CCtx_setParameter(cctx, ZSTD_c_checksumFlag, 0));
66*01826a49SYabin Cui         if (refPrefix)
67*01826a49SYabin Cui             FUZZ_ZASSERT(ZSTD_CCtx_refPrefix_advanced(
68*01826a49SYabin Cui                 cctx, dict.buff, dict.size,
69*01826a49SYabin Cui                 dictContentType));
70*01826a49SYabin Cui         else
71*01826a49SYabin Cui             FUZZ_ZASSERT(ZSTD_CCtx_loadDictionary_advanced(
72*01826a49SYabin Cui                 cctx, dict.buff, dict.size,
73*01826a49SYabin Cui                 (ZSTD_dictLoadMethod_e)FUZZ_dataProducer_uint32Range(producer, 0, 1),
74*01826a49SYabin Cui                 dictContentType));
75*01826a49SYabin Cui         cSize = ZSTD_compress2(cctx, compressed, compressedCapacity, src, srcSize);
76*01826a49SYabin Cui         FUZZ_ZASSERT(cSize);
77*01826a49SYabin Cui         // Compress a second time and check for determinism
78*01826a49SYabin Cui         {
79*01826a49SYabin Cui             size_t const cSize0 = cSize;
80*01826a49SYabin Cui             XXH64_hash_t const hash0 = XXH64(compressed, cSize, 0);
81*01826a49SYabin Cui             FUZZ_dataProducer_rollBack(producer, remainingBytes);
82*01826a49SYabin Cui             FUZZ_setRandomParameters(cctx, srcSize, producer);
83*01826a49SYabin Cui             FUZZ_ZASSERT(ZSTD_CCtx_setParameter(cctx, ZSTD_c_checksumFlag, 0));
84*01826a49SYabin Cui             if (refPrefix)
85*01826a49SYabin Cui                 FUZZ_ZASSERT(ZSTD_CCtx_refPrefix_advanced(
86*01826a49SYabin Cui                     cctx, dict.buff, dict.size,
87*01826a49SYabin Cui                     dictContentType));
88*01826a49SYabin Cui             cSize = ZSTD_compress2(cctx, compressed, compressedCapacity, src, srcSize);
89*01826a49SYabin Cui             FUZZ_ASSERT(cSize == cSize0);
90*01826a49SYabin Cui             FUZZ_ASSERT(XXH64(compressed, cSize, 0) == hash0);
91*01826a49SYabin Cui         }
92*01826a49SYabin Cui     }
93*01826a49SYabin Cui     if (refPrefix)
94*01826a49SYabin Cui         FUZZ_ZASSERT(ZSTD_DCtx_refPrefix_advanced(
95*01826a49SYabin Cui             dctx, dict.buff, dict.size,
96*01826a49SYabin Cui             dictContentType));
97*01826a49SYabin Cui     else
98*01826a49SYabin Cui         FUZZ_ZASSERT(ZSTD_DCtx_loadDictionary_advanced(
99*01826a49SYabin Cui             dctx, dict.buff, dict.size,
100*01826a49SYabin Cui             (ZSTD_dictLoadMethod_e)FUZZ_dataProducer_uint32Range(producer, 0, 1),
101*01826a49SYabin Cui             dictContentType));
102*01826a49SYabin Cui     {
103*01826a49SYabin Cui         size_t const ret = ZSTD_decompressDCtx(
104*01826a49SYabin Cui                 dctx, result, resultCapacity, compressed, cSize);
105*01826a49SYabin Cui         free(dict.buff);
106*01826a49SYabin Cui         return ret;
107*01826a49SYabin Cui     }
108*01826a49SYabin Cui }
109*01826a49SYabin Cui 
LLVMFuzzerTestOneInput(const uint8_t * src,size_t size)110*01826a49SYabin Cui int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size)
111*01826a49SYabin Cui {
112*01826a49SYabin Cui     FUZZ_SEQ_PROD_SETUP();
113*01826a49SYabin Cui 
114*01826a49SYabin Cui     /* Give a random portion of src data to the producer, to use for
115*01826a49SYabin Cui     parameter generation. The rest will be used for (de)compression */
116*01826a49SYabin Cui     FUZZ_dataProducer_t *producer = FUZZ_dataProducer_create(src, size);
117*01826a49SYabin Cui     size = FUZZ_dataProducer_reserveDataPrefix(producer);
118*01826a49SYabin Cui 
119*01826a49SYabin Cui     size_t const rBufSize = size;
120*01826a49SYabin Cui     void* rBuf = FUZZ_malloc(rBufSize);
121*01826a49SYabin Cui     size_t cBufSize = ZSTD_compressBound(size);
122*01826a49SYabin Cui     void *cBuf;
123*01826a49SYabin Cui     /* Half of the time fuzz with a 1 byte smaller output size.
124*01826a49SYabin Cui      * This will still succeed because we force the checksum to be disabled,
125*01826a49SYabin Cui      * giving us 4 bytes of overhead.
126*01826a49SYabin Cui      */
127*01826a49SYabin Cui     cBufSize -= FUZZ_dataProducer_uint32Range(producer, 0, 1);
128*01826a49SYabin Cui     cBuf = FUZZ_malloc(cBufSize);
129*01826a49SYabin Cui 
130*01826a49SYabin Cui     if (!cctx) {
131*01826a49SYabin Cui         cctx = ZSTD_createCCtx();
132*01826a49SYabin Cui         FUZZ_ASSERT(cctx);
133*01826a49SYabin Cui     }
134*01826a49SYabin Cui     if (!dctx) {
135*01826a49SYabin Cui         dctx = ZSTD_createDCtx();
136*01826a49SYabin Cui         FUZZ_ASSERT(dctx);
137*01826a49SYabin Cui     }
138*01826a49SYabin Cui 
139*01826a49SYabin Cui     {
140*01826a49SYabin Cui         size_t const result =
141*01826a49SYabin Cui             roundTripTest(rBuf, rBufSize, cBuf, cBufSize, src, size, producer);
142*01826a49SYabin Cui         FUZZ_ZASSERT(result);
143*01826a49SYabin Cui         FUZZ_ASSERT_MSG(result == size, "Incorrect regenerated size");
144*01826a49SYabin Cui         FUZZ_ASSERT_MSG(!FUZZ_memcmp(src, rBuf, size), "Corruption!");
145*01826a49SYabin Cui     }
146*01826a49SYabin Cui     free(rBuf);
147*01826a49SYabin Cui     free(cBuf);
148*01826a49SYabin Cui     FUZZ_dataProducer_free(producer);
149*01826a49SYabin Cui #ifndef STATEFUL_FUZZING
150*01826a49SYabin Cui     ZSTD_freeCCtx(cctx); cctx = NULL;
151*01826a49SYabin Cui     ZSTD_freeDCtx(dctx); dctx = NULL;
152*01826a49SYabin Cui #endif
153*01826a49SYabin Cui     FUZZ_SEQ_PROD_TEARDOWN();
154*01826a49SYabin Cui     return 0;
155*01826a49SYabin Cui }
156