/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 <stdio.h> |
18 | | #include <stdlib.h> |
19 | | |
20 | | #include "./fuzz_utils.h" |
21 | | #include "src/webp/encode.h" |
22 | | #include "src/webp/mux.h" |
23 | | |
24 | | namespace { |
25 | | |
26 | | const VP8CPUInfo default_VP8GetCPUInfo = VP8GetCPUInfo; |
27 | | |
28 | | int AddFrame(WebPAnimEncoder** const enc, |
29 | | const WebPAnimEncoderOptions& anim_config, int* const width, |
30 | | int* const height, int timestamp_ms, const uint8_t data[], |
31 | 47.3k | size_t size, uint32_t* const bit_pos) { |
32 | 47.3k | if (enc == nullptr || width == nullptr || height == nullptr) { |
33 | 0 | fprintf(stderr, "NULL parameters.\n"); |
34 | 0 | if (enc != nullptr) WebPAnimEncoderDelete(*enc); |
35 | 0 | abort(); |
36 | 0 | } |
37 | | |
38 | | // Init the source picture. |
39 | 47.3k | WebPPicture pic; |
40 | 47.3k | if (!WebPPictureInit(&pic)) { |
41 | 0 | fprintf(stderr, "WebPPictureInit failed.\n"); |
42 | 0 | WebPAnimEncoderDelete(*enc); |
43 | 0 | abort(); |
44 | 0 | } |
45 | 47.3k | pic.use_argb = Extract(1, data, size, bit_pos); |
46 | | |
47 | | // Read the source picture. |
48 | 47.3k | if (!ExtractSourcePicture(&pic, data, size, bit_pos)) { |
49 | 0 | const WebPEncodingError error_code = pic.error_code; |
50 | 0 | WebPAnimEncoderDelete(*enc); |
51 | 0 | WebPPictureFree(&pic); |
52 | 0 | if (error_code == VP8_ENC_ERROR_OUT_OF_MEMORY) return 0; |
53 | 0 | fprintf(stderr, "Can't read input image. Error code: %d\n", error_code); |
54 | 0 | abort(); |
55 | 0 | } |
56 | | |
57 | | // Crop and scale. |
58 | 47.3k | if (*enc == nullptr) { // First frame will set canvas width and height. |
59 | 4.73k | if (!ExtractAndCropOrScale(&pic, data, size, bit_pos)) { |
60 | 0 | const WebPEncodingError error_code = pic.error_code; |
61 | 0 | WebPPictureFree(&pic); |
62 | 0 | if (error_code == VP8_ENC_ERROR_OUT_OF_MEMORY) return 0; |
63 | 0 | fprintf(stderr, "ExtractAndCropOrScale failed. Error code: %d\n", |
64 | 0 | error_code); |
65 | 0 | abort(); |
66 | 0 | } |
67 | 42.6k | } else { // Other frames will be resized to the first frame's dimensions. |
68 | 42.6k | if (!WebPPictureRescale(&pic, *width, *height)) { |
69 | 0 | const WebPEncodingError error_code = pic.error_code; |
70 | 0 | WebPAnimEncoderDelete(*enc); |
71 | 0 | WebPPictureFree(&pic); |
72 | 0 | if (error_code == VP8_ENC_ERROR_OUT_OF_MEMORY) return 0; |
73 | 0 | fprintf(stderr, |
74 | 0 | "WebPPictureRescale failed. Size: %d,%d. Error code: %d\n", |
75 | 0 | *width, *height, error_code); |
76 | 0 | abort(); |
77 | 0 | } |
78 | 42.6k | } |
79 | | |
80 | | // Create encoder if it doesn't exist. |
81 | 47.3k | if (*enc == nullptr) { |
82 | 4.73k | *width = pic.width; |
83 | 4.73k | *height = pic.height; |
84 | 4.73k | *enc = WebPAnimEncoderNew(*width, *height, &anim_config); |
85 | 4.73k | if (*enc == nullptr) { |
86 | 0 | WebPPictureFree(&pic); |
87 | 0 | return 0; |
88 | 0 | } |
89 | 4.73k | } |
90 | | |
91 | | // Create frame encoding config. |
92 | 47.3k | WebPConfig config; |
93 | 47.3k | if (!ExtractWebPConfig(&config, data, size, bit_pos)) { |
94 | 0 | fprintf(stderr, "ExtractWebPConfig failed.\n"); |
95 | 0 | WebPAnimEncoderDelete(*enc); |
96 | 0 | WebPPictureFree(&pic); |
97 | 0 | abort(); |
98 | 0 | } |
99 | | // Skip slow settings on big images, it's likely to timeout. |
100 | 47.3k | if (pic.width * pic.height > 32 * 32) { |
101 | 22.5k | config.method = (config.method > 4) ? 4 : config.method; |
102 | 22.5k | config.quality = (config.quality > 99.0f) ? 99.0f : config.quality; |
103 | 22.5k | config.alpha_quality = |
104 | 22.5k | (config.alpha_quality > 99) ? 99 : config.alpha_quality; |
105 | 22.5k | } |
106 | | |
107 | | // Encode. |
108 | 47.3k | if (!WebPAnimEncoderAdd(*enc, &pic, timestamp_ms, &config)) { |
109 | 0 | const WebPEncodingError error_code = pic.error_code; |
110 | 0 | WebPAnimEncoderDelete(*enc); |
111 | 0 | WebPPictureFree(&pic); |
112 | | // Tolerate failures when running under the nallocfuzz engine as |
113 | | // WebPAnimEncoderAdd() may fail due to memory allocation errors outside of |
114 | | // the encoder; in muxer functions that return booleans for instance. |
115 | 0 | if (error_code == VP8_ENC_ERROR_OUT_OF_MEMORY || |
116 | 0 | error_code == VP8_ENC_ERROR_BAD_WRITE || |
117 | 0 | getenv("NALLOC_FUZZ_VERSION") != nullptr) { |
118 | 0 | return 0; |
119 | 0 | } |
120 | 0 | fprintf(stderr, "WebPEncode failed. Error code: %d\n", error_code); |
121 | 0 | abort(); |
122 | 0 | } |
123 | | |
124 | 47.3k | WebPPictureFree(&pic); |
125 | 47.3k | return 1; |
126 | 47.3k | } |
127 | | |
128 | | } // namespace |
129 | | |
130 | 4.73k | extern "C" int LLVMFuzzerTestOneInput(const uint8_t* const data, size_t size) { |
131 | 4.73k | WebPAnimEncoder* enc = nullptr; |
132 | 4.73k | int width = 0, height = 0, timestamp_ms = 0; |
133 | 4.73k | uint32_t bit_pos = 0; |
134 | | |
135 | 4.73k | ExtractAndDisableOptimizations(default_VP8GetCPUInfo, data, size, &bit_pos); |
136 | | |
137 | | // Extract a configuration from the packed bits. |
138 | 4.73k | WebPAnimEncoderOptions anim_config; |
139 | 4.73k | if (!WebPAnimEncoderOptionsInit(&anim_config)) { |
140 | 0 | fprintf(stderr, "WebPAnimEncoderOptionsInit failed.\n"); |
141 | 0 | abort(); |
142 | 0 | } |
143 | 4.73k | anim_config.minimize_size = Extract(1, data, size, &bit_pos); |
144 | 4.73k | anim_config.kmax = Extract(15, data, size, &bit_pos); |
145 | 4.73k | const int min_kmin = (anim_config.kmax > 1) ? (anim_config.kmax / 2) : 0; |
146 | 4.73k | const int max_kmin = (anim_config.kmax > 1) ? (anim_config.kmax - 1) : 0; |
147 | 4.73k | anim_config.kmin = |
148 | 4.73k | min_kmin + Extract((uint32_t)(max_kmin - min_kmin), data, size, &bit_pos); |
149 | 4.73k | anim_config.allow_mixed = Extract(1, data, size, &bit_pos); |
150 | 4.73k | anim_config.verbose = 0; |
151 | | |
152 | 4.73k | const int nb_frames = 1 + Extract(15, data, size, &bit_pos); |
153 | | |
154 | | // For each frame. |
155 | 52.0k | for (int i = 0; i < nb_frames; ++i) { |
156 | 47.3k | if (!AddFrame(&enc, anim_config, &width, &height, timestamp_ms, data, size, |
157 | 47.3k | &bit_pos)) { |
158 | 0 | return 0; |
159 | 0 | } |
160 | | |
161 | 47.3k | timestamp_ms += (1 << (2 + Extract(15, data, size, &bit_pos))) + |
162 | 47.3k | Extract(1, data, size, &bit_pos); // [1..131073], arbitrary |
163 | 47.3k | } |
164 | | |
165 | | // Assemble. |
166 | 4.73k | if (!WebPAnimEncoderAdd(enc, nullptr, timestamp_ms, nullptr)) { |
167 | 0 | fprintf(stderr, "Last WebPAnimEncoderAdd failed: %s.\n", |
168 | 0 | WebPAnimEncoderGetError(enc)); |
169 | 0 | WebPAnimEncoderDelete(enc); |
170 | 0 | abort(); |
171 | 0 | } |
172 | 4.73k | WebPData webp_data; |
173 | 4.73k | WebPDataInit(&webp_data); |
174 | | // Tolerate failures when running under the nallocfuzz engine as allocations |
175 | | // during assembly may fail. |
176 | 4.73k | if (!WebPAnimEncoderAssemble(enc, &webp_data) && |
177 | 4.73k | getenv("NALLOC_FUZZ_VERSION") == nullptr) { |
178 | 0 | fprintf(stderr, "WebPAnimEncoderAssemble failed: %s.\n", |
179 | 0 | WebPAnimEncoderGetError(enc)); |
180 | 0 | WebPAnimEncoderDelete(enc); |
181 | 0 | WebPDataClear(&webp_data); |
182 | 0 | abort(); |
183 | 0 | } |
184 | | |
185 | 4.73k | WebPAnimEncoderDelete(enc); |
186 | 4.73k | WebPDataClear(&webp_data); |
187 | 4.73k | return 0; |
188 | 4.73k | } |