diff options
Diffstat (limited to 'projects/libwebp/fuzz_webp_animencoder.cc')
-rw-r--r-- | projects/libwebp/fuzz_webp_animencoder.cc | 165 |
1 files changed, 165 insertions, 0 deletions
diff --git a/projects/libwebp/fuzz_webp_animencoder.cc b/projects/libwebp/fuzz_webp_animencoder.cc new file mode 100644 index 00000000..f5cb2d13 --- /dev/null +++ b/projects/libwebp/fuzz_webp_animencoder.cc @@ -0,0 +1,165 @@ +// 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 "fuzz.h" +#include "webp/encode.h" +#include "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; +} |