:: bzip3 / examples / fuzz-round-trip.c 4.4 KB raw

1
/* A tiny utility for fuzzing bzip3 round-trip compression/decompression.
2
 *
3
 * Prerequisites:
4
 * 
5
 * - AFL https://github.com/AFLplusplus/AFLplusplus
6
 * - clang (part of LLVM)
7
 * 
8
 * On Arch this is `pacman -S afl++ clang`
9
 *
10
 * # Instructions:
11
 * 
12
 * 1. Prepare fuzzer directories
13
 * 
14
 * mkdir -p afl_in && mkdir -p afl_out
15
 * 
16
 * 2. Insert a test file to afl_in/
17
 * 
18
 * cp ./standard_test_files/63_byte_file.bin afl_in/
19
 * 
20
 * 3. Build binary (for fuzzing)
21
 * 
22
 * afl-clang-fast fuzz-round-trip.c -I../include -o fuzz -g3 "-DVERSION=\"0.0.0\"" -O3 -march=native
23
 * 
24
 * 4. Run the fuzzer
25
 * 
26
 * AFL_SKIP_CPUFREQ=1 afl-fuzz -i afl_in -o afl_out -- ./fuzz @@
27
 *
28
 * 5. Need to go faster? Multithread.
29
 * 
30
 * alacritty -e bash -c "afl-fuzz -i afl_in -o afl_out -M fuzzer01 -- ./fuzz @@; exec bash" &
31
 * alacritty -e bash -c "afl-fuzz -i afl_in -o afl_out -S fuzzer02 -- ./fuzz @@; exec bash" &
32
 * alacritty -e bash -c "afl-fuzz -i afl_in -o afl_out -S fuzzer03 -- ./fuzz @@; exec bash" &
33
 * alacritty -e bash -c "afl-fuzz -i afl_in -o afl_out -S fuzzer04 -- ./fuzz @@; exec bash" &
34
 * 
35
 * etc. Replace `alacritty` with your terminal.
36
 * 
37
 * 6. For ASAN testing:
38
 *
39
 * export AFL_USE_ASAN=1
40
 * afl-clang-fast fuzz-round-trip.c -I../include -o fuzz -g3 "-DVERSION=\"0.0.0\"" -O3 -march=native
41
 */
42
43
#include "../include/libbz3.h"
44
#include "../src/libbz3.c"
45
#include <stdio.h>
46
#include <stdlib.h>
47
#include <stdint.h>
48
#include <string.h>
49
50
#define KiB(x) ((x)*1024)
51
#define DEFAULT_BLOCK_SIZE KiB(65)
52
53
// Required for AFL++ persistent mode
54
#ifdef __AFL_HAVE_MANUAL_CONTROL
55
#include <unistd.h>
56
__AFL_FUZZ_INIT();
57
#endif
58
59
// Function to emulate a crash for diagnostic purposes
60
static void __attribute__((noreturn)) crash_with_message(const char* msg) {
61
    fprintf(stderr, "Emulating crash: %s\n", msg);
62
    // Use abort() to generate a crash that ASAN and other tools can catch
63
    abort();
64
}
65
66
// Returns 0 on success, crashes on failure
67
static int try_round_trip(const uint8_t *input_buf, size_t input_len) {
68
    if (input_len == 0) return 0;
69
70
    // Use the larger of DEFAULT_BLOCK_SIZE or input_len
71
    size_t block_size = input_len > DEFAULT_BLOCK_SIZE ? input_len : DEFAULT_BLOCK_SIZE;
72
    
73
    struct bz3_state *state = bz3_new(block_size);
74
    if (!state) {
75
        return -1; // allocation failures not tested.
76
    }
77
78
    // Allocate buffer for both compression and decompression
79
    // Using block_size to ensure we have enough space for both operations
80
    size_t comp_buf_len = bz3_bound(input_len);
81
    uint8_t *comp_buf = malloc(comp_buf_len);
82
    if (!comp_buf) {
83
        bz3_free(state);
84
        return -1; // allocation failures not tested.
85
    }
86
87
    // Step 0: Move input to compress buffer
88
    memmove(comp_buf, input_buf, input_len);
89
90
    // Step 1: Compress the input
91
    int32_t comp_size = bz3_encode_block(state, comp_buf, input_len);
92
    if (comp_size < 0) {
93
        bz3_free(state);
94
        free(comp_buf);
95
        crash_with_message("Compression failed");
96
    }
97
98
    // Step 2: Decompress
99
    int bzerr = bz3_decode_block(state, comp_buf, comp_buf_len, comp_size, input_len);
100
    if (bzerr < 0 || bzerr != input_len) {
101
        bz3_free(state);
102
        free(comp_buf);
103
        crash_with_message("Decompression failed");
104
    }
105
106
    // Step 3: Compare
107
    if (memcmp(input_buf, comp_buf, input_len) != 0) {
108
        bz3_free(state);
109
        free(comp_buf);
110
        crash_with_message("Round-trip data mismatch");
111
    }
112
113
    bz3_free(state);
114
    free(comp_buf);
115
    return 0;
116
}
117
118
static int test_file(const char *filename) {
119
    FILE *fp = fopen(filename, "rb");
120
    if (!fp) {
121
        perror("Failed to open input file");
122
        return 1;
123
    }
124
125
    fseek(fp, 0, SEEK_END);
126
    size_t size = ftell(fp);
127
    fseek(fp, 0, SEEK_SET);
128
129
    uint8_t *buffer = malloc(size);
130
    if (!buffer) {
131
        fclose(fp);
132
        crash_with_message("Failed to allocate input buffer");
133
    }
134
135
    if (fread(buffer, 1, size, fp) != size) {
136
        fclose(fp);
137
        free(buffer);
138
        crash_with_message("Failed to read input file");
139
    }
140
    fclose(fp);
141
142
    int result = try_round_trip(buffer, size);
143
    free(buffer);
144
    return result;
145
}
146
147
int main(int argc, char **argv) {
148
#ifdef __AFL_HAVE_MANUAL_CONTROL
149
    __AFL_INIT();
150
    
151
    while (__AFL_LOOP(1000)) {
152
        try_round_trip(__AFL_FUZZ_TESTCASE_BUF, __AFL_FUZZ_TESTCASE_LEN);
153
    }
154
#else
155
    if (argc != 2) {
156
        fprintf(stderr, "Usage: %s <input_file>\n", argv[0]);
157
        return 1;
158
    }
159
160
    return test_file(argv[1]);
161
#endif
162
163
    return 0;
164
}
tab: 248 wrap: offon