Coverage Report

Created: 2026-11-12 06:37

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/zxc/tests/fuzz/fuzz_roundtrip.c
Line
Count
Source
1
// SPDX-License-Identifier: BSD-3-Clause
2
/*
3
 * ZXC - High-performance lossless compression
4
 *
5
 * Copyright (c) Bertrand Lebonnois and contributors.
6
 */
7
8
/**
9
 * @file fuzz_roundtrip.c
10
 * @brief Fuzzer for the one-shot compress -> decompress roundtrip.
11
 *
12
 * Asserts the core invariant of a lossless codec: decompressing what the
13
 * compressor produced must reproduce the original bytes exactly. The fuzzer
14
 * data is the payload to compress (never untrusted decoder input -- that is
15
 * fuzz_decompress.c's job), so this target hunts for encoder/decoder
16
 * mismatches rather than malformed-frame handling.
17
 *
18
 * Strategy: derive the compression level from the first byte so libFuzzer
19
 * explores every level, compress the fuzzed bytes, then decompress into an
20
 * exact-size buffer and assert a bit-exact roundtrip. Buffers are reused
21
 * across iterations to keep allocator pressure low.
22
 */
23
24
#include <assert.h>
25
#include <stddef.h>
26
#include <stdint.h>
27
#include <stdlib.h>
28
#include <string.h>
29
30
#include "../../include/zxc_buffer.h"
31
32
25.9k
#define FUZZ_ROUNDTRIP_MAX_INPUT (4 << 20) /* 4 MiB */
33
34
25.9k
int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
35
25.9k
    static void* comp_buf = NULL;
36
25.9k
    static size_t comp_cap = 0;
37
25.9k
    static void* decomp_buf = NULL;
38
25.9k
    static size_t decomp_cap = 0;
39
40
25.9k
    if (size > FUZZ_ROUNDTRIP_MAX_INPUT) return 0;
41
42
25.9k
    const size_t bound = zxc_compress_bound(size);
43
25.9k
    if (bound == 0) return 0;
44
25.9k
    if (bound > comp_cap) {
45
13.4k
        void* new_buf = realloc(comp_buf, bound);
46
13.4k
        if (!new_buf) return 0;
47
13.4k
        comp_buf = new_buf;
48
13.4k
        comp_cap = bound;
49
13.4k
    }
50
51
25.9k
    const int level = size > 0 ? (data[0] % (unsigned)zxc_max_level()) + 1 : 1;
52
25.9k
    const int64_t csize = zxc_compress(data, size, comp_buf, bound, level);
53
25.9k
    if (csize < 0) return 0;
54
55
25.9k
    if (size == 0) return 0;
56
57
25.9k
    if (size > decomp_cap) {
58
13.4k
        void* new_buf = realloc(decomp_buf, size);
59
13.4k
        if (!new_buf) return 0;
60
13.4k
        decomp_buf = new_buf;
61
13.4k
        decomp_cap = size;
62
13.4k
    }
63
64
25.9k
    const int64_t dsize = zxc_decompress(comp_buf, (size_t)csize, decomp_buf, size);
65
66
25.9k
    if (dsize >= 0) {
67
25.9k
        assert((size_t)dsize == size);
68
25.9k
        assert(memcmp(data, decomp_buf, size) == 0);
69
25.9k
    }
70
71
25.9k
    return 0;
72
25.9k
}