Coverage Report

Created: 2026-01-10 07:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/zstd/tests/fuzz/dictionary_round_trip.c
Line
Count
Source
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) with
13
 * a dictionary, compares the result with the original, and calls abort() on
14
 * corruption.
15
 */
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
#include "fuzz_data_producer.h"
24
#include "fuzz_third_party_seq_prod.h"
25
26
static ZSTD_CCtx* cctx = NULL;
27
static ZSTD_DCtx* dctx = NULL;
28
29
static size_t roundTripTest(void* result, size_t resultCapacity,
30
                            void* compressed, size_t compressedCapacity,
31
                            const void* src, size_t srcSize,
32
                            FUZZ_dataProducer_t* producer)
33
26.8k
{
34
26.8k
    ZSTD_dictContentType_e dictContentType = ZSTD_dct_auto;
35
26.8k
    FUZZ_dict_t dict = FUZZ_train(src, srcSize, producer);
36
26.8k
    int const refPrefix = FUZZ_dataProducer_uint32Range(producer, 0, 1) != 0;
37
26.8k
    size_t cSize;
38
26.8k
    if (FUZZ_dataProducer_uint32Range(producer, 0, 15) == 0) {
39
3.05k
        int const cLevel = FUZZ_dataProducer_int32Range(producer, kMinClevel, kMaxClevel);
40
41
3.05k
        cSize = ZSTD_compress_usingDict(cctx,
42
3.05k
                compressed, compressedCapacity,
43
3.05k
                src, srcSize,
44
3.05k
                dict.buff, dict.size,
45
3.05k
                cLevel);
46
3.05k
        FUZZ_ZASSERT(cSize);
47
        // Compress a second time and check for determinism
48
3.05k
        {
49
3.05k
            size_t const cSize0 = cSize;
50
3.05k
            XXH64_hash_t const hash0 = XXH64(compressed, cSize, 0);
51
3.05k
            cSize = ZSTD_compress_usingDict(cctx,
52
3.05k
                    compressed, compressedCapacity,
53
3.05k
                    src, srcSize,
54
3.05k
                    dict.buff, dict.size,
55
3.05k
                    cLevel);
56
3.05k
            FUZZ_ASSERT(cSize == cSize0);
57
3.05k
            FUZZ_ASSERT(XXH64(compressed, cSize, 0) == hash0);
58
3.05k
        }
59
23.7k
    } else {
60
23.7k
        size_t remainingBytes;
61
23.7k
        dictContentType = FUZZ_dataProducer_uint32Range(producer, 0, 2);
62
23.7k
        remainingBytes = FUZZ_dataProducer_remainingBytes(producer);
63
23.7k
        FUZZ_setRandomParameters(cctx, srcSize, producer);
64
        /* Disable checksum so we can use sizes smaller than compress bound. */
65
23.7k
        FUZZ_ZASSERT(ZSTD_CCtx_setParameter(cctx, ZSTD_c_checksumFlag, 0));
66
23.7k
        if (refPrefix)
67
23.7k
            FUZZ_ZASSERT(ZSTD_CCtx_refPrefix_advanced(
68
15.5k
                cctx, dict.buff, dict.size,
69
15.5k
                dictContentType));
70
15.5k
        else
71
23.7k
            FUZZ_ZASSERT(ZSTD_CCtx_loadDictionary_advanced(
72
23.7k
                cctx, dict.buff, dict.size,
73
23.7k
                (ZSTD_dictLoadMethod_e)FUZZ_dataProducer_uint32Range(producer, 0, 1),
74
23.7k
                dictContentType));
75
23.7k
        cSize = ZSTD_compress2(cctx, compressed, compressedCapacity, src, srcSize);
76
23.7k
        FUZZ_ZASSERT(cSize);
77
        // Compress a second time and check for determinism
78
23.7k
        {
79
23.7k
            size_t const cSize0 = cSize;
80
23.7k
            XXH64_hash_t const hash0 = XXH64(compressed, cSize, 0);
81
23.7k
            FUZZ_dataProducer_rollBack(producer, remainingBytes);
82
23.7k
            FUZZ_setRandomParameters(cctx, srcSize, producer);
83
23.7k
            FUZZ_ZASSERT(ZSTD_CCtx_setParameter(cctx, ZSTD_c_checksumFlag, 0));
84
23.7k
            if (refPrefix)
85
23.7k
                FUZZ_ZASSERT(ZSTD_CCtx_refPrefix_advanced(
86
23.7k
                    cctx, dict.buff, dict.size,
87
23.7k
                    dictContentType));
88
23.7k
            cSize = ZSTD_compress2(cctx, compressed, compressedCapacity, src, srcSize);
89
23.7k
            FUZZ_ASSERT(cSize == cSize0);
90
23.7k
            FUZZ_ASSERT(XXH64(compressed, cSize, 0) == hash0);
91
23.7k
        }
92
23.7k
    }
93
26.8k
    if (refPrefix)
94
26.8k
        FUZZ_ZASSERT(ZSTD_DCtx_refPrefix_advanced(
95
18.1k
            dctx, dict.buff, dict.size,
96
18.1k
            dictContentType));
97
18.1k
    else
98
26.8k
        FUZZ_ZASSERT(ZSTD_DCtx_loadDictionary_advanced(
99
26.8k
            dctx, dict.buff, dict.size,
100
26.8k
            (ZSTD_dictLoadMethod_e)FUZZ_dataProducer_uint32Range(producer, 0, 1),
101
26.8k
            dictContentType));
102
26.8k
    {
103
26.8k
        size_t const ret = ZSTD_decompressDCtx(
104
26.8k
                dctx, result, resultCapacity, compressed, cSize);
105
26.8k
        free(dict.buff);
106
26.8k
        return ret;
107
26.8k
    }
108
26.8k
}
109
110
int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size)
111
49.7k
{
112
49.7k
    FUZZ_SEQ_PROD_SETUP();
113
114
    /* Give a random portion of src data to the producer, to use for
115
    parameter generation. The rest will be used for (de)compression */
116
49.7k
    FUZZ_dataProducer_t *producer = FUZZ_dataProducer_create(src, size);
117
49.7k
    size = FUZZ_dataProducer_reserveDataPrefix(producer);
118
119
49.7k
    size_t const rBufSize = size;
120
49.7k
    void* rBuf = FUZZ_malloc(rBufSize);
121
49.7k
    size_t cBufSize = ZSTD_compressBound(size);
122
49.7k
    void *cBuf;
123
    /* Half of the time fuzz with a 1 byte smaller output size.
124
     * This will still succeed because we force the checksum to be disabled,
125
     * giving us 4 bytes of overhead.
126
     */
127
49.7k
    cBufSize -= FUZZ_dataProducer_uint32Range(producer, 0, 1);
128
49.7k
    cBuf = FUZZ_malloc(cBufSize);
129
130
49.7k
    if (!cctx) {
131
49.7k
        cctx = ZSTD_createCCtx();
132
49.7k
        FUZZ_ASSERT(cctx);
133
49.7k
    }
134
49.7k
    if (!dctx) {
135
49.7k
        dctx = ZSTD_createDCtx();
136
49.7k
        FUZZ_ASSERT(dctx);
137
49.7k
    }
138
139
49.7k
    {
140
49.7k
        size_t const result =
141
49.7k
            roundTripTest(rBuf, rBufSize, cBuf, cBufSize, src, size, producer);
142
49.7k
        FUZZ_ZASSERT(result);
143
49.7k
        FUZZ_ASSERT_MSG(result == size, "Incorrect regenerated size");
144
49.7k
        FUZZ_ASSERT_MSG(!FUZZ_memcmp(src, rBuf, size), "Corruption!");
145
49.7k
    }
146
49.7k
    free(rBuf);
147
49.7k
    free(cBuf);
148
49.7k
    FUZZ_dataProducer_free(producer);
149
49.7k
#ifndef STATEFUL_FUZZING
150
49.7k
    ZSTD_freeCCtx(cctx); cctx = NULL;
151
    ZSTD_freeDCtx(dctx); dctx = NULL;
152
49.7k
#endif
153
49.7k
    FUZZ_SEQ_PROD_TEARDOWN();
154
49.7k
    return 0;
155
49.7k
}