File: fuzz-decode-block.c

package info (click to toggle)
bzip3 1.5.3-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 7,652 kB
  • sloc: ansic: 6,893; sh: 218; makefile: 82
file content (332 lines) | stat: -rw-r--r-- 9,706 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
/* A tiny utility for fuzzing bzip3 block decompression.
 *
 * Prerequisites:
 * 
 * - AFL https://github.com/AFLplusplus/AFLplusplus
 * - clang (part of LLVM)
 * 
 * On Arch this is `pacman -S afl++ clang`
 *
 * # Instructions:
 * 
 * 1. Prepare fuzzer directories
 * 
 * mkdir -p afl_in && mkdir -p afl_out
 * 
 * 2. Build binary (to compress test data).
 * 
 * afl-clang fuzz-decode-block.c -I../include -o fuzz -g3 "-DVERSION=\"0.0.0\"" -O3 -march=native
 * 
 * 3. Make a fuzzer input file.
 * 
 * With `your_file` being an arbitrary input to test, use this utility
 * to generate a compressed test block:
 * 
 * ./fuzz standard_test_files/63_byte_file.bin 63_byte_file.bin.bz3b 8
 * ./fuzz standard_test_files/65_byte_file.bin 65_byte_file.bin.bz3b 8
 * mv 63_byte_file.bin.bz3b afl_in/
 * mv 65_byte_file.bin.bz3b afl_in/
 * 
 * For this test, it is recommended to make 2 files, one that's <64 bytes and one that's >64 bytes.
 * 
 * 4. Build binary (for fuzzing).
 * 
 * afl-clang-fast fuzz-decode-block.c -I../include -o fuzz -g3 "-DVERSION=\"0.0.0\"" -O3 -march=native
 * 
 * 5. Run the fuzzer.
 * 
 * AFL_SKIP_CPUFREQ=1 afl-fuzz -i afl_in -o afl_out -- ./fuzz @@
 *
 * 6. Wanna go faster? Multithread.
 * 
 * alacritty -e bash -c "afl-fuzz -i afl_in -o afl_out -M fuzzer01 -- ./fuzz @@; exec bash" &
 * alacritty -e bash -c "afl-fuzz -i afl_in -o afl_out -S fuzzer02 -- ./fuzz @@; exec bash" &
 * alacritty -e bash -c "afl-fuzz -i afl_in -o afl_out -S fuzzer03 -- ./fuzz @@; exec bash" &
 * alacritty -e bash -c "afl-fuzz -i afl_in -o afl_out -S fuzzer04 -- ./fuzz @@; exec bash" &
 * 
 * etc. Replace `alacritty` with your terminal.
 * 
 * And check progress with `afl-whatsup afl_out` (updates periodically).
 * 
 * 7. Found a crash?
 * 
 * If you find a crash, consider also doing the following:
 * 
 *      clang fuzz-decode-block.c -g3 -O3 -march=native -o fuzz_asan -I../include "-DVERSION=\"0.0.0\"" -fsanitize=undefined -fsanitize=address
 *
 * And run fuzz_asan on the crashing test case (you can find it in one of the `afl_out/crashes/` folders).
 * Attach the test case /and/ the output of fuzz_asan to the bug report.
 * 
 * If no error occurs, it could be that there was a memory corruption `between` the runs.
 * In which case, you want to run AFL with address sanitizer. Use `export AFL_USE_ASAN=1` to enable
 * addres sanitizer; then run AFL.
 * 
 * export AFL_USE_ASAN=1
 * afl-clang-fast fuzz-decode-block.c -I../include -o fuzz -g3 "-DVERSION=\"0.0.0\"" -O3 -march=native
 */

/*

This hex editor template can be used to help debug a breaking file.
Would provide for ImHex, but ImHex terminates if template is borked.


//------------------------------------------------
//--- 010 Editor v15.0.1 Binary Template
//
//      File: bzip3block.bt
//   Authors: Sewer56
//   Version: 1.0.0
//   Purpose: Parse bzip3 fuzzer block data
//  Category: Archive
// File Mask: *.bz3b
//------------------------------------------------

// Colors for different sections
#define COLOR_HEADER     0xA0FFA0 // Block metadata
#define COLOR_BLOCKHEAD  0xFFB0B0 // Block headers
#define COLOR_DATA       0xB0B0FF // Compressed data

local uint32 currentBlockSize; // Store block size globally

// Block metadata structure
typedef struct {
    uint32 orig_size;      // Original uncompressed size
    uint32 comp_size;      // Compressed size
    uint32 buffer_size;    // Size of decompression buffer
} BLOCK_META <bgcolor=COLOR_HEADER>;

// Regular block header (for blocks >= 64 bytes)
typedef struct {
    uint32 crc32;         // CRC32 checksum of uncompressed data
    uint32 bwtIndex;      // Burrows-Wheeler transform index
    uint8  model;         // Compression model flags:
                         // bit 1 (0x02): LZP was used
                         // bit 2 (0x04): RLE was used
    
    // Optional size fields based on compression flags
    if(model & 0x02)     
        uint32 lzpSize;   // Size after LZP compression
    if(model & 0x04)     
        uint32 rleSize;   // Size after RLE compression
} BLOCK_HEADER <bgcolor=COLOR_BLOCKHEAD>;

// Small block header (for blocks < 64 bytes)
typedef struct {
    uint32 crc32;        // CRC32 checksum
    uint32 literal;      // Always 0xFFFFFFFF for small blocks
    uint8 data[currentBlockSize - 8]; // Uncompressed data
} SMALL_BLOCK <bgcolor=COLOR_BLOCKHEAD>;

// Block content structure
typedef struct {
    currentBlockSize = meta.comp_size;
    
    if(meta.orig_size < 64) {
        SMALL_BLOCK content;
    } else {
        BLOCK_HEADER header;
        uchar data[meta.comp_size - (Popcount(header.model) * 4 + 9)];
    }
} BLOCK_CONTENT <bgcolor=COLOR_DATA>;

// Helper function for bit counting (used for header size calculation)
int Popcount(byte b) {
    local int count = 0;
    while(b) {
        count += b & 1;
        b >>= 1;
    }
    return count;
}

// Main block structure
typedef struct {
    BLOCK_META meta;
    BLOCK_CONTENT content;
} BLOCK;

// Main parsing structure
BLOCK block;
*/

#include "../include/libbz3.h"
#include "../src/libbz3.c"
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>

#define KiB(x) ((x)*1024)

// Required for AFL++ persistent mode
#ifdef __AFL_HAVE_MANUAL_CONTROL
#include <unistd.h>
__AFL_FUZZ_INIT();
#endif

size_t min_size_t(size_t a, size_t b) {
    return (a < b) ? a : b;
}

// Returns 0 on success, positive on bzip3 errors
static int try_decode_block(const uint8_t *input_buf, size_t input_len) {
    // Read whatever metadata we can get
    uint32_t orig_size = 0;
    uint32_t comp_size = 0;
    uint32_t buffer_size = 0;
    
    if (input_len >= 4) orig_size = *(const uint32_t *)input_buf;
    if (input_len >= 8) comp_size = *(const uint32_t *)(input_buf + 4);
    if (input_len >= 12) buffer_size = *(const uint32_t *)(input_buf + 8);
    
    // Initialize state with minimum block size
    struct bz3_state *state = bz3_new(KiB(65));
    if (!state) return 0; // not under test

    // Allocate buffer with fuzzer-provided size
    uint8_t *buffer = malloc(buffer_size);
    if (!buffer) {
        bz3_free(state);
        return 0; // not under test
    }

    // Copy whatever compressed data we can get
    size_t data_len = input_len > 12 ? input_len - 12 : 0;
    if (data_len > 0) {
        memcpy(buffer, input_buf + 12, min_size_t(data_len, (size_t)buffer_size));
    }

    // Attempt decompression with potentially invalid parameters
    int bzerr = bz3_decode_block(state, buffer, buffer_size, comp_size, orig_size);
    // and pray we don't crash :p

    free(buffer);
    bz3_free(state);
    return bzerr;
}

static int encode_block(const char *infile, const char *outfile, uint32_t block_size) {
    block_size = block_size <= KiB(65) ? KiB(65) : block_size;
    
    // Read input file
    FILE *fp_in = fopen(infile, "rb");
    if (!fp_in) {
        perror("Failed to open input file");
        return 1;
    }

    fseek(fp_in, 0, SEEK_END);
    size_t insize = ftell(fp_in);
    fseek(fp_in, 0, SEEK_SET);

    uint8_t *inbuf = malloc(insize);
    if (!inbuf) {
        fclose(fp_in);
        return 1;
    }

    fread(inbuf, 1, insize, fp_in);
    fclose(fp_in);

    // Initialize compression state
    struct bz3_state *state = bz3_new(block_size);
    if (!state) {
        free(inbuf);
        return 1;
    }

    // Make output buffer
    size_t outsize = bz3_bound(insize);
    uint8_t *outbuf = malloc(outsize + 12); // +12 for metadata
    if (!outbuf) {
        bz3_free(state);
        free(inbuf);
        return 1;
    }

    // Store metadata
    *(uint32_t *)outbuf = insize;        // Original size
    *(uint32_t *)(outbuf + 8) = outsize; // Buffer size needed for decompression
    
    // Compress the block
    int32_t comp_size = bz3_encode_block(state, outbuf + 12, insize);
    if (comp_size < 0) {
        printf("bz3_encode_block() failed with error code %d\n", comp_size);
        bz3_free(state);
        free(inbuf);
        free(outbuf);
        return comp_size;
    }

    // Store compressed size
    *(uint32_t *)(outbuf + 4) = comp_size;

    FILE *fp_out = fopen(outfile, "wb");
    if (!fp_out) {
        perror("Failed to open output file");
        bz3_free(state);
        free(inbuf);
        free(outbuf);
        return 1;
    }

    fwrite(outbuf, 1, comp_size + 12, fp_out);
    fclose(fp_out);

    printf("Encoded block from %s (%zu bytes) to %s (%d bytes)\n", 
           infile, insize, outfile, comp_size + 12);

    bz3_free(state);
    free(inbuf);
    free(outbuf);
    return 0;
}

int main(int argc, char **argv) {
#ifdef __AFL_HAVE_MANUAL_CONTROL
    __AFL_INIT();
    
    while (__AFL_LOOP(1000)) {
        try_decode_block(__AFL_FUZZ_TESTCASE_BUF, __AFL_FUZZ_TESTCASE_LEN);
    }
#else
    if (argc == 4) {
        // Compression mode: input_file output_file block_size
        return encode_block(argv[1], argv[2], atoi(argv[3]));
    }
    
    if (argc != 2) {
        fprintf(stderr, "Usage:\n");
        fprintf(stderr, "  Decode: %s <input_file>\n", argv[0]);
        fprintf(stderr, "  Encode: %s <input_file> <output_file> <block_size>\n", argv[0]);
        return 1;
    }

    // Decode mode
    FILE *fp = fopen(argv[1], "rb");
    if (!fp) {
        perror("Failed to open input file");
        return 1;
    }

    fseek(fp, 0, SEEK_END);
    size_t size = ftell(fp);
    fseek(fp, 0, SEEK_SET);

    uint8_t *buffer = malloc(size);
    if (!buffer) {
        fclose(fp);
        return 1;
    }

    fread(buffer, 1, size, fp);
    fclose(fp);

    int result = try_decode_block(buffer, size);
    free(buffer);
    return result > 0 ? result : 0; // Return bzip3 errors but treat validation errors as success
#endif

    return 0;
}