Line | Count | Source (jump to first uncovered line) |
1 | | // Copyright 2019 Google LLC |
2 | | // |
3 | | // Licensed under the Apache License, Version 2.0 (the "License"); |
4 | | // you may not use this file except in compliance with the License. |
5 | | // You may obtain a copy of the License at |
6 | | // |
7 | | // http://www.apache.org/licenses/LICENSE-2.0 |
8 | | // |
9 | | // Unless required by applicable law or agreed to in writing, software |
10 | | // distributed under the License is distributed on an "AS IS" BASIS, |
11 | | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
12 | | // See the License for the specific language governing permissions and |
13 | | // limitations under the License. |
14 | | |
15 | | #include <fuzzer/FuzzedDataProvider.h> |
16 | | |
17 | | #include <cstdint> |
18 | | #include <cstdio> |
19 | | #include <cstdlib> |
20 | | #include <memory> |
21 | | #include <vector> |
22 | | |
23 | | #include "lzo1.h" |
24 | | #include "lzo1a.h" |
25 | | #include "lzo1b.h" |
26 | | #include "lzo1c.h" |
27 | | #include "lzo1f.h" |
28 | | #include "lzo1x.h" |
29 | | #include "lzo1y.h" |
30 | | #include "lzo1z.h" |
31 | | #include "lzo2a.h" |
32 | | #include "lzoconf.h" |
33 | | |
34 | | namespace { |
35 | | |
36 | | struct LzoAlgorithm { |
37 | | enum class Category { LZO1, LZO2 }; |
38 | | enum class Type { |
39 | | LZO1, |
40 | | LZO1A, |
41 | | LZO1B, |
42 | | LZO1C, |
43 | | LZO1F, |
44 | | LZO1X, |
45 | | LZO1Y, |
46 | | LZO1Z, |
47 | | LZO2A |
48 | | }; |
49 | | |
50 | | constexpr LzoAlgorithm(Category category, Type type, int compression_level, |
51 | | int memory_level, lzo_compress_t compress_fn, |
52 | | lzo_decompress_t decompress_fn, |
53 | | size_t working_memory_size) |
54 | | : category(category), |
55 | | type(type), |
56 | | compression_level(compression_level), |
57 | | memory_level(memory_level), |
58 | | compress_fn(compress_fn), |
59 | | decompress_fn(decompress_fn), |
60 | 0 | working_memory_size(working_memory_size) {} |
61 | | |
62 | 18.5k | size_t GetMaxCompressedSize(size_t size) const { |
63 | | // Formula taken from the LZO FAQ. |
64 | 18.5k | switch (category) { |
65 | 17.7k | case Category::LZO1: |
66 | 17.7k | return size + (size / 16) + 64 + 3; |
67 | 814 | case Category::LZO2: |
68 | 814 | return size + (size / 8) + 128 + 3; |
69 | 18.5k | } |
70 | 18.5k | } |
71 | | |
72 | | Category category; |
73 | | Type type; |
74 | | int compression_level; |
75 | | int memory_level; |
76 | | |
77 | | lzo_compress_t compress_fn; |
78 | | lzo_decompress_t decompress_fn; |
79 | | size_t working_memory_size; |
80 | | }; |
81 | | |
82 | 18.5k | static const std::vector<std::vector<LzoAlgorithm>>& GetLzoAlgorithms() { |
83 | 18.5k | static auto* algorithms = new std::vector<std::vector<LzoAlgorithm>>{ |
84 | 18.5k | { |
85 | 18.5k | LzoAlgorithm(LzoAlgorithm::Category::LZO1, LzoAlgorithm::Type::LZO1, |
86 | 18.5k | 0, 0, lzo1_compress, lzo1_decompress, LZO1_MEM_COMPRESS), |
87 | 18.5k | LzoAlgorithm(LzoAlgorithm::Category::LZO1, LzoAlgorithm::Type::LZO1, |
88 | 18.5k | 99, 0, lzo1_99_compress, lzo1_decompress, |
89 | 18.5k | LZO1_99_MEM_COMPRESS), |
90 | 18.5k | }, |
91 | 18.5k | { |
92 | 18.5k | LzoAlgorithm(LzoAlgorithm::Category::LZO1, LzoAlgorithm::Type::LZO1A, |
93 | 18.5k | 0, 0, lzo1a_compress, lzo1a_decompress, |
94 | 18.5k | LZO1A_MEM_COMPRESS), |
95 | 18.5k | LzoAlgorithm(LzoAlgorithm::Category::LZO1, LzoAlgorithm::Type::LZO1A, |
96 | 18.5k | 99, 0, lzo1a_99_compress, lzo1a_decompress, |
97 | 18.5k | LZO1A_99_MEM_COMPRESS), |
98 | 18.5k | }, |
99 | 18.5k | { |
100 | 18.5k | LzoAlgorithm(LzoAlgorithm::Category::LZO1, LzoAlgorithm::Type::LZO1B, |
101 | 18.5k | 1, 0, lzo1b_1_compress, lzo1b_decompress, |
102 | 18.5k | LZO1B_MEM_COMPRESS), |
103 | 18.5k | LzoAlgorithm(LzoAlgorithm::Category::LZO1, LzoAlgorithm::Type::LZO1B, |
104 | 18.5k | 2, 0, lzo1b_2_compress, lzo1b_decompress, |
105 | 18.5k | LZO1B_MEM_COMPRESS), |
106 | 18.5k | LzoAlgorithm(LzoAlgorithm::Category::LZO1, LzoAlgorithm::Type::LZO1B, |
107 | 18.5k | 3, 0, lzo1b_3_compress, lzo1b_decompress, |
108 | 18.5k | LZO1B_MEM_COMPRESS), |
109 | 18.5k | LzoAlgorithm(LzoAlgorithm::Category::LZO1, LzoAlgorithm::Type::LZO1B, |
110 | 18.5k | 4, 0, lzo1b_4_compress, lzo1b_decompress, |
111 | 18.5k | LZO1B_MEM_COMPRESS), |
112 | 18.5k | LzoAlgorithm(LzoAlgorithm::Category::LZO1, LzoAlgorithm::Type::LZO1B, |
113 | 18.5k | 5, 0, lzo1b_5_compress, lzo1b_decompress, |
114 | 18.5k | LZO1B_MEM_COMPRESS), |
115 | 18.5k | LzoAlgorithm(LzoAlgorithm::Category::LZO1, LzoAlgorithm::Type::LZO1B, |
116 | 18.5k | 6, 0, lzo1b_6_compress, lzo1b_decompress, |
117 | 18.5k | LZO1B_MEM_COMPRESS), |
118 | 18.5k | LzoAlgorithm(LzoAlgorithm::Category::LZO1, LzoAlgorithm::Type::LZO1B, |
119 | 18.5k | 7, 0, lzo1b_7_compress, lzo1b_decompress, |
120 | 18.5k | LZO1B_MEM_COMPRESS), |
121 | 18.5k | LzoAlgorithm(LzoAlgorithm::Category::LZO1, LzoAlgorithm::Type::LZO1B, |
122 | 18.5k | 8, 0, lzo1b_8_compress, lzo1b_decompress, |
123 | 18.5k | LZO1B_MEM_COMPRESS), |
124 | 18.5k | LzoAlgorithm(LzoAlgorithm::Category::LZO1, LzoAlgorithm::Type::LZO1B, |
125 | 18.5k | 9, 0, lzo1b_9_compress, lzo1b_decompress, |
126 | 18.5k | LZO1B_MEM_COMPRESS), |
127 | 18.5k | LzoAlgorithm(LzoAlgorithm::Category::LZO1, LzoAlgorithm::Type::LZO1B, |
128 | 18.5k | 99, 0, lzo1b_99_compress, lzo1b_decompress, |
129 | 18.5k | LZO1B_99_MEM_COMPRESS), |
130 | 18.5k | LzoAlgorithm(LzoAlgorithm::Category::LZO1, LzoAlgorithm::Type::LZO1B, |
131 | 18.5k | 999, 0, lzo1b_999_compress, lzo1b_decompress, |
132 | 18.5k | LZO1B_999_MEM_COMPRESS), |
133 | 18.5k | }, |
134 | 18.5k | { |
135 | 18.5k | LzoAlgorithm(LzoAlgorithm::Category::LZO1, LzoAlgorithm::Type::LZO1C, |
136 | 18.5k | 1, 0, lzo1c_1_compress, lzo1c_decompress, |
137 | 18.5k | LZO1C_MEM_COMPRESS), |
138 | 18.5k | LzoAlgorithm(LzoAlgorithm::Category::LZO1, LzoAlgorithm::Type::LZO1C, |
139 | 18.5k | 5, 0, lzo1c_5_compress, lzo1c_decompress, |
140 | 18.5k | LZO1C_MEM_COMPRESS), |
141 | 18.5k | LzoAlgorithm(LzoAlgorithm::Category::LZO1, LzoAlgorithm::Type::LZO1C, |
142 | 18.5k | 9, 0, lzo1c_9_compress, lzo1c_decompress, |
143 | 18.5k | LZO1C_MEM_COMPRESS), |
144 | 18.5k | LzoAlgorithm(LzoAlgorithm::Category::LZO1, LzoAlgorithm::Type::LZO1C, |
145 | 18.5k | 99, 0, lzo1c_99_compress, lzo1c_decompress, |
146 | 18.5k | LZO1C_99_MEM_COMPRESS), |
147 | 18.5k | LzoAlgorithm(LzoAlgorithm::Category::LZO1, LzoAlgorithm::Type::LZO1C, |
148 | 18.5k | 999, 0, lzo1c_999_compress, lzo1c_decompress, |
149 | 18.5k | LZO1C_999_MEM_COMPRESS), |
150 | 18.5k | }, |
151 | 18.5k | { |
152 | 18.5k | LzoAlgorithm(LzoAlgorithm::Category::LZO1, LzoAlgorithm::Type::LZO1F, |
153 | 18.5k | 1, 0, lzo1f_1_compress, lzo1f_decompress, |
154 | 18.5k | LZO1F_MEM_COMPRESS), |
155 | 18.5k | LzoAlgorithm(LzoAlgorithm::Category::LZO1, LzoAlgorithm::Type::LZO1F, |
156 | 18.5k | 999, 0, lzo1f_999_compress, lzo1f_decompress, |
157 | 18.5k | LZO1F_999_MEM_COMPRESS), |
158 | 18.5k | }, |
159 | 18.5k | { |
160 | 18.5k | LzoAlgorithm(LzoAlgorithm::Category::LZO1, LzoAlgorithm::Type::LZO1X, |
161 | 18.5k | 1, 0, lzo1x_1_compress, lzo1x_decompress, |
162 | 18.5k | LZO1X_1_MEM_COMPRESS), |
163 | 18.5k | LzoAlgorithm(LzoAlgorithm::Category::LZO1, LzoAlgorithm::Type::LZO1X, |
164 | 18.5k | 1, 11, lzo1x_1_11_compress, lzo1x_decompress, |
165 | 18.5k | LZO1X_1_11_MEM_COMPRESS), |
166 | 18.5k | LzoAlgorithm(LzoAlgorithm::Category::LZO1, LzoAlgorithm::Type::LZO1X, |
167 | 18.5k | 1, 12, lzo1x_1_12_compress, lzo1x_decompress, |
168 | 18.5k | LZO1X_1_12_MEM_COMPRESS), |
169 | 18.5k | LzoAlgorithm(LzoAlgorithm::Category::LZO1, LzoAlgorithm::Type::LZO1X, |
170 | 18.5k | 1, 15, lzo1x_1_15_compress, lzo1x_decompress, |
171 | 18.5k | LZO1X_1_15_MEM_COMPRESS), |
172 | 18.5k | LzoAlgorithm(LzoAlgorithm::Category::LZO1, LzoAlgorithm::Type::LZO1X, |
173 | 18.5k | 999, 0, lzo1x_999_compress, lzo1x_decompress, |
174 | 18.5k | LZO1X_999_MEM_COMPRESS), |
175 | 18.5k | }, |
176 | 18.5k | { |
177 | 18.5k | LzoAlgorithm(LzoAlgorithm::Category::LZO1, LzoAlgorithm::Type::LZO1Y, |
178 | 18.5k | 1, 0, lzo1y_1_compress, lzo1y_decompress, |
179 | 18.5k | LZO1Y_MEM_COMPRESS), |
180 | 18.5k | LzoAlgorithm(LzoAlgorithm::Category::LZO1, LzoAlgorithm::Type::LZO1Y, |
181 | 18.5k | 999, 0, lzo1y_999_compress, lzo1y_decompress, |
182 | 18.5k | LZO1Y_999_MEM_COMPRESS), |
183 | 18.5k | }, |
184 | 18.5k | { |
185 | 18.5k | LzoAlgorithm(LzoAlgorithm::Category::LZO1, LzoAlgorithm::Type::LZO1Z, |
186 | 18.5k | 999, 0, lzo1z_999_compress, lzo1z_decompress, |
187 | 18.5k | LZO1Z_999_MEM_COMPRESS), |
188 | 18.5k | }, |
189 | 18.5k | { |
190 | 18.5k | LzoAlgorithm(LzoAlgorithm::Category::LZO2, LzoAlgorithm::Type::LZO2A, |
191 | 18.5k | 999, 0, lzo2a_999_compress, lzo2a_decompress, |
192 | 18.5k | LZO2A_999_MEM_COMPRESS), |
193 | 18.5k | }, |
194 | 18.5k | }; |
195 | 18.5k | return *algorithms; |
196 | 18.5k | } |
197 | | |
198 | | void FuzzLzoAlgorithm(const LzoAlgorithm& algorithm, |
199 | 18.5k | const std::vector<uint8_t>& input_buffer) { |
200 | 18.5k | std::unique_ptr<uint8_t[]> working_buffer( |
201 | 18.5k | new uint8_t[algorithm.working_memory_size]); |
202 | 18.5k | std::unique_ptr<uint8_t[]> compressed_buffer( |
203 | 18.5k | new uint8_t[algorithm.GetMaxCompressedSize(input_buffer.size())]); |
204 | | |
205 | | #if MEMORY_SANITIZER |
206 | | __msan_unpoison(working_buffer.get(), algorithm.working_memory_size); |
207 | | #endif |
208 | | |
209 | 18.5k | lzo_uint compressed_size; |
210 | 18.5k | if (algorithm.compress_fn(input_buffer.data(), input_buffer.size(), |
211 | 18.5k | compressed_buffer.get(), &compressed_size, |
212 | 18.5k | working_buffer.get()) != LZO_E_OK) { |
213 | 0 | abort(); |
214 | 0 | } |
215 | | |
216 | 18.5k | std::unique_ptr<uint8_t[]> decompressed_buffer( |
217 | 18.5k | new uint8_t[input_buffer.size()]); |
218 | 18.5k | lzo_uint decompressed_size; |
219 | 18.5k | if (algorithm.decompress_fn(compressed_buffer.get(), compressed_size, |
220 | 18.5k | decompressed_buffer.get(), &decompressed_size, |
221 | 18.5k | nullptr) != LZO_E_OK) { |
222 | 0 | abort(); |
223 | 0 | } |
224 | | |
225 | 18.5k | if (decompressed_size != input_buffer.size()) { |
226 | 0 | fprintf(stderr, "Decompressed size %zu does not match original size %zu.\n", |
227 | 0 | decompressed_size, input_buffer.size()); |
228 | 0 | abort(); |
229 | 18.5k | } else if (memcmp(input_buffer.data(), decompressed_buffer.get(), |
230 | 18.5k | input_buffer.size()) != 0) { |
231 | 0 | fprintf(stderr, |
232 | 0 | "Decompressed buffer does not match original buffer of size %zu.\n", |
233 | 0 | input_buffer.size()); |
234 | 0 | abort(); |
235 | 0 | } |
236 | 18.5k | } |
237 | | |
238 | | } // namespace |
239 | | |
240 | 18.5k | extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { |
241 | 18.5k | static bool initialized __attribute__((unused)) = []() { |
242 | 1 | if (lzo_init() != LZO_E_OK) { |
243 | 0 | abort(); |
244 | 0 | } |
245 | 1 | return true; |
246 | 1 | }(); |
247 | | |
248 | 18.5k | FuzzedDataProvider data_provider(data, size); |
249 | 18.5k | const auto& algorithms = GetLzoAlgorithms(); |
250 | 18.5k | const auto first_level_index = |
251 | 18.5k | data_provider.ConsumeIntegralInRange<size_t>(0, algorithms.size() - 1); |
252 | 18.5k | const auto& algorithm_group = algorithms[first_level_index]; |
253 | 18.5k | const auto second_level_index = data_provider.ConsumeIntegralInRange<size_t>( |
254 | 18.5k | 0, algorithm_group.size() - 1); |
255 | 18.5k | const std::vector<uint8_t> input_buffer = |
256 | 18.5k | data_provider.ConsumeRemainingBytes<uint8_t>(); |
257 | 18.5k | FuzzLzoAlgorithm(algorithm_group[second_level_index], input_buffer); |
258 | 18.5k | return 0; |
259 | 18.5k | } |