/src/lz4/ossfuzz/round_trip_frame_uncompressed_fuzzer.c
Line | Count | Source |
1 | | /** |
2 | | * This fuzz target performs a lz4 round-trip test (compress & decompress), |
3 | | * compares the result with the original, and calls abort() on corruption. |
4 | | */ |
5 | | |
6 | | #include <stddef.h> |
7 | | #include <stdint.h> |
8 | | #include <stdlib.h> |
9 | | #include <string.h> |
10 | | |
11 | | #include "fuzz_data_producer.h" |
12 | | #include "fuzz_helpers.h" |
13 | | #include "lz4.h" |
14 | | #include "lz4_helpers.h" |
15 | | #include "lz4frame.h" |
16 | | #include "lz4frame_static.h" |
17 | | |
18 | | static void decompress(LZ4F_dctx *dctx, void *src, void *dst, |
19 | 9.91k | size_t dstCapacity, size_t readSize) { |
20 | 9.91k | size_t ret = 1; |
21 | 9.91k | const void *srcPtr = (const char *) src; |
22 | 9.91k | void *dstPtr = (char *) dst; |
23 | 9.91k | const void *const srcEnd = (const char *) srcPtr + readSize; |
24 | | |
25 | 19.8k | while (ret != 0) { |
26 | 19.8k | while (srcPtr < srcEnd && ret != 0) { |
27 | | /* Any data within dst has been flushed at this stage */ |
28 | 9.91k | size_t dstSize = dstCapacity; |
29 | 9.91k | size_t srcSize = (const char *) srcEnd - (const char *) srcPtr; |
30 | 9.91k | ret = LZ4F_decompress(dctx, dstPtr, &dstSize, srcPtr, &srcSize, |
31 | | /* LZ4F_decompressOptions_t */ NULL); |
32 | 9.91k | FUZZ_ASSERT(!LZ4F_isError(ret)); |
33 | | |
34 | | /* Update input */ |
35 | 9.91k | srcPtr = (const char *) srcPtr + srcSize; |
36 | 9.91k | dstPtr = (char *) dstPtr + dstSize; |
37 | 9.91k | } |
38 | | |
39 | 9.91k | FUZZ_ASSERT(srcPtr <= srcEnd); |
40 | 9.91k | } |
41 | 9.91k | } |
42 | | |
43 | | static void compress_round_trip(const uint8_t *data, size_t size, |
44 | 9.91k | FUZZ_dataProducer_t *producer, LZ4F_preferences_t const prefs) { |
45 | | |
46 | | // Choose random uncompressed offset start and end by producing seeds from random data, calculate the remaining |
47 | | // data size that will be used for compression later and use the seeds to actually calculate the offsets |
48 | 9.91k | size_t const uncompressedOffsetSeed = FUZZ_dataProducer_retrieve32(producer); |
49 | 9.91k | size_t const uncompressedEndOffsetSeed = FUZZ_dataProducer_retrieve32(producer); |
50 | 9.91k | size = FUZZ_dataProducer_remainingBytes(producer); |
51 | | |
52 | 9.91k | size_t const uncompressedOffset = FUZZ_getRange_from_uint32(uncompressedOffsetSeed, 0, size); |
53 | 9.91k | size_t const uncompressedEndOffset = FUZZ_getRange_from_uint32(uncompressedEndOffsetSeed, uncompressedOffset, size); |
54 | 9.91k | size_t const uncompressedSize = uncompressedEndOffset - uncompressedOffset; |
55 | 9.91k | FUZZ_ASSERT(uncompressedOffset <= uncompressedEndOffset); |
56 | 9.91k | FUZZ_ASSERT(uncompressedEndOffset <= size); |
57 | | |
58 | 9.91k | const uint8_t *const uncompressedData = data + uncompressedOffset; |
59 | | |
60 | 9.91k | size_t const dstCapacity = |
61 | 9.91k | LZ4F_compressFrameBound(LZ4_compressBound(size), &prefs) + |
62 | 9.91k | uncompressedSize; |
63 | 9.91k | char *const dst = (char *) malloc(dstCapacity); |
64 | 9.91k | size_t rtCapacity = dstCapacity; |
65 | 9.91k | char *const rt = (char *) malloc(rtCapacity); |
66 | | |
67 | 9.91k | FUZZ_ASSERT(dst); |
68 | 9.91k | FUZZ_ASSERT(rt); |
69 | | |
70 | | /* Compression must succeed and round trip correctly. */ |
71 | 9.91k | LZ4F_compressionContext_t ctx; |
72 | 9.91k | size_t const ctxCreation = LZ4F_createCompressionContext(&ctx, LZ4F_VERSION); |
73 | 9.91k | FUZZ_ASSERT(!LZ4F_isError(ctxCreation)); |
74 | | |
75 | 9.91k | size_t const headerSize = LZ4F_compressBegin(ctx, dst, dstCapacity, &prefs); |
76 | 9.91k | FUZZ_ASSERT(!LZ4F_isError(headerSize)); |
77 | 9.91k | size_t compressedSize = headerSize; |
78 | | |
79 | | /* Compress data before uncompressed offset */ |
80 | 9.91k | size_t lz4Return = LZ4F_compressUpdate(ctx, dst + compressedSize, dstCapacity, |
81 | 9.91k | data, uncompressedOffset, NULL); |
82 | 9.91k | FUZZ_ASSERT(!LZ4F_isError(lz4Return)); |
83 | 9.91k | compressedSize += lz4Return; |
84 | | |
85 | | /* Add uncompressed data */ |
86 | 9.91k | lz4Return = LZ4F_uncompressedUpdate(ctx, dst + compressedSize, dstCapacity, |
87 | 9.91k | uncompressedData, uncompressedSize, NULL); |
88 | 9.91k | FUZZ_ASSERT(!LZ4F_isError(lz4Return)); |
89 | 9.91k | compressedSize += lz4Return; |
90 | | |
91 | | /* Compress data after uncompressed offset */ |
92 | 9.91k | lz4Return = LZ4F_compressUpdate(ctx, dst + compressedSize, dstCapacity, |
93 | 9.91k | data + uncompressedEndOffset, |
94 | 9.91k | size - uncompressedEndOffset, NULL); |
95 | 9.91k | FUZZ_ASSERT(!LZ4F_isError(lz4Return)); |
96 | 9.91k | compressedSize += lz4Return; |
97 | | |
98 | | /* Finish compression */ |
99 | 9.91k | lz4Return = LZ4F_compressEnd(ctx, dst + compressedSize, dstCapacity, NULL); |
100 | 9.91k | FUZZ_ASSERT(!LZ4F_isError(lz4Return)); |
101 | 9.91k | compressedSize += lz4Return; |
102 | | |
103 | 9.91k | LZ4F_decompressOptions_t opts; |
104 | 9.91k | memset(&opts, 0, sizeof(opts)); |
105 | 9.91k | opts.stableDst = 1; |
106 | 9.91k | LZ4F_dctx *dctx; |
107 | 9.91k | LZ4F_createDecompressionContext(&dctx, LZ4F_VERSION); |
108 | 9.91k | FUZZ_ASSERT(dctx); |
109 | | |
110 | 9.91k | decompress(dctx, dst, rt, rtCapacity, compressedSize); |
111 | | |
112 | 9.91k | LZ4F_freeDecompressionContext(dctx); |
113 | | |
114 | 9.91k | FUZZ_ASSERT_MSG(!memcmp(data, rt, size), "Corruption!"); |
115 | | |
116 | 9.91k | free(dst); |
117 | 9.91k | free(rt); |
118 | | |
119 | 9.91k | FUZZ_dataProducer_free(producer); |
120 | 9.91k | LZ4F_freeCompressionContext(ctx); |
121 | 9.91k | } |
122 | | |
123 | 9.91k | static void compress_independent_block_mode(const uint8_t *data, size_t size) { |
124 | 9.91k | FUZZ_dataProducer_t *producer = FUZZ_dataProducer_create(data, size); |
125 | 9.91k | LZ4F_preferences_t prefs = FUZZ_dataProducer_preferences(producer); |
126 | 9.91k | prefs.frameInfo.blockMode = LZ4F_blockIndependent; |
127 | 9.91k | compress_round_trip(data, size, producer, prefs); |
128 | 9.91k | } |
129 | | |
130 | | |
131 | 9.91k | int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { |
132 | 9.91k | compress_independent_block_mode(data, size); |
133 | 9.91k | return 0; |
134 | 9.91k | } |