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