/src/libwebp/tests/fuzzer/animencoder_fuzzer.cc
Line | Count | Source |
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 <cstdint> |
18 | | #include <cstdio> |
19 | | #include <cstdlib> |
20 | | #include <utility> |
21 | | #include <vector> |
22 | | |
23 | | #include "./fuzz_utils.h" |
24 | | #include "gtest/gtest.h" |
25 | | #include "src/dsp/cpu.h" |
26 | | #include "webp/encode.h" |
27 | | #include "webp/mux.h" |
28 | | #include "webp/mux_types.h" |
29 | | |
30 | | namespace { |
31 | | |
32 | | const VP8CPUInfo default_VP8GetCPUInfo = fuzz_utils::VP8GetCPUInfo; |
33 | | |
34 | | struct FrameConfig { |
35 | | int use_argb; |
36 | | int timestamp; |
37 | | WebPConfig webp_config; |
38 | | fuzz_utils::CropOrScaleParams crop_or_scale_params; |
39 | | fuzz_utils::WebPPictureCpp pic_cpp; |
40 | | }; |
41 | | |
42 | 8 | auto ArbitraryKMinKMax() { |
43 | 8 | return fuzztest::FlatMap( |
44 | 60.5k | [](int kmax) { |
45 | 60.5k | const int min_kmin = (kmax > 1) ? (kmax / 2) : 0; |
46 | 60.5k | const int max_kmin = (kmax > 1) ? (kmax - 1) : 0; |
47 | 60.5k | return fuzztest::PairOf(fuzztest::InRange(min_kmin, max_kmin), |
48 | 60.5k | fuzztest::Just(kmax)); |
49 | 60.5k | }, |
50 | 8 | fuzztest::InRange(0, 15)); |
51 | 8 | } |
52 | | |
53 | | int AddFrame(WebPAnimEncoder** const enc, |
54 | | const WebPAnimEncoderOptions& anim_config, int* const width, |
55 | | int* const height, int timestamp_ms, FrameConfig& frame_config, |
56 | 38.5k | uint32_t* const bit_pos) { |
57 | 38.5k | if (enc == nullptr || width == nullptr || height == nullptr) { |
58 | 0 | fprintf(stderr, "NULL parameters.\n"); |
59 | 0 | if (enc != nullptr) WebPAnimEncoderDelete(*enc); |
60 | 0 | std::abort(); |
61 | 0 | } |
62 | | |
63 | | // Init the source picture. |
64 | 38.5k | WebPPicture& pic = frame_config.pic_cpp.ref(); |
65 | | |
66 | | // Crop and scale. |
67 | 38.5k | if (*enc == nullptr) { // First frame will set canvas width and height. |
68 | 19.9k | if (!fuzz_utils::CropOrScale(&pic, frame_config.crop_or_scale_params)) { |
69 | 0 | const WebPEncodingError error_code = pic.error_code; |
70 | 0 | if (error_code == VP8_ENC_ERROR_OUT_OF_MEMORY) return 0; |
71 | 0 | fprintf(stderr, "ExtractAndCropOrScale failed. Error code: %d\n", |
72 | 0 | error_code); |
73 | 0 | std::abort(); |
74 | 0 | } |
75 | 19.9k | } else { // Other frames will be resized to the first frame's dimensions. |
76 | 18.6k | if (!WebPPictureRescale(&pic, *width, *height)) { |
77 | 0 | const WebPEncodingError error_code = pic.error_code; |
78 | 0 | WebPAnimEncoderDelete(*enc); |
79 | 0 | if (error_code == VP8_ENC_ERROR_OUT_OF_MEMORY) return 0; |
80 | 0 | fprintf(stderr, |
81 | 0 | "WebPPictureRescale failed. Size: %d,%d. Error code: %d\n", |
82 | 0 | *width, *height, error_code); |
83 | 0 | std::abort(); |
84 | 0 | } |
85 | 18.6k | } |
86 | | |
87 | | // Create encoder if it doesn't exist. |
88 | 38.5k | if (*enc == nullptr) { |
89 | 19.9k | *width = pic.width; |
90 | 19.9k | *height = pic.height; |
91 | 19.9k | *enc = WebPAnimEncoderNew(*width, *height, &anim_config); |
92 | 19.9k | if (*enc == nullptr) { |
93 | 0 | return 0; |
94 | 0 | } |
95 | 19.9k | } |
96 | | |
97 | | // Create frame encoding config. |
98 | 38.5k | WebPConfig config = frame_config.webp_config; |
99 | | // Skip slow settings on big images, it's likely to timeout. |
100 | 38.5k | if (pic.width * pic.height > 32 * 32) { |
101 | 11.4k | config.method = (config.method > 4) ? 4 : config.method; |
102 | 11.4k | config.quality = (config.quality > 99.0f) ? 99.0f : config.quality; |
103 | 11.4k | config.alpha_quality = |
104 | 11.4k | (config.alpha_quality > 99) ? 99 : config.alpha_quality; |
105 | 11.4k | } |
106 | | |
107 | | // Encode. |
108 | 38.5k | if (!WebPAnimEncoderAdd(*enc, &pic, timestamp_ms, &config)) { |
109 | 0 | const WebPEncodingError error_code = pic.error_code; |
110 | 0 | WebPAnimEncoderDelete(*enc); |
111 | | // Tolerate failures when running under the nallocfuzz engine as |
112 | | // WebPAnimEncoderAdd() may fail due to memory allocation errors outside of |
113 | | // the encoder; in muxer functions that return booleans for instance. |
114 | 0 | if (error_code == VP8_ENC_ERROR_OUT_OF_MEMORY || |
115 | 0 | error_code == VP8_ENC_ERROR_BAD_WRITE || |
116 | 0 | getenv("NALLOC_FUZZ_VERSION") != nullptr) { |
117 | 0 | return 0; |
118 | 0 | } |
119 | 0 | fprintf(stderr, "WebPEncode failed. Error code: %d\n", error_code); |
120 | 0 | std::abort(); |
121 | 0 | } |
122 | | |
123 | 38.5k | return 1; |
124 | 38.5k | } |
125 | | |
126 | | void AnimEncoderTest(bool minimize_size, std::pair<int, int> kmin_kmax, |
127 | | bool allow_mixed, std::vector<FrameConfig> frame_configs, |
128 | 19.9k | int optimization_index) { |
129 | 19.9k | WebPAnimEncoder* enc = nullptr; |
130 | 19.9k | int width = 0, height = 0, timestamp_ms = 0; |
131 | 19.9k | uint32_t bit_pos = 0; |
132 | | |
133 | 19.9k | fuzz_utils::SetOptimization(default_VP8GetCPUInfo, optimization_index); |
134 | | |
135 | | // Extract a configuration from the packed bits. |
136 | 19.9k | WebPAnimEncoderOptions anim_config; |
137 | 19.9k | if (!WebPAnimEncoderOptionsInit(&anim_config)) { |
138 | 0 | fprintf(stderr, "WebPAnimEncoderOptionsInit failed.\n"); |
139 | 0 | std::abort(); |
140 | 0 | } |
141 | 19.9k | anim_config.minimize_size = minimize_size; |
142 | 19.9k | anim_config.kmin = kmin_kmax.first; |
143 | 19.9k | anim_config.kmax = kmin_kmax.second; |
144 | 19.9k | anim_config.allow_mixed = allow_mixed; |
145 | 19.9k | anim_config.verbose = 0; |
146 | | |
147 | | // For each frame. |
148 | 38.5k | for (FrameConfig& frame_config : frame_configs) { |
149 | 38.5k | if (!AddFrame(&enc, anim_config, &width, &height, timestamp_ms, |
150 | 38.5k | frame_config, &bit_pos)) { |
151 | 0 | return; |
152 | 0 | } |
153 | | |
154 | 38.5k | timestamp_ms += frame_config.timestamp; |
155 | 38.5k | } |
156 | | |
157 | | // Assemble. |
158 | 19.9k | if (!WebPAnimEncoderAdd(enc, nullptr, timestamp_ms, nullptr)) { |
159 | 0 | fprintf(stderr, "Last WebPAnimEncoderAdd failed: %s.\n", |
160 | 0 | WebPAnimEncoderGetError(enc)); |
161 | 0 | WebPAnimEncoderDelete(enc); |
162 | 0 | std::abort(); |
163 | 0 | } |
164 | 19.9k | WebPData webp_data; |
165 | 19.9k | WebPDataInit(&webp_data); |
166 | | // Tolerate failures when running under the nallocfuzz engine as allocations |
167 | | // during assembly may fail. |
168 | 19.9k | if (!WebPAnimEncoderAssemble(enc, &webp_data) && |
169 | 0 | getenv("NALLOC_FUZZ_VERSION") == nullptr) { |
170 | 0 | fprintf(stderr, "WebPAnimEncoderAssemble failed: %s.\n", |
171 | 0 | WebPAnimEncoderGetError(enc)); |
172 | 0 | WebPAnimEncoderDelete(enc); |
173 | 0 | WebPDataClear(&webp_data); |
174 | 0 | std::abort(); |
175 | 0 | } |
176 | | |
177 | 19.9k | WebPAnimEncoderDelete(enc); |
178 | 19.9k | WebPDataClear(&webp_data); |
179 | 19.9k | } |
180 | | |
181 | | } // namespace |
182 | | |
183 | | FUZZ_TEST(AnimIndexEncoder, AnimEncoderTest) |
184 | | .WithDomains( |
185 | | /*minimize_size=*/fuzztest::Arbitrary<bool>(), ArbitraryKMinKMax(), |
186 | | /*allow_mixed=*/fuzztest::Arbitrary<bool>(), |
187 | | fuzztest::VectorOf(fuzztest::StructOf<FrameConfig>( |
188 | | fuzztest::InRange<int>(0, 1), |
189 | | fuzztest::InRange<int>(0, 131073), |
190 | | fuzz_utils::ArbitraryWebPConfig(), |
191 | | fuzz_utils::ArbitraryCropOrScaleParams(), |
192 | | fuzz_utils::ArbitraryWebPPictureFromIndex())) |
193 | | .WithMinSize(1) |
194 | | .WithMaxSize(15), |
195 | | /*optimization_index=*/ |
196 | | fuzztest::InRange<uint32_t>(0, fuzz_utils::kMaxOptimizationIndex)); |
197 | | |
198 | | FUZZ_TEST(AnimArbitraryEncoder, AnimEncoderTest) |
199 | | .WithDomains( |
200 | | /*minimize_size=*/fuzztest::Arbitrary<bool>(), ArbitraryKMinKMax(), |
201 | | /*allow_mixed=*/fuzztest::Arbitrary<bool>(), |
202 | | fuzztest::VectorOf(fuzztest::StructOf<FrameConfig>( |
203 | | fuzztest::InRange<int>(0, 1), |
204 | | fuzztest::InRange<int>(0, 131073), |
205 | | fuzz_utils::ArbitraryWebPConfig(), |
206 | | fuzz_utils::ArbitraryCropOrScaleParams(), |
207 | | fuzz_utils::ArbitraryWebPPicture())) |
208 | | .WithMinSize(1) |
209 | | .WithMaxSize(15), |
210 | | /*optimization_index=*/ |
211 | | fuzztest::InRange<uint32_t>(0, fuzz_utils::kMaxOptimizationIndex)); |
212 | | |
213 | 0 | TEST(AnimIndexEncoder, Buganizer498967191) { |
214 | 0 | auto GetWebPPicture = [](int index, |
215 | 0 | bool use_argb) -> fuzz_utils::WebPPictureCpp { |
216 | 0 | WebPPicture pic = fuzz_utils::GetSourcePicture(index, use_argb); |
217 | 0 | return fuzz_utils::WebPPictureCpp( |
218 | 0 | use_argb, pic.colorspace, pic.width, pic.height, pic.y, pic.u, pic.v, |
219 | 0 | pic.y_stride, pic.uv_stride, pic.a, pic.a_stride, pic.argb, |
220 | 0 | pic.argb_stride, pic.memory_, pic.memory_argb_); |
221 | 0 | }; |
222 | 0 | AnimEncoderTest( |
223 | 0 | false, {0, 1}, true, |
224 | 0 | {FrameConfig{1, 0, WebPConfig{0, 0.f, 6, static_cast<WebPImageHint>(3), |
225 | 0 | 0, 0.f, 4, 0, |
226 | 0 | 38, 7, 1, 0, |
227 | 0 | 0, 0, 1, 10, |
228 | 0 | 1, 1, 1, 10, |
229 | 0 | 0, 0, 0, 20, |
230 | 0 | 1, 0, 0, 0, |
231 | 0 | 100}, |
232 | 0 | fuzz_utils::CropOrScaleParams{true, true, 6, 1, 2, 1}, |
233 | 0 | GetWebPPicture(0, true)}, |
234 | 0 | FrameConfig{0, 7248, |
235 | 0 | WebPConfig{1, 0.f, 1, static_cast<WebPImageHint>(3), |
236 | 0 | 0, 0.f, 1, 0, |
237 | 0 | 100, 0, 1, 0, |
238 | 0 | 1, 0, 0, 10, |
239 | 0 | 1, 1, 0, 10, |
240 | 0 | 0, 1, 0, 0, |
241 | 0 | 1, 0, 0, 0, |
242 | 0 | 100}, |
243 | 0 | fuzz_utils::CropOrScaleParams{true, true, 6, 8, 2, 1}, |
244 | 0 | GetWebPPicture(0, true)}}, |
245 | 0 | 1); |
246 | 0 | } |