Coverage Report

Created: 2026-08-13 07:17

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/zxc/tests/fuzz_roundtrip.c
Line
Count
Source
1
/*
2
 * ZXC - High-performance lossless compression
3
 *
4
 * Copyright (c) 2025-2026 Bertrand Lebonnois and contributors.
5
 * SPDX-License-Identifier: BSD-3-Clause
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
11.9k
#define FUZZ_ROUNDTRIP_MAX_INPUT (4 << 20) /* 4 MiB */
33
34
11.9k
int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
35
11.9k
    static void* comp_buf = NULL;
36
11.9k
    static size_t comp_cap = 0;
37
11.9k
    static void* decomp_buf = NULL;
38
11.9k
    static size_t decomp_cap = 0;
39
40
11.9k
    if (size > FUZZ_ROUNDTRIP_MAX_INPUT) return 0;
41
42
11.9k
    const uint64_t bound64 = zxc_compress_bound(size);
43
11.9k
    if (bound64 == 0 || bound64 > SIZE_MAX) return 0;
44
11.9k
    const size_t bound = (size_t)bound64;
45
11.9k
    if (bound > comp_cap) {
46
6.26k
        void* new_buf = realloc(comp_buf, bound);
47
6.26k
        if (!new_buf) return 0;
48
6.26k
        comp_buf = new_buf;
49
6.26k
        comp_cap = bound;
50
6.26k
    }
51
52
11.9k
    const int level = size > 0 ? (data[0] % (unsigned)zxc_max_level()) + 1 : 1;
53
11.9k
    zxc_compress_opts_t copts = {.level = level};
54
11.9k
    const int64_t csize = zxc_compress(data, size, comp_buf, bound, &copts);
55
11.9k
    if (csize < 0) return 0;
56
57
11.9k
    if (size == 0) return 0;
58
59
11.9k
    if (size > decomp_cap) {
60
6.26k
        void* new_buf = realloc(decomp_buf, size);
61
6.26k
        if (!new_buf) return 0;
62
6.26k
        decomp_buf = new_buf;
63
6.26k
        decomp_cap = size;
64
6.26k
    }
65
66
11.9k
    const int64_t dsize = zxc_decompress(comp_buf, (size_t)csize, decomp_buf, size, NULL);
67
68
11.9k
    if (dsize >= 0) {
69
11.9k
        assert((size_t)dsize == size);
70
11.9k
        assert(memcmp(data, decomp_buf, size) == 0);
71
11.9k
    }
72
73
11.9k
    return 0;
74
11.9k
}