From 1ad8633819e2631ff6f1c11e28446343991f7fe0 Mon Sep 17 00:00:00 2001 From: YannisGuyon <7632072+YannisGuyon@users.noreply.github.com> Date: Wed, 7 Nov 2018 20:17:08 +0100 Subject: [PATCH] webp_animencoder: Add target to fuzz animation encoding (#1935) * Add new fuzz target for encoding and misc - Add fuzz_webp_enc_dec and adapt Dockerfile, build.sh - Lint existing targets - Add license headers - Increase fuzz.dict * webp_enc_dec: Convert input images to inline C arrays Local files are not available on oss-fuzz servers. * webp_enc_dec: Fix timeout by skipping crusher The target fuzz_webp_enc_dec with msan crashes (timeout) on a 128*128px image encoding with max compression (crusher). Reduce crusher encoding to 16*16px and below. Bug report 10423 * webp_enc_dec: Replace cruncher by lossy alpha encoding The target fuzz_webp_enc_dec with msan crashes (timeout) during encoding with max compression (cruncher). Reduce alpha cruncher encoding to 16*16px and below. Bug report 10634 * webp_enc_dec: Clamp slow parameters for big images The target fuzz_webp_enc_dec with ubsan crashes (timeout) during encoding with heavy compression. The cause can not be easily removed without reducing performance. Clamp compression parameters for images bigger than 16*16. Bug report 10700 * webp_enc_dec: Limit alpha_quality to 99 when method is 6 The target fuzz_webp_enc_dec with asan crashes (timeout) during encoding with heavy alpha compression. Clamp alpha compression parameters for images bigger than 16*16. Bug report 10838 * webp_animencoder: Add target to fuzz animation encoding Add fuzz_webp_animencoder.cc and modify Dockerfile, build.sh accordingly. The thresholds for input size and encoding parameters are low to prevent timeouts. Some functions used by fuzz_webp_animencoder and fuzz_webp_enc_dec have been moved to fuzz.h. --- projects/libwebp/Dockerfile | 1 + projects/libwebp/build.sh | 16 +- projects/libwebp/fuzz.h | 156 +++++++++++++++++++ projects/libwebp/fuzz_webp_animencoder.cc | 165 +++++++++++++++++++++ projects/libwebp/fuzz_webp_enc_dec.cc | 173 +++------------------- 5 files changed, 358 insertions(+), 153 deletions(-) create mode 100644 projects/libwebp/fuzz_webp_animencoder.cc diff --git a/projects/libwebp/Dockerfile b/projects/libwebp/Dockerfile index b00a937e0..081d36344 100644 --- a/projects/libwebp/Dockerfile +++ b/projects/libwebp/Dockerfile @@ -27,6 +27,7 @@ COPY img_alpha.h img_grid.h img_peak.h $SRC/ COPY fuzz_simple_api.c $SRC/ COPY fuzz_advanced_api.c $SRC/ COPY fuzz_animation_api.c $SRC/ +COPY fuzz_webp_animencoder.cc $SRC/ COPY fuzz_demux_api.c $SRC/ COPY fuzz_webp_enc_dec.cc $SRC/ WORKDIR libwebp diff --git a/projects/libwebp/build.sh b/projects/libwebp/build.sh index d432741ed..612f10ef8 100644 --- a/projects/libwebp/build.sh +++ b/projects/libwebp/build.sh @@ -35,7 +35,7 @@ find $SRC/libwebp-test-data -type f -size -32k -iname "*.webp" \ -exec zip -qju fuzz_seed_corpus.zip "{}" \; # Simple Decoding API -$CC $CFLAGS -Isrc -c $SRC/fuzz_simple_api.c +$CC $CFLAGS -Isrc -I. -c $SRC/fuzz_simple_api.c $CXX $CXXFLAGS -lFuzzingEngine \ fuzz_simple_api.o -o $OUT/fuzz_simple_api \ src/.libs/libwebp.a @@ -43,7 +43,7 @@ cp fuzz_seed_corpus.zip $OUT/fuzz_simple_api_seed_corpus.zip cp $SRC/fuzz.dict $OUT/fuzz_simple_api.dict # Advanced Decoding API -$CC $CFLAGS -Isrc -c $SRC/fuzz_advanced_api.c +$CC $CFLAGS -Isrc -I. -c $SRC/fuzz_advanced_api.c $CXX $CXXFLAGS -lFuzzingEngine \ fuzz_advanced_api.o -o $OUT/fuzz_advanced_api \ src/.libs/libwebp.a @@ -51,7 +51,7 @@ cp fuzz_seed_corpus.zip $OUT/fuzz_advanced_api_seed_corpus.zip cp $SRC/fuzz.dict $OUT/fuzz_advanced_api.dict # Animation Decoding API -$CC $CFLAGS -Isrc -c $SRC/fuzz_animation_api.c +$CC $CFLAGS -Isrc -I. -c $SRC/fuzz_animation_api.c $CXX $CXXFLAGS -lFuzzingEngine \ fuzz_animation_api.o -o $OUT/fuzz_animation_api \ src/demux/.libs/libwebpdemux.a \ @@ -59,8 +59,16 @@ $CXX $CXXFLAGS -lFuzzingEngine \ cp fuzz_seed_corpus.zip $OUT/fuzz_animation_api_seed_corpus.zip cp $SRC/fuzz.dict $OUT/fuzz_animation_api.dict +# Animation Encoding API +$CC $CFLAGS -Isrc -I. -c $SRC/fuzz_webp_animencoder.cc +$CXX $CXXFLAGS -lFuzzingEngine \ + fuzz_webp_animencoder.o -o $OUT/fuzz_webp_animencoder \ + src/mux/.libs/libwebpmux.a \ + src/.libs/libwebp.a +cp fuzz_seed_corpus.zip $OUT/fuzz_webp_animencoder_seed_corpus.zip + # (De)mux API -$CC $CFLAGS -Isrc -c $SRC/fuzz_demux_api.c +$CC $CFLAGS -Isrc -I. -c $SRC/fuzz_demux_api.c $CXX $CXXFLAGS -lFuzzingEngine \ fuzz_demux_api.o -o $OUT/fuzz_demux_api \ src/demux/.libs/libwebpdemux.a src/mux/.libs/libwebpmux.a \ diff --git a/projects/libwebp/fuzz.h b/projects/libwebp/fuzz.h index d3e984b0d..408211f36 100644 --- a/projects/libwebp/fuzz.h +++ b/projects/libwebp/fuzz.h @@ -17,6 +17,13 @@ #include #include +#include "dsp/dsp.h" +#include "img_alpha.h" +#include "img_grid.h" +#include "img_peak.h" +#include "webp/encode.h" + +//------------------------------------------------------------------------------ // Arbitrary limits to prevent OOM, timeout, or slow execution. // // The decoded image size, and for animations additionally the canvas size. @@ -32,3 +39,152 @@ uint8_t FuzzHash(const uint8_t* const data, size_t size) { for (size_t i = 0; i < size; i += incr) value += data[i]; return value; } + +//------------------------------------------------------------------------------ +// Extract an integer in [0, max_value]. + +static uint32_t Extract(uint32_t max_value, const uint8_t data[], size_t size, + uint32_t* const bit_pos) { + uint32_t v = 0; + int range = 1; + while (*bit_pos < 8 * size && range <= max_value) { + const uint8_t mask = 1u << (*bit_pos & 7); + v = (v << 1) | !!(data[*bit_pos >> 3] & mask); + range <<= 1; + ++*bit_pos; + } + return v % (max_value + 1); +} + +//------------------------------------------------------------------------------ +// Some functions to override VP8GetCPUInfo and disable some optimizations. + +static VP8CPUInfo GetCPUInfo; + +static int GetCPUInfoNoSSE41(CPUFeature feature) { + if (feature == kSSE4_1 || feature == kAVX) return 0; + return GetCPUInfo(feature); +} + +static int GetCPUInfoNoAVX(CPUFeature feature) { + if (feature == kAVX) return 0; + return GetCPUInfo(feature); +} + +static int GetCPUInfoForceSlowSSSE3(CPUFeature feature) { + if (feature == kSlowSSSE3 && GetCPUInfo(kSSE3)) { + return 1; // we have SSE3 -> force SlowSSSE3 + } + return GetCPUInfo(feature); +} + +static int GetCPUInfoOnlyC(CPUFeature feature) { return 0; } + +static void ExtractAndDisableOptimizations(VP8CPUInfo default_VP8GetCPUInfo, + const uint8_t data[], size_t size, + uint32_t* const bit_pos) { + GetCPUInfo = default_VP8GetCPUInfo; + const VP8CPUInfo kVP8CPUInfos[5] = {GetCPUInfoOnlyC, GetCPUInfoForceSlowSSSE3, + GetCPUInfoNoSSE41, GetCPUInfoNoAVX, + GetCPUInfo}; + int VP8GetCPUInfo_index = Extract(4, data, size, bit_pos); + VP8GetCPUInfo = kVP8CPUInfos[VP8GetCPUInfo_index]; +} + +//------------------------------------------------------------------------------ + +static int ExtractWebPConfig(WebPConfig* const config, const uint8_t data[], + size_t size, uint32_t* const bit_pos) { + if (config == NULL || !WebPConfigInit(config)) return 0; + config->lossless = Extract(1, data, size, bit_pos); + config->quality = Extract(100, data, size, bit_pos); + config->method = Extract(6, data, size, bit_pos); + config->image_hint = + (WebPImageHint)Extract(WEBP_HINT_LAST - 1, data, size, bit_pos); + config->segments = 1 + Extract(3, data, size, bit_pos); + config->sns_strength = Extract(100, data, size, bit_pos); + config->filter_strength = Extract(100, data, size, bit_pos); + config->filter_sharpness = Extract(7, data, size, bit_pos); + config->filter_type = Extract(1, data, size, bit_pos); + config->autofilter = Extract(1, data, size, bit_pos); + config->alpha_compression = Extract(1, data, size, bit_pos); + config->alpha_filtering = Extract(2, data, size, bit_pos); + config->alpha_quality = Extract(100, data, size, bit_pos); + config->pass = 1 + Extract(9, data, size, bit_pos); + config->show_compressed = 1; + config->preprocessing = Extract(2, data, size, bit_pos); + config->partitions = Extract(3, data, size, bit_pos); + config->partition_limit = 10 * Extract(10, data, size, bit_pos); + config->emulate_jpeg_size = Extract(1, data, size, bit_pos); + config->thread_level = Extract(1, data, size, bit_pos); + config->low_memory = Extract(1, data, size, bit_pos); + config->near_lossless = 20 * Extract(5, data, size, bit_pos); + config->exact = Extract(1, data, size, bit_pos); + config->use_delta_palette = Extract(1, data, size, bit_pos); + config->use_sharp_yuv = Extract(1, data, size, bit_pos); + return WebPValidateConfig(config); +} + +//------------------------------------------------------------------------------ + +static int ExtractSourcePicture(WebPPicture* const pic, + const uint8_t data[], size_t size, + uint32_t* const bit_pos) { + if (pic == NULL) return 0; + + // Pick a source picture. + const uint8_t* kImagesData[] = { + kImgAlphaData, + kImgGridData, + kImgPeakData + }; + const int kImagesWidth[] = { + kImgAlphaWidth, + kImgGridWidth, + kImgPeakWidth + }; + const int kImagesHeight[] = { + kImgAlphaHeight, + kImgGridHeight, + kImgPeakHeight + }; + const size_t kNbImages = sizeof(kImagesData) / sizeof(kImagesData[0]); + const size_t image_index = Extract(kNbImages - 1, data, size, bit_pos); + const uint8_t* const image_data = kImagesData[image_index]; + pic->width = kImagesWidth[image_index]; + pic->height = kImagesHeight[image_index]; + pic->argb_stride = pic->width * 4 * sizeof(uint8_t); + + // Read the bytes. + return WebPPictureImportRGBA(pic, image_data, pic->argb_stride); +} + +//------------------------------------------------------------------------------ + +static int max(int a, int b) { return ((a < b) ? b : a); } + +static int ExtractAndCropOrScale(WebPPicture* const pic, const uint8_t data[], + size_t size, uint32_t* const bit_pos) { + if (pic == NULL) return 0; + const int alter_input = Extract(1, data, size, bit_pos); + const int crop_or_scale = Extract(1, data, size, bit_pos); + const int width_ratio = 1 + Extract(7, data, size, bit_pos); + const int height_ratio = 1 + Extract(7, data, size, bit_pos); + if (alter_input) { + if (crop_or_scale) { + const uint32_t left_ratio = 1 + Extract(7, data, size, bit_pos); + const uint32_t top_ratio = 1 + Extract(7, data, size, bit_pos); + const int cropped_width = max(1, pic->width / width_ratio); + const int cropped_height = max(1, pic->height / height_ratio); + const int cropped_left = (pic->width - cropped_width) / left_ratio; + const int cropped_top = (pic->height - cropped_height) / top_ratio; + return WebPPictureCrop(pic, cropped_left, cropped_top, cropped_width, + cropped_height); + } else { + const int scaled_width = 1 + (pic->width * width_ratio) / 8; + const int scaled_height = 1 + (pic->height * height_ratio) / 8; + return WebPPictureRescale(pic, scaled_width, scaled_height); + } + } + return 1; +} diff --git a/projects/libwebp/fuzz_webp_animencoder.cc b/projects/libwebp/fuzz_webp_animencoder.cc new file mode 100644 index 000000000..f5cb2d13f --- /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 +#include +#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; +} diff --git a/projects/libwebp/fuzz_webp_enc_dec.cc b/projects/libwebp/fuzz_webp_enc_dec.cc index c1830e5d1..5e3ef4276 100644 --- a/projects/libwebp/fuzz_webp_enc_dec.cc +++ b/projects/libwebp/fuzz_webp_enc_dec.cc @@ -16,98 +16,20 @@ #include #include +#include "fuzz.h" #include "webp/encode.h" #include "webp/decode.h" -#include "img_alpha.h" -#include "img_grid.h" -#include "img_peak.h" -#include "dsp/dsp.h" namespace { -const VP8CPUInfo LibGetCPUInfo = VP8GetCPUInfo; +const VP8CPUInfo default_VP8GetCPUInfo = VP8GetCPUInfo; -int GetCPUInfoNoSSE41(CPUFeature feature) { - if (feature == kSSE4_1 || feature == kAVX) return 0; - return LibGetCPUInfo(feature); -} - -int GetCPUInfoNoAVX(CPUFeature feature) { - if (feature == kAVX) return 0; - return LibGetCPUInfo(feature); -} - -int GetCPUInfoForceSlowSSSE3(CPUFeature feature) { - if (feature == kSlowSSSE3 && LibGetCPUInfo(kSSE3)) { - return 1; // we have SSE3 -> force SlowSSSE3 - } - return LibGetCPUInfo(feature); -} - -int GetCPUInfoOnlyC(CPUFeature feature) { - return false; -} - -const VP8CPUInfo kVP8CPUInfos[5] = { - GetCPUInfoOnlyC, GetCPUInfoForceSlowSSSE3, - GetCPUInfoNoSSE41, GetCPUInfoNoAVX, LibGetCPUInfo -}; - -static uint32_t Extract(uint32_t max, const uint8_t data[], size_t size, - uint32_t* const bit_pos) { - uint32_t v = 0; - int range = 1; - while (*bit_pos < 8 * size && range <= max) { - const uint8_t mask = 1u << (*bit_pos & 7); - v = (v << 1) | !!(data[*bit_pos >> 3] & mask); - range <<= 1; - ++*bit_pos; - } - return v % (max + 1); -} - -static int max(int a, int b) { return ((a < b) ? b : a); } - -} // namespace +} // namespace extern "C" int LLVMFuzzerTestOneInput(const uint8_t* const data, size_t size) { - // Extract a configuration from the packed bits. - WebPConfig config; - if (!WebPConfigInit(&config)) { - fprintf(stderr, "WebPConfigInit failed.\n"); - abort(); - } uint32_t bit_pos = 0; - config.lossless = Extract(1, data, size, &bit_pos); - config.quality = Extract(100, data, size, &bit_pos); - config.method = Extract(6, data, size, &bit_pos); - config.image_hint = - (WebPImageHint)Extract(WEBP_HINT_LAST - 1, data, size, &bit_pos); - config.segments = 1 + Extract(3, data, size, &bit_pos); - config.sns_strength = Extract(100, data, size, &bit_pos); - config.filter_strength = Extract(100, data, size, &bit_pos); - config.filter_sharpness = Extract(7, data, size, &bit_pos); - config.filter_type = Extract(1, data, size, &bit_pos); - config.autofilter = Extract(1, data, size, &bit_pos); - config.alpha_compression = Extract(1, data, size, &bit_pos); - config.alpha_filtering = Extract(2, data, size, &bit_pos); - config.alpha_quality = Extract(100, data, size, &bit_pos); - config.pass = 1 + Extract(9, data, size, &bit_pos); - config.show_compressed = 1; - config.preprocessing = Extract(2, data, size, &bit_pos); - config.partitions = Extract(3, data, size, &bit_pos); - config.partition_limit = 10 * Extract(10, data, size, &bit_pos); - config.emulate_jpeg_size = Extract(1, data, size, &bit_pos); - config.thread_level = Extract(1, data, size, &bit_pos); - config.low_memory = Extract(1, data, size, &bit_pos); - config.near_lossless = 20 * Extract(5, data, size, &bit_pos); - config.exact = Extract(1, data, size, &bit_pos); - config.use_delta_palette = Extract(1, data, size, &bit_pos); - config.use_sharp_yuv = Extract(1, data, size, &bit_pos); - if (!WebPValidateConfig(&config)) { - fprintf(stderr, "WebPValidateConfig failed.\n"); - abort(); - } + + ExtractAndDisableOptimizations(default_VP8GetCPUInfo, data, size, &bit_pos); // Init the source picture. WebPPicture pic; @@ -117,79 +39,35 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* const data, size_t size) { } pic.use_argb = Extract(1, data, size, &bit_pos); - VP8GetCPUInfo = kVP8CPUInfos[Extract(4, data, size, &bit_pos)]; - - // Pick a source picture. - const uint8_t* kImagesData[] = { - kImgAlphaData, - kImgGridData, - kImgPeakData - }; - const int kImagesWidth[] = { - kImgAlphaWidth, - kImgGridWidth, - kImgPeakWidth - }; - const int kImagesHeight[] = { - kImgAlphaHeight, - kImgGridHeight, - kImgPeakHeight - }; - const size_t kNbImages = sizeof(kImagesData) / sizeof(kImagesData[0]); - const size_t image_index = Extract(kNbImages - 1, data, size, &bit_pos); - const uint8_t* const image_data = kImagesData[image_index]; - pic.width = kImagesWidth[image_index]; - pic.height = kImagesHeight[image_index]; - pic.argb_stride = pic.width * 4 * sizeof(uint8_t); - - // Read the bytes. - if (!WebPPictureImportRGBA(&pic, image_data, pic.argb_stride)) { - fprintf(stderr, "Can't read input image: %zu\n", image_index); + // 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. - const bool alter_input = Extract(1, data, size, &bit_pos) != 0; - const bool crop_or_scale = Extract(1, data, size, &bit_pos) != 0; - const int width_ratio = 1 + Extract(7, data, size, &bit_pos); - const int height_ratio = 1 + Extract(7, data, size, &bit_pos); - if (alter_input) { - if (crop_or_scale) { - const uint32_t left_ratio = 1 + Extract(7, data, size, &bit_pos); - const uint32_t top_ratio = 1 + Extract(7, data, size, &bit_pos); - const int cropped_width = max(1, pic.width / width_ratio); - const int cropped_height = max(1, pic.height / height_ratio); - const int cropped_left = (pic.width - cropped_width) / left_ratio; - const int cropped_top = (pic.height - cropped_height) / top_ratio; - if (!WebPPictureCrop(&pic, cropped_left, cropped_top, cropped_width, - cropped_height)) { - fprintf(stderr, "WebPPictureCrop failed. Parameters: %d,%d,%d,%d\n", - cropped_left, cropped_top, cropped_width, cropped_height); - WebPPictureFree(&pic); - abort(); - } - } else { - const int scaled_width = 1 + pic.width * width_ratio / 4; - const int scaled_height = 1 + pic.height * height_ratio / 4; - if (!WebPPictureRescale(&pic, scaled_width, scaled_height)) { - fprintf(stderr, "WebPPictureRescale failed. Parameters: %d,%d\n", - scaled_width, scaled_height); - WebPPictureFree(&pic); - abort(); - } - } + if (!ExtractAndCropOrScale(&pic, data, size, &bit_pos)) { + fprintf(stderr, "ExtractAndCropOrScale failed."); + WebPPictureFree(&pic); + abort(); } + // Extract a configuration from the packed bits. + WebPConfig config; + if (!ExtractWebPConfig(&config, data, size, &bit_pos)) { + fprintf(stderr, "ExtractWebPConfig failed.\n"); + abort(); + } // Skip slow settings on big images, it's likely to timeout. - if (pic.width * pic.height > 16 * 16) { + if (pic.width * pic.height > 32 * 32) { if (config.lossless) { - if (config.quality >= 99.0f && config.method >= 5) { + if (config.quality > 99.0f && config.method >= 5) { config.quality = 99.0f; config.method = 5; } } else { - if (config.quality >= 99.0f && config.method == 6) { + if (config.quality > 99.0f && config.method == 6) { config.quality = 99.0f; } } @@ -204,8 +82,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* const data, size_t size) { pic.writer = WebPMemoryWrite; pic.custom_ptr = &memory_writer; if (!WebPEncode(&config, &pic)) { - fprintf(stderr, "WebPEncode failed. Error code: %d\nFile: %zu\n", - pic.error_code, image_index); + fprintf(stderr, "WebPEncode failed. Error code: %d\n", pic.error_code); WebPMemoryWriterClear(&memory_writer); WebPPictureFree(&pic); abort(); @@ -217,7 +94,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* const data, size_t size) { const size_t out_size = memory_writer.size; uint8_t* const rgba = WebPDecodeBGRA(out_data, out_size, &w, &h); if (rgba == nullptr || w != pic.width || h != pic.height) { - fprintf(stderr, "WebPDecodeBGRA failed.\nFile: %zu\n", image_index); + fprintf(stderr, "WebPDecodeBGRA failed.\n"); WebPFree(rgba); WebPMemoryWriterClear(&memory_writer); WebPPictureFree(&pic); @@ -239,9 +116,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* const data, size_t size) { } } if (v1 != v2) { - fprintf(stderr, - "Lossless compression failed pixel-exactness.\nFile: %zu\n", - image_index); + fprintf(stderr, "Lossless compression failed pixel-exactness.\n"); WebPFree(rgba); WebPMemoryWriterClear(&memory_writer); WebPPictureFree(&pic);