go-libwebp

Experimental translation from libwebp to Go source.
Log | Files | Refs | README | LICENSE

animencoder_fuzzer.cc (7238B)


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