/src/fuzz_webp_enc_dec.cc
Line | Count | Source (jump to first uncovered line) |
1 | | // Copyright 2018 Google Inc. |
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 | | //////////////////////////////////////////////////////////////////////////////// |
16 | | |
17 | | #include <stdio.h> |
18 | | #include <stdlib.h> |
19 | | #include "webp/encode.h" |
20 | | #include "webp/decode.h" |
21 | | #include "img_alpha.h" |
22 | | #include "img_grid.h" |
23 | | #include "img_peak.h" |
24 | | #include "dsp/dsp.h" |
25 | | |
26 | | namespace { |
27 | | |
28 | | const VP8CPUInfo LibGetCPUInfo = VP8GetCPUInfo; |
29 | | |
30 | 5.30k | int GetCPUInfoNoSSE41(CPUFeature feature) { |
31 | 5.30k | if (feature == kSSE4_1 || feature == kAVX) return 0; |
32 | 3.31k | return LibGetCPUInfo(feature); |
33 | 3.31k | } |
34 | | |
35 | 2.38k | int GetCPUInfoNoAVX(CPUFeature feature) { |
36 | 2.38k | if (feature == kAVX) return 0; |
37 | 2.38k | return LibGetCPUInfo(feature); |
38 | 2.38k | } |
39 | | |
40 | 4.35k | int GetCPUInfoForceSlowSSSE3(CPUFeature feature) { |
41 | 4.35k | if (feature == kSlowSSSE3 && LibGetCPUInfo(kSSE3)) { |
42 | 0 | return 1; // we have SSE3 -> force SlowSSSE3 |
43 | 0 | } |
44 | 4.35k | return LibGetCPUInfo(feature); |
45 | 4.35k | } |
46 | | |
47 | 4.05k | int GetCPUInfoOnlyC(CPUFeature feature) { |
48 | 4.05k | return false; |
49 | 4.05k | } |
50 | | |
51 | | const VP8CPUInfo kVP8CPUInfos[5] = { |
52 | | GetCPUInfoOnlyC, GetCPUInfoForceSlowSSSE3, |
53 | | GetCPUInfoNoSSE41, GetCPUInfoNoAVX, LibGetCPUInfo |
54 | | }; |
55 | | |
56 | | static uint32_t Extract(uint32_t max, const uint8_t data[], size_t size, |
57 | 87.0k | uint32_t* const bit_pos) { |
58 | 87.0k | uint32_t v = 0; |
59 | 87.0k | int range = 1; |
60 | 276k | while (*bit_pos < 8 * size && range <= max) { |
61 | 189k | const uint8_t mask = 1u << (*bit_pos & 7); |
62 | 189k | v = (v << 1) | !!(data[*bit_pos >> 3] & mask); |
63 | 189k | range <<= 1; |
64 | 189k | ++*bit_pos; |
65 | 189k | } |
66 | 87.0k | return v % (max + 1); |
67 | 87.0k | } |
68 | | |
69 | 992 | static int max(int a, int b) { return ((a < b) ? b : a); } |
70 | | |
71 | | } // namespace |
72 | | |
73 | 2.77k | extern "C" int LLVMFuzzerTestOneInput(const uint8_t* const data, size_t size) { |
74 | 2.77k | // Extract a configuration from the packed bits. |
75 | 2.77k | WebPConfig config; |
76 | 2.77k | if (!WebPConfigInit(&config)) { |
77 | 0 | fprintf(stderr, "WebPConfigInit failed.\n"); |
78 | 0 | abort(); |
79 | 0 | } |
80 | 2.77k | uint32_t bit_pos = 0; |
81 | 2.77k | config.lossless = Extract(1, data, size, &bit_pos); |
82 | 2.77k | config.quality = Extract(100, data, size, &bit_pos); |
83 | 2.77k | config.method = Extract(6, data, size, &bit_pos); |
84 | 2.77k | config.image_hint = |
85 | 2.77k | (WebPImageHint)Extract(WEBP_HINT_LAST - 1, data, size, &bit_pos); |
86 | 2.77k | config.segments = 1 + Extract(3, data, size, &bit_pos); |
87 | 2.77k | config.sns_strength = Extract(100, data, size, &bit_pos); |
88 | 2.77k | config.filter_strength = Extract(100, data, size, &bit_pos); |
89 | 2.77k | config.filter_sharpness = Extract(7, data, size, &bit_pos); |
90 | 2.77k | config.filter_type = Extract(1, data, size, &bit_pos); |
91 | 2.77k | config.autofilter = Extract(1, data, size, &bit_pos); |
92 | 2.77k | config.alpha_compression = Extract(1, data, size, &bit_pos); |
93 | 2.77k | config.alpha_filtering = Extract(2, data, size, &bit_pos); |
94 | 2.77k | config.alpha_quality = Extract(100, data, size, &bit_pos); |
95 | 2.77k | config.pass = 1 + Extract(9, data, size, &bit_pos); |
96 | 2.77k | config.show_compressed = 1; |
97 | 2.77k | config.preprocessing = Extract(2, data, size, &bit_pos); |
98 | 2.77k | config.partitions = Extract(3, data, size, &bit_pos); |
99 | 2.77k | config.partition_limit = 10 * Extract(10, data, size, &bit_pos); |
100 | 2.77k | config.emulate_jpeg_size = Extract(1, data, size, &bit_pos); |
101 | 2.77k | config.thread_level = Extract(1, data, size, &bit_pos); |
102 | 2.77k | config.low_memory = Extract(1, data, size, &bit_pos); |
103 | 2.77k | config.near_lossless = 20 * Extract(5, data, size, &bit_pos); |
104 | 2.77k | config.exact = Extract(1, data, size, &bit_pos); |
105 | 2.77k | config.use_delta_palette = Extract(1, data, size, &bit_pos); |
106 | 2.77k | config.use_sharp_yuv = Extract(1, data, size, &bit_pos); |
107 | 2.77k | if (!WebPValidateConfig(&config)) { |
108 | 0 | fprintf(stderr, "WebPValidateConfig failed.\n"); |
109 | 0 | abort(); |
110 | 0 | } |
111 | 2.77k | |
112 | 2.77k | // Init the source picture. |
113 | 2.77k | WebPPicture pic; |
114 | 2.77k | if (!WebPPictureInit(&pic)) { |
115 | 0 | fprintf(stderr, "WebPPictureInit failed.\n"); |
116 | 0 | abort(); |
117 | 0 | } |
118 | 2.77k | pic.use_argb = Extract(1, data, size, &bit_pos); |
119 | 2.77k | |
120 | 2.77k | VP8GetCPUInfo = kVP8CPUInfos[Extract(4, data, size, &bit_pos)]; |
121 | 2.77k | |
122 | 2.77k | // Pick a source picture. |
123 | 2.77k | const uint8_t* kImagesData[] = { |
124 | 2.77k | kImgAlphaData, |
125 | 2.77k | kImgGridData, |
126 | 2.77k | kImgPeakData |
127 | 2.77k | }; |
128 | 2.77k | const int kImagesWidth[] = { |
129 | 2.77k | kImgAlphaWidth, |
130 | 2.77k | kImgGridWidth, |
131 | 2.77k | kImgPeakWidth |
132 | 2.77k | }; |
133 | 2.77k | const int kImagesHeight[] = { |
134 | 2.77k | kImgAlphaHeight, |
135 | 2.77k | kImgGridHeight, |
136 | 2.77k | kImgPeakHeight |
137 | 2.77k | }; |
138 | 2.77k | const size_t kNbImages = sizeof(kImagesData) / sizeof(kImagesData[0]); |
139 | 2.77k | const size_t image_index = Extract(kNbImages - 1, data, size, &bit_pos); |
140 | 2.77k | const uint8_t* const image_data = kImagesData[image_index]; |
141 | 2.77k | pic.width = kImagesWidth[image_index]; |
142 | 2.77k | pic.height = kImagesHeight[image_index]; |
143 | 2.77k | pic.argb_stride = pic.width * 4 * sizeof(uint8_t); |
144 | 2.77k | |
145 | 2.77k | // Read the bytes. |
146 | 2.77k | if (!WebPPictureImportRGBA(&pic, image_data, pic.argb_stride)) { |
147 | 0 | fprintf(stderr, "Can't read input image: %zu\n", image_index); |
148 | 0 | WebPPictureFree(&pic); |
149 | 0 | abort(); |
150 | 0 | } |
151 | 2.77k | |
152 | 2.77k | // Crop and scale. |
153 | 2.77k | const bool alter_input = Extract(1, data, size, &bit_pos) != 0; |
154 | 2.77k | const bool crop_or_scale = Extract(1, data, size, &bit_pos) != 0; |
155 | 2.77k | const int width_ratio = 1 + Extract(7, data, size, &bit_pos); |
156 | 2.77k | const int height_ratio = 1 + Extract(7, data, size, &bit_pos); |
157 | 2.77k | if (alter_input) { |
158 | 1.67k | if (crop_or_scale) { |
159 | 496 | const uint32_t left_ratio = 1 + Extract(7, data, size, &bit_pos); |
160 | 496 | const uint32_t top_ratio = 1 + Extract(7, data, size, &bit_pos); |
161 | 496 | const int cropped_width = max(1, pic.width / width_ratio); |
162 | 496 | const int cropped_height = max(1, pic.height / height_ratio); |
163 | 496 | const int cropped_left = (pic.width - cropped_width) / left_ratio; |
164 | 496 | const int cropped_top = (pic.height - cropped_height) / top_ratio; |
165 | 496 | if (!WebPPictureCrop(&pic, cropped_left, cropped_top, cropped_width, |
166 | 496 | cropped_height)) { |
167 | 0 | fprintf(stderr, "WebPPictureCrop failed. Parameters: %d,%d,%d,%d\n", |
168 | 0 | cropped_left, cropped_top, cropped_width, cropped_height); |
169 | 0 | WebPPictureFree(&pic); |
170 | 0 | abort(); |
171 | 0 | } |
172 | 1.18k | } else { |
173 | 1.18k | const int scaled_width = 1 + pic.width * width_ratio / 4; |
174 | 1.18k | const int scaled_height = 1 + pic.height * height_ratio / 4; |
175 | 1.18k | if (!WebPPictureRescale(&pic, scaled_width, scaled_height)) { |
176 | 0 | fprintf(stderr, "WebPPictureRescale failed. Parameters: %d,%d\n", |
177 | 0 | scaled_width, scaled_height); |
178 | 0 | WebPPictureFree(&pic); |
179 | 0 | abort(); |
180 | 0 | } |
181 | 2.77k | } |
182 | 1.67k | } |
183 | 2.77k | |
184 | 2.77k | // Skip the cruncher except on small images, it's likely to timeout. |
185 | 2.77k | if (config.lossless && config.quality == 100. && config.method == 6 && |
186 | 2.77k | pic.width * pic.height >= 16 * 16) { |
187 | 31 | config.lossless = 0; |
188 | 31 | } |
189 | 2.77k | if (config.alpha_quality == 100 && config.method == 6 && |
190 | 2.77k | pic.width * pic.height >= 16 * 16) { |
191 | 41 | config.alpha_quality = 99; |
192 | 41 | } |
193 | 2.77k | |
194 | 2.77k | // Encode. |
195 | 2.77k | WebPMemoryWriter memory_writer; |
196 | 2.77k | WebPMemoryWriterInit(&memory_writer); |
197 | 2.77k | pic.writer = WebPMemoryWrite; |
198 | 2.77k | pic.custom_ptr = &memory_writer; |
199 | 2.77k | if (!WebPEncode(&config, &pic)) { |
200 | 0 | fprintf(stderr, "WebPEncode failed. Error code: %d\nFile: %zu\n", |
201 | 0 | pic.error_code, image_index); |
202 | 0 | WebPMemoryWriterClear(&memory_writer); |
203 | 0 | WebPPictureFree(&pic); |
204 | 0 | abort(); |
205 | 0 | } |
206 | 2.77k | |
207 | 2.77k | // Try decoding the result. |
208 | 2.77k | int w, h; |
209 | 2.77k | const uint8_t* const out_data = memory_writer.mem; |
210 | 2.77k | const size_t out_size = memory_writer.size; |
211 | 2.77k | uint8_t* const rgba = WebPDecodeBGRA(out_data, out_size, &w, &h); |
212 | 2.77k | if (rgba == nullptr || w != pic.width || h != pic.height) { |
213 | 0 | fprintf(stderr, "WebPDecodeBGRA failed.\nFile: %zu\n", image_index); |
214 | 0 | WebPFree(rgba); |
215 | 0 | WebPMemoryWriterClear(&memory_writer); |
216 | 0 | WebPPictureFree(&pic); |
217 | 0 | abort(); |
218 | 0 | } |
219 | 2.77k | |
220 | 2.77k | // Compare the results if exact encoding. |
221 | 2.77k | if (pic.use_argb && config.lossless && config.near_lossless == 100) { |
222 | 92 | const uint32_t* src1 = (const uint32_t*)rgba; |
223 | 92 | const uint32_t* src2 = pic.argb; |
224 | 7.74k | for (int y = 0; y < h; ++y, src1 += w, src2 += pic.argb_stride) { |
225 | 1.16M | for (int x = 0; x < w; ++x) { |
226 | 1.15M | uint32_t v1 = src1[x], v2 = src2[x]; |
227 | 1.15M | if (!config.exact) { |
228 | 808k | if ((v1 & 0xff000000u) == 0 || (v2 & 0xff000000u) == 0) { |
229 | 0 | // Only keep alpha for comparison of fully transparent area. |
230 | 0 | v1 &= 0xff000000u; |
231 | 0 | v2 &= 0xff000000u; |
232 | 0 | } |
233 | 808k | } |
234 | 1.15M | if (v1 != v2) { |
235 | 0 | fprintf(stderr, |
236 | 0 | "Lossless compression failed pixel-exactness.\nFile: %zu\n", |
237 | 0 | image_index); |
238 | 0 | WebPFree(rgba); |
239 | 0 | WebPMemoryWriterClear(&memory_writer); |
240 | 0 | WebPPictureFree(&pic); |
241 | 0 | abort(); |
242 | 0 | } |
243 | 1.15M | } |
244 | 7.64k | } |
245 | 92 | } |
246 | 2.77k | |
247 | 2.77k | WebPFree(rgba); |
248 | 2.77k | WebPMemoryWriterClear(&memory_writer); |
249 | 2.77k | WebPPictureFree(&pic); |
250 | 2.77k | return 0; |
251 | 2.77k | } |