xref: /aosp_15_r20/external/zstd/tests/fuzz/decompress_cross_format.c (revision 01826a4963a0d8a59bc3812d29bdf0fb76416722)
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 // This fuzz target validates decompression of magicless-format compressed data.
12 
13 #include <stddef.h>
14 #include <stdlib.h>
15 #include <stdio.h>
16 #include <string.h>
17 #include "fuzz_helpers.h"
18 #define ZSTD_STATIC_LINKING_ONLY
19 #include "zstd.h"
20 #include "fuzz_data_producer.h"
21 
22 static ZSTD_DCtx *dctx = NULL;
23 
LLVMFuzzerTestOneInput(const uint8_t * src,size_t size)24 int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size)
25 {
26     // Give a random portion of src data to the producer, to use for parameter generation.
27     // The rest will be interpreted as magicless compressed data.
28     FUZZ_dataProducer_t *producer = FUZZ_dataProducer_create(src, size);
29     size_t magiclessSize = FUZZ_dataProducer_reserveDataPrefix(producer);
30     const uint8_t* const magiclessSrc = src;
31     size_t const dstSize = FUZZ_dataProducer_uint32Range(producer, 0, 10 * size);
32     uint8_t* const standardDst = (uint8_t*)FUZZ_malloc(dstSize);
33     uint8_t* const magiclessDst = (uint8_t*)FUZZ_malloc(dstSize);
34 
35     // Create standard-format src from magicless-format src
36     const uint32_t zstd_magic = ZSTD_MAGICNUMBER;
37     size_t standardSize = sizeof(zstd_magic) + magiclessSize;
38     uint8_t* const standardSrc = (uint8_t*)FUZZ_malloc(standardSize);
39     memcpy(standardSrc, &zstd_magic, sizeof(zstd_magic)); // assume fuzzing on little-endian machine
40     memcpy(standardSrc + sizeof(zstd_magic), magiclessSrc, magiclessSize);
41 
42     // Truncate to a single frame
43     {
44         const size_t standardFrameCompressedSize = ZSTD_findFrameCompressedSize(standardSrc, standardSize);
45         if (ZSTD_isError(standardFrameCompressedSize)) {
46             goto cleanup_and_return;
47         }
48         standardSize = standardFrameCompressedSize;
49         magiclessSize = standardFrameCompressedSize - sizeof(zstd_magic);
50     }
51 
52     // Create DCtx if needed
53     if (!dctx) {
54         dctx = ZSTD_createDCtx();
55         FUZZ_ASSERT(dctx);
56     }
57 
58     // Test one-shot decompression
59     {
60         FUZZ_ZASSERT(ZSTD_DCtx_reset(dctx, ZSTD_reset_session_and_parameters));
61         FUZZ_ZASSERT(ZSTD_DCtx_setParameter(dctx, ZSTD_d_format, ZSTD_f_zstd1));
62         const size_t standardRet = ZSTD_decompressDCtx(
63                                         dctx, standardDst, dstSize, standardSrc, standardSize);
64 
65         FUZZ_ZASSERT(ZSTD_DCtx_reset(dctx, ZSTD_reset_session_and_parameters));
66         FUZZ_ZASSERT(ZSTD_DCtx_setParameter(dctx, ZSTD_d_format, ZSTD_f_zstd1_magicless));
67         const size_t magiclessRet = ZSTD_decompressDCtx(
68                                         dctx, magiclessDst, dstSize, magiclessSrc, magiclessSize);
69 
70         // Standard accepts => magicless should accept
71         if (!ZSTD_isError(standardRet)) FUZZ_ZASSERT(magiclessRet);
72 
73         // Magicless accepts => standard should accept
74         // NOTE: this is nice-to-have, please disable this check if it is difficult to satisfy.
75         if (!ZSTD_isError(magiclessRet)) FUZZ_ZASSERT(standardRet);
76 
77         // If both accept, decompressed size and data should match
78         if (!ZSTD_isError(standardRet) && !ZSTD_isError(magiclessRet)) {
79             FUZZ_ASSERT(standardRet == magiclessRet);
80             if (standardRet > 0) {
81                 FUZZ_ASSERT(
82                     memcmp(standardDst, magiclessDst, standardRet) == 0
83                 );
84             }
85         }
86     }
87 
88     // Test streaming decompression
89     {
90         ZSTD_inBuffer standardIn = { standardSrc, standardSize, 0 };
91         ZSTD_inBuffer magiclessIn = { magiclessSrc, magiclessSize, 0 };
92         ZSTD_outBuffer standardOut = { standardDst, dstSize, 0 };
93         ZSTD_outBuffer magiclessOut = { magiclessDst, dstSize, 0 };
94 
95         FUZZ_ZASSERT(ZSTD_DCtx_reset(dctx, ZSTD_reset_session_and_parameters));
96         FUZZ_ZASSERT(ZSTD_DCtx_setParameter(dctx, ZSTD_d_format, ZSTD_f_zstd1));
97         const size_t standardRet = ZSTD_decompressStream(dctx, &standardOut, &standardIn);
98 
99         FUZZ_ZASSERT(ZSTD_DCtx_reset(dctx, ZSTD_reset_session_and_parameters));
100         FUZZ_ZASSERT(ZSTD_DCtx_setParameter(dctx, ZSTD_d_format, ZSTD_f_zstd1_magicless));
101         const size_t magiclessRet = ZSTD_decompressStream(dctx, &magiclessOut, &magiclessIn);
102 
103         // Standard accepts => magicless should accept
104         if (standardRet == 0) FUZZ_ASSERT(magiclessRet == 0);
105 
106         // Magicless accepts => standard should accept
107         // NOTE: this is nice-to-have, please disable this check if it is difficult to satisfy.
108         if (magiclessRet == 0) FUZZ_ASSERT(standardRet == 0);
109 
110         // If both accept, decompressed size and data should match
111         if (standardRet == 0 && magiclessRet == 0) {
112             FUZZ_ASSERT(standardOut.pos == magiclessOut.pos);
113             if (standardOut.pos > 0) {
114                 FUZZ_ASSERT(
115                     memcmp(standardOut.dst, magiclessOut.dst, standardOut.pos) == 0
116                 );
117             }
118         }
119     }
120 
121 cleanup_and_return:
122 #ifndef STATEFUL_FUZZING
123     ZSTD_freeDCtx(dctx); dctx = NULL;
124 #endif
125     free(standardSrc);
126     free(standardDst);
127     free(magiclessDst);
128     FUZZ_dataProducer_free(producer);
129     return 0;
130 }
131