xref: /aosp_15_r20/external/lz4/ossfuzz/round_trip_frame_uncompressed_fuzzer.c (revision 27162e4e17433d5aa7cb38e7b6a433a09405fc7f)
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 
decompress(LZ4F_dctx * dctx,void * src,void * dst,size_t dstCapacity,size_t readSize)18 static void decompress(LZ4F_dctx *dctx, void *src, void *dst,
19                        size_t dstCapacity, size_t readSize) {
20     size_t ret = 1;
21     const void *srcPtr = (const char *) src;
22     void *dstPtr = (char *) dst;
23     const void *const srcEnd = (const char *) srcPtr + readSize;
24 
25     while (ret != 0) {
26         while (srcPtr < srcEnd && ret != 0) {
27             /* Any data within dst has been flushed at this stage */
28             size_t dstSize = dstCapacity;
29             size_t srcSize = (const char *) srcEnd - (const char *) srcPtr;
30             ret = LZ4F_decompress(dctx, dstPtr, &dstSize, srcPtr, &srcSize,
31                     /* LZ4F_decompressOptions_t */ NULL);
32             FUZZ_ASSERT(!LZ4F_isError(ret));
33 
34             /* Update input */
35             srcPtr = (const char *) srcPtr + srcSize;
36             dstPtr = (char *) dstPtr + dstSize;
37         }
38 
39         FUZZ_ASSERT(srcPtr <= srcEnd);
40     }
41 }
42 
compress_round_trip(const uint8_t * data,size_t size,FUZZ_dataProducer_t * producer,LZ4F_preferences_t const prefs)43 static void compress_round_trip(const uint8_t *data, size_t size,
44                                 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     size_t const uncompressedOffsetSeed = FUZZ_dataProducer_retrieve32(producer);
49     size_t const uncompressedEndOffsetSeed = FUZZ_dataProducer_retrieve32(producer);
50     size = FUZZ_dataProducer_remainingBytes(producer);
51 
52     size_t const uncompressedOffset = FUZZ_getRange_from_uint32(uncompressedOffsetSeed, 0, size);
53     size_t const uncompressedEndOffset = FUZZ_getRange_from_uint32(uncompressedEndOffsetSeed, uncompressedOffset, size);
54     size_t const uncompressedSize = uncompressedEndOffset - uncompressedOffset;
55     FUZZ_ASSERT(uncompressedOffset <= uncompressedEndOffset);
56     FUZZ_ASSERT(uncompressedEndOffset <= size);
57 
58     const uint8_t *const uncompressedData = data + uncompressedOffset;
59 
60     size_t const dstCapacity =
61             LZ4F_compressFrameBound(LZ4_compressBound(size), &prefs) +
62             uncompressedSize;
63     char *const dst = (char *) malloc(dstCapacity);
64     size_t rtCapacity = dstCapacity;
65     char *const rt = (char *) malloc(rtCapacity);
66 
67     FUZZ_ASSERT(dst);
68     FUZZ_ASSERT(rt);
69 
70     /* Compression must succeed and round trip correctly. */
71     LZ4F_compressionContext_t ctx;
72     size_t const ctxCreation = LZ4F_createCompressionContext(&ctx, LZ4F_VERSION);
73     FUZZ_ASSERT(!LZ4F_isError(ctxCreation));
74 
75     size_t const headerSize = LZ4F_compressBegin(ctx, dst, dstCapacity, &prefs);
76     FUZZ_ASSERT(!LZ4F_isError(headerSize));
77     size_t compressedSize = headerSize;
78 
79     /* Compress data before uncompressed offset */
80     size_t lz4Return = LZ4F_compressUpdate(ctx, dst + compressedSize, dstCapacity,
81                                            data, uncompressedOffset, NULL);
82     FUZZ_ASSERT(!LZ4F_isError(lz4Return));
83     compressedSize += lz4Return;
84 
85     /* Add uncompressed data */
86     lz4Return = LZ4F_uncompressedUpdate(ctx, dst + compressedSize, dstCapacity,
87                                         uncompressedData, uncompressedSize, NULL);
88     FUZZ_ASSERT(!LZ4F_isError(lz4Return));
89     compressedSize += lz4Return;
90 
91     /* Compress data after uncompressed offset */
92     lz4Return = LZ4F_compressUpdate(ctx, dst + compressedSize, dstCapacity,
93                                     data + uncompressedEndOffset,
94                                     size - uncompressedEndOffset, NULL);
95     FUZZ_ASSERT(!LZ4F_isError(lz4Return));
96     compressedSize += lz4Return;
97 
98     /* Finish compression */
99     lz4Return = LZ4F_compressEnd(ctx, dst + compressedSize, dstCapacity, NULL);
100     FUZZ_ASSERT(!LZ4F_isError(lz4Return));
101     compressedSize += lz4Return;
102 
103     LZ4F_decompressOptions_t opts;
104     memset(&opts, 0, sizeof(opts));
105     opts.stableDst = 1;
106     LZ4F_dctx *dctx;
107     LZ4F_createDecompressionContext(&dctx, LZ4F_VERSION);
108     FUZZ_ASSERT(dctx);
109 
110     decompress(dctx, dst, rt, rtCapacity, compressedSize);
111 
112     LZ4F_freeDecompressionContext(dctx);
113 
114     FUZZ_ASSERT_MSG(!memcmp(data, rt, size), "Corruption!");
115 
116     free(dst);
117     free(rt);
118 
119     FUZZ_dataProducer_free(producer);
120     LZ4F_freeCompressionContext(ctx);
121 }
122 
compress_independent_block_mode(const uint8_t * data,size_t size)123 static void compress_independent_block_mode(const uint8_t *data, size_t size) {
124     FUZZ_dataProducer_t *producer = FUZZ_dataProducer_create(data, size);
125     LZ4F_preferences_t prefs = FUZZ_dataProducer_preferences(producer);
126     prefs.frameInfo.blockMode = LZ4F_blockIndependent;
127     compress_round_trip(data, size, producer, prefs);
128 }
129 
130 
LLVMFuzzerTestOneInput(const uint8_t * data,size_t size)131 int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
132     compress_independent_block_mode(data, size);
133     return 0;
134 }
135