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