| // Copyright 2018 Google Inc. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| // |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include "fuzzing/fuzz.h" |
| #include "src/webp/encode.h" |
| #include "src/webp/mux.h" |
| |
| namespace { |
| |
| const VP8CPUInfo default_VP8GetCPUInfo = VP8GetCPUInfo; |
| |
| int AddFrame(WebPAnimEncoder** const enc, |
| const WebPAnimEncoderOptions& anim_config, |
| int* const width, |
| int* const height, |
| int timestamp_ms, |
| const uint8_t data[], |
| size_t size, |
| uint32_t* const bit_pos) { |
| if (enc == nullptr || width == nullptr || height == nullptr) { |
| fprintf(stderr, "NULL parameters.\n"); |
| if (enc != nullptr) |
| WebPAnimEncoderDelete(*enc); |
| abort(); |
| } |
| |
| // Init the source picture. |
| WebPPicture pic; |
| if (!WebPPictureInit(&pic)) { |
| fprintf(stderr, "WebPPictureInit failed.\n"); |
| WebPAnimEncoderDelete(*enc); |
| abort(); |
| } |
| pic.use_argb = Extract(1, data, size, bit_pos); |
| |
| // Read the source picture. |
| if (!ExtractSourcePicture(&pic, data, size, bit_pos)) { |
| fprintf(stderr, "Can't read input image.\n"); |
| WebPPictureFree(&pic); |
| abort(); |
| } |
| |
| // Crop and scale. |
| if (*enc == nullptr) { // First frame will set canvas width and height. |
| if (!ExtractAndCropOrScale(&pic, data, size, bit_pos)) { |
| fprintf(stderr, "ExtractAndCropOrScale failed."); |
| WebPPictureFree(&pic); |
| abort(); |
| } |
| } else { // Other frames will be resized to the first frame's dimensions. |
| if (!WebPPictureRescale(&pic, *width, *height)) { |
| fprintf(stderr, "WebPPictureRescale failed. Size: %d,%d\n", *width, |
| *height); |
| WebPAnimEncoderDelete(*enc); |
| WebPPictureFree(&pic); |
| abort(); |
| } |
| } |
| |
| // Create encoder if it doesn't exist. |
| if (*enc == nullptr) { |
| *width = pic.width; |
| *height = pic.height; |
| *enc = WebPAnimEncoderNew(*width, *height, &anim_config); |
| if (*enc == nullptr) { |
| fprintf(stderr, "WebPAnimEncoderNew failed.\n"); |
| WebPPictureFree(&pic); |
| abort(); |
| } |
| } |
| |
| // Create frame encoding config. |
| WebPConfig config; |
| if (!ExtractWebPConfig(&config, data, size, bit_pos)) { |
| fprintf(stderr, "ExtractWebPConfig failed.\n"); |
| WebPAnimEncoderDelete(*enc); |
| WebPPictureFree(&pic); |
| abort(); |
| } |
| // Skip slow settings on big images, it's likely to timeout. |
| if (pic.width * pic.height > 32 * 32) { |
| config.method = (config.method > 4) ? 4 : config.method; |
| config.quality = (config.quality > 99.0f) ? 99.0f : config.quality; |
| config.alpha_quality = |
| (config.alpha_quality > 99) ? 99 : config.alpha_quality; |
| } |
| |
| // Encode. |
| if (!WebPAnimEncoderAdd(*enc, &pic, timestamp_ms, &config)) { |
| fprintf(stderr, "WebPEncode failed. Error code: %d\n", pic.error_code); |
| WebPAnimEncoderDelete(*enc); |
| WebPPictureFree(&pic); |
| abort(); |
| } |
| |
| WebPPictureFree(&pic); |
| return 1; |
| } |
| |
| } // namespace |
| |
| extern "C" int LLVMFuzzerTestOneInput(const uint8_t* const data, size_t size) { |
| WebPAnimEncoder* enc = nullptr; |
| int width = 0, height = 0, timestamp_ms = 0; |
| uint32_t bit_pos = 0; |
| |
| ExtractAndDisableOptimizations(default_VP8GetCPUInfo, data, size, &bit_pos); |
| |
| // Extract a configuration from the packed bits. |
| WebPAnimEncoderOptions anim_config; |
| if (!WebPAnimEncoderOptionsInit(&anim_config)) { |
| fprintf(stderr, "WebPAnimEncoderOptionsInit failed.\n"); |
| abort(); |
| } |
| anim_config.minimize_size = Extract(1, data, size, &bit_pos); |
| anim_config.kmax = Extract(15, data, size, &bit_pos); |
| const int min_kmin = (anim_config.kmax > 1) ? (anim_config.kmax / 2) : 0; |
| const int max_kmin = (anim_config.kmax > 1) ? (anim_config.kmax - 1) : 0; |
| anim_config.kmin = |
| min_kmin + Extract((uint32_t)(max_kmin - min_kmin), data, size, &bit_pos); |
| anim_config.allow_mixed = Extract(1, data, size, &bit_pos); |
| anim_config.verbose = 0; |
| |
| const int nb_frames = 1 + Extract(15, data, size, &bit_pos); |
| |
| // For each frame. |
| for (int i = 0; i < nb_frames; ++i) { |
| if (!AddFrame(&enc, anim_config, &width, &height, timestamp_ms, data, size, |
| &bit_pos)) { |
| return 0; |
| } |
| |
| timestamp_ms += (1 << (2 + Extract(15, data, size, &bit_pos))) + |
| Extract(1, data, size, &bit_pos); // [1..131073], arbitrary |
| } |
| |
| // Assemble. |
| if (!WebPAnimEncoderAdd(enc, nullptr, timestamp_ms, nullptr)) { |
| fprintf(stderr, "Last WebPAnimEncoderAdd failed."); |
| WebPAnimEncoderDelete(enc); |
| abort(); |
| } |
| WebPData webp_data; |
| WebPDataInit(&webp_data); |
| if (!WebPAnimEncoderAssemble(enc, &webp_data)) { |
| fprintf(stderr, "WebPAnimEncoderAssemble failed."); |
| WebPAnimEncoderDelete(enc); |
| WebPDataClear(&webp_data); |
| abort(); |
| } |
| |
| WebPAnimEncoderDelete(enc); |
| WebPDataClear(&webp_data); |
| return 0; |
| } |