xref: /aosp_15_r20/external/zlib/contrib/tests/fuzzers/deflate_fuzzer.cc (revision 86ee64e75fa5f8bce2c8c356138035642429cd05)
1 // Copyright 2017 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include <fuzzer/FuzzedDataProvider.h>
6 #include <stddef.h>
7 #include <stdint.h>
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <vector>
12 
13 #include "zlib.h"
14 
15 // Fuzzer builds often have NDEBUG set, so roll our own assert macro.
16 #define ASSERT(cond)                                                           \
17   do {                                                                         \
18     if (!(cond)) {                                                             \
19       fprintf(stderr, "%s:%d Assert failed: %s\n", __FILE__, __LINE__, #cond); \
20       exit(1);                                                                 \
21     }                                                                          \
22   } while (0)
23 
LLVMFuzzerTestOneInput(const uint8_t * data,size_t size)24 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
25   FuzzedDataProvider fdp(data, size);
26   int level = fdp.PickValueInArray({-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9});
27   int windowBits = fdp.PickValueInArray({9, 10, 11, 12, 13, 14, 15});
28   int memLevel = fdp.PickValueInArray({1, 2, 3, 4, 5, 6, 7, 8, 9});
29   int strategy = fdp.PickValueInArray(
30       {Z_DEFAULT_STRATEGY, Z_FILTERED, Z_HUFFMAN_ONLY, Z_RLE, Z_FIXED});
31 
32    if (fdp.ConsumeBool()) {
33     // Gzip wrapper.
34     windowBits += 16;
35   } else if (fdp.ConsumeBool()) {
36     // Raw deflate.
37     windowBits *= -1;
38   } else {
39     // Default: zlib wrapper.
40   }
41 
42   std::vector<uint8_t> src;
43   std::vector<uint8_t> compressed;
44   static const int kMinChunk = 1;
45   static const int kMaxChunk = 512 * 1024;
46 
47   z_stream stream;
48   stream.zalloc = Z_NULL;
49   stream.zfree = Z_NULL;
50   int ret =
51       deflateInit2(&stream, level, Z_DEFLATED, windowBits, memLevel, strategy);
52   ASSERT(ret == Z_OK);
53 
54   // Stream with random-sized input and output buffers.
55   while (fdp.ConsumeBool()) {
56     if (fdp.ConsumeBool()) {
57       // Check that copying the stream's state works. Gating this behind
58       // ConsumeBool() allows to interleave deflateCopy() with deflate() calls
59       // to better stress the code.
60       z_stream stream2;
61       ASSERT(deflateCopy(&stream2, &stream) == Z_OK);
62       ret = deflateEnd(&stream);
63       ASSERT(ret == Z_OK || Z_DATA_ERROR);
64       memset(&stream, 0xff, sizeof(stream));
65 
66       ASSERT(deflateCopy(&stream, &stream2) == Z_OK);
67       ret = deflateEnd(&stream2);
68       ASSERT(ret == Z_OK || Z_DATA_ERROR);
69     }
70 
71     std::vector<uint8_t> src_chunk = fdp.ConsumeBytes<uint8_t>(
72         fdp.ConsumeIntegralInRange(kMinChunk, kMaxChunk));
73     std::vector<uint8_t> out_chunk(
74         fdp.ConsumeIntegralInRange(kMinChunk, kMaxChunk));
75     stream.next_in = src_chunk.data();
76     stream.avail_in = src_chunk.size();
77     stream.next_out = out_chunk.data();
78     stream.avail_out = out_chunk.size();
79     ret = deflate(&stream, Z_NO_FLUSH);
80     ASSERT(ret == Z_OK || ret == Z_BUF_ERROR);
81 
82     src.insert(src.end(), src_chunk.begin(), src_chunk.end() - stream.avail_in);
83     compressed.insert(compressed.end(), out_chunk.begin(),
84                       out_chunk.end() - stream.avail_out);
85   }
86   // Finish up.
87   while (true) {
88     std::vector<uint8_t> out_chunk(
89         fdp.ConsumeIntegralInRange(kMinChunk, kMaxChunk));
90     stream.next_in = Z_NULL;
91     stream.avail_in = 0;
92     stream.next_out = out_chunk.data();
93     stream.avail_out = out_chunk.size();
94     ret = deflate(&stream, Z_FINISH);
95     compressed.insert(compressed.end(), out_chunk.begin(),
96                       out_chunk.end() - stream.avail_out);
97     if (ret == Z_STREAM_END) {
98       break;
99     }
100     ASSERT(ret == Z_OK || Z_BUF_ERROR);
101   }
102   deflateEnd(&stream);
103 
104   // Check deflateBound().
105   // Use a newly initialized stream since computing the bound on a "used" stream
106   // may not yield a correct result (https://github.com/madler/zlib/issues/944).
107   z_stream bound_stream;
108   bound_stream.zalloc = Z_NULL;
109   bound_stream.zfree = Z_NULL;
110   ret = deflateInit2(&bound_stream, level, Z_DEFLATED, windowBits, memLevel,
111                      strategy);
112   ASSERT(ret == Z_OK);
113   size_t deflate_bound = deflateBound(&bound_stream, src.size());
114   ASSERT(compressed.size() <= deflate_bound);
115   deflateEnd(&bound_stream);
116 
117   // Verify that the data decompresses correctly.
118   ret = inflateInit2(&stream, windowBits);
119   ASSERT(ret == Z_OK);
120   // Make room for at least one byte so it's never empty.
121   std::vector<uint8_t> decompressed(src.size() + 1);
122   stream.next_in = compressed.data();
123   stream.avail_in = compressed.size();
124   stream.next_out = decompressed.data();
125   stream.avail_out = decompressed.size();
126   ret = inflate(&stream, Z_FINISH);
127   ASSERT(ret == Z_STREAM_END);
128   decompressed.resize(decompressed.size() - stream.avail_out);
129   inflateEnd(&stream);
130 
131   ASSERT(decompressed == src);
132 
133   return 0;
134 }
135