Coverage Report

Created: 2024-09-08 06:32

/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.0k
{
34
26.0k
    ZSTD_dictContentType_e dictContentType = ZSTD_dct_auto;
35
26.0k
    FUZZ_dict_t dict = FUZZ_train(src, srcSize, producer);
36
26.0k
    int const refPrefix = FUZZ_dataProducer_uint32Range(producer, 0, 1) != 0;
37
26.0k
    size_t cSize;
38
26.0k
    if (FUZZ_dataProducer_uint32Range(producer, 0, 15) == 0) {
39
2.41k
        int const cLevel = FUZZ_dataProducer_int32Range(producer, kMinClevel, kMaxClevel);
40
41
2.41k
        cSize = ZSTD_compress_usingDict(cctx,
42
2.41k
                compressed, compressedCapacity,
43
2.41k
                src, srcSize,
44
2.41k
                dict.buff, dict.size,
45
2.41k
                cLevel);
46
2.41k
        FUZZ_ZASSERT(cSize);
47
        // Compress a second time and check for determinism
48
2.41k
        {
49
2.41k
            size_t const cSize0 = cSize;
50
2.41k
            XXH64_hash_t const hash0 = XXH64(compressed, cSize, 0);
51
2.41k
            cSize = ZSTD_compress_usingDict(cctx,
52
2.41k
                    compressed, compressedCapacity,
53
2.41k
                    src, srcSize,
54
2.41k
                    dict.buff, dict.size,
55
2.41k
                    cLevel);
56
2.41k
            FUZZ_ASSERT(cSize == cSize0);
57
2.41k
            FUZZ_ASSERT(XXH64(compressed, cSize, 0) == hash0);
58
2.41k
        }
59
23.6k
    } else {
60
23.6k
        size_t remainingBytes;
61
23.6k
        dictContentType = FUZZ_dataProducer_uint32Range(producer, 0, 2);
62
23.6k
        remainingBytes = FUZZ_dataProducer_remainingBytes(producer);
63
23.6k
        FUZZ_setRandomParameters(cctx, srcSize, producer);
64
        /* Disable checksum so we can use sizes smaller than compress bound. */
65
23.6k
        FUZZ_ZASSERT(ZSTD_CCtx_setParameter(cctx, ZSTD_c_checksumFlag, 0));
66
23.6k
        if (refPrefix)
67
23.6k
            FUZZ_ZASSERT(ZSTD_CCtx_refPrefix_advanced(
68
14.6k
                cctx, dict.buff, dict.size,
69
14.6k
                dictContentType));
70
14.6k
        else
71
23.6k
            FUZZ_ZASSERT(ZSTD_CCtx_loadDictionary_advanced(
72
23.6k
                cctx, dict.buff, dict.size,
73
23.6k
                (ZSTD_dictLoadMethod_e)FUZZ_dataProducer_uint32Range(producer, 0, 1),
74
23.6k
                dictContentType));
75
23.6k
        cSize = ZSTD_compress2(cctx, compressed, compressedCapacity, src, srcSize);
76
23.6k
        FUZZ_ZASSERT(cSize);
77
        // Compress a second time and check for determinism
78
23.6k
        {
79
23.6k
            size_t const cSize0 = cSize;
80
23.6k
            XXH64_hash_t const hash0 = XXH64(compressed, cSize, 0);
81
23.6k
            FUZZ_dataProducer_rollBack(producer, remainingBytes);
82
23.6k
            FUZZ_setRandomParameters(cctx, srcSize, producer);
83
23.6k
            FUZZ_ZASSERT(ZSTD_CCtx_setParameter(cctx, ZSTD_c_checksumFlag, 0));
84
23.6k
            if (refPrefix)
85
23.6k
                FUZZ_ZASSERT(ZSTD_CCtx_refPrefix_advanced(
86
23.6k
                    cctx, dict.buff, dict.size,
87
23.6k
                    dictContentType));
88
23.6k
            cSize = ZSTD_compress2(cctx, compressed, compressedCapacity, src, srcSize);
89
23.6k
            FUZZ_ASSERT(cSize == cSize0);
90
23.6k
            FUZZ_ASSERT(XXH64(compressed, cSize, 0) == hash0);
91
23.6k
        }
92
23.6k
    }
93
26.0k
    if (refPrefix)
94
26.0k
        FUZZ_ZASSERT(ZSTD_DCtx_refPrefix_advanced(
95
16.6k
            dctx, dict.buff, dict.size,
96
16.6k
            dictContentType));
97
16.6k
    else
98
26.0k
        FUZZ_ZASSERT(ZSTD_DCtx_loadDictionary_advanced(
99
26.0k
            dctx, dict.buff, dict.size,
100
26.0k
            (ZSTD_dictLoadMethod_e)FUZZ_dataProducer_uint32Range(producer, 0, 1),
101
26.0k
            dictContentType));
102
26.0k
    {
103
26.0k
        size_t const ret = ZSTD_decompressDCtx(
104
26.0k
                dctx, result, resultCapacity, compressed, cSize);
105
26.0k
        free(dict.buff);
106
26.0k
        return ret;
107
26.0k
    }
108
26.0k
}
109
110
int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size)
111
40.8k
{
112
40.8k
    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
40.8k
    FUZZ_dataProducer_t *producer = FUZZ_dataProducer_create(src, size);
117
40.8k
    size = FUZZ_dataProducer_reserveDataPrefix(producer);
118
119
40.8k
    size_t const rBufSize = size;
120
40.8k
    void* rBuf = FUZZ_malloc(rBufSize);
121
40.8k
    size_t cBufSize = ZSTD_compressBound(size);
122
40.8k
    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
40.8k
    cBufSize -= FUZZ_dataProducer_uint32Range(producer, 0, 1);
128
40.8k
    cBuf = FUZZ_malloc(cBufSize);
129
130
40.8k
    if (!cctx) {
131
40.8k
        cctx = ZSTD_createCCtx();
132
40.8k
        FUZZ_ASSERT(cctx);
133
40.8k
    }
134
40.8k
    if (!dctx) {
135
40.8k
        dctx = ZSTD_createDCtx();
136
40.8k
        FUZZ_ASSERT(dctx);
137
40.8k
    }
138
139
40.8k
    {
140
40.8k
        size_t const result =
141
40.8k
            roundTripTest(rBuf, rBufSize, cBuf, cBufSize, src, size, producer);
142
40.8k
        FUZZ_ZASSERT(result);
143
40.8k
        FUZZ_ASSERT_MSG(result == size, "Incorrect regenerated size");
144
40.8k
        FUZZ_ASSERT_MSG(!FUZZ_memcmp(src, rBuf, size), "Corruption!");
145
40.8k
    }
146
40.8k
    free(rBuf);
147
40.8k
    free(cBuf);
148
40.8k
    FUZZ_dataProducer_free(producer);
149
40.8k
#ifndef STATEFUL_FUZZING
150
40.8k
    ZSTD_freeCCtx(cctx); cctx = NULL;
151
40.8k
    ZSTD_freeDCtx(dctx); dctx = NULL;
152
40.8k
#endif
153
40.8k
    FUZZ_SEQ_PROD_TEARDOWN();
154
40.8k
    return 0;
155
40.8k
}