diff --git a/tests/fuzzer/animencoder_fuzzer.cc b/tests/fuzzer/animencoder_fuzzer.cc index 6288e030..9be0b1dd 100644 --- a/tests/fuzzer/animencoder_fuzzer.cc +++ b/tests/fuzzer/animencoder_fuzzer.cc @@ -14,11 +14,9 @@ // //////////////////////////////////////////////////////////////////////////////// -#include #include #include #include -#include #include #include @@ -37,7 +35,7 @@ struct FrameConfig { int timestamp; WebPConfig webp_config; fuzz_utils::CropOrScaleParams crop_or_scale_params; - int source_image_index; + fuzz_utils::WebPPictureCpp pic_cpp; }; auto ArbitraryKMinKMax() { @@ -53,8 +51,7 @@ auto ArbitraryKMinKMax() { int AddFrame(WebPAnimEncoder** const enc, const WebPAnimEncoderOptions& anim_config, int* const width, - int* const height, int timestamp_ms, - const FrameConfig& frame_config, const uint8_t data[], size_t size, + int* const height, int timestamp_ms, FrameConfig& frame_config, uint32_t* const bit_pos) { if (enc == nullptr || width == nullptr || height == nullptr) { fprintf(stderr, "NULL parameters.\n"); @@ -63,14 +60,12 @@ int AddFrame(WebPAnimEncoder** const enc, } // Init the source picture. - WebPPicture pic = fuzz_utils::GetSourcePicture( - frame_config.source_image_index, frame_config.use_argb); + WebPPicture& pic = frame_config.pic_cpp.ref(); // Crop and scale. if (*enc == nullptr) { // First frame will set canvas width and height. if (!fuzz_utils::CropOrScale(&pic, frame_config.crop_or_scale_params)) { const WebPEncodingError error_code = pic.error_code; - WebPPictureFree(&pic); if (error_code == VP8_ENC_ERROR_OUT_OF_MEMORY) return 0; fprintf(stderr, "ExtractAndCropOrScale failed. Error code: %d\n", error_code); @@ -80,7 +75,6 @@ int AddFrame(WebPAnimEncoder** const enc, if (!WebPPictureRescale(&pic, *width, *height)) { const WebPEncodingError error_code = pic.error_code; WebPAnimEncoderDelete(*enc); - WebPPictureFree(&pic); if (error_code == VP8_ENC_ERROR_OUT_OF_MEMORY) return 0; fprintf(stderr, "WebPPictureRescale failed. Size: %d,%d. Error code: %d\n", @@ -95,7 +89,6 @@ int AddFrame(WebPAnimEncoder** const enc, *height = pic.height; *enc = WebPAnimEncoderNew(*width, *height, &anim_config); if (*enc == nullptr) { - WebPPictureFree(&pic); return 0; } } @@ -114,7 +107,6 @@ int AddFrame(WebPAnimEncoder** const enc, if (!WebPAnimEncoderAdd(*enc, &pic, timestamp_ms, &config)) { const WebPEncodingError error_code = pic.error_code; WebPAnimEncoderDelete(*enc); - WebPPictureFree(&pic); // Tolerate failures when running under the nallocfuzz engine as // WebPAnimEncoderAdd() may fail due to memory allocation errors outside of // the encoder; in muxer functions that return booleans for instance. @@ -127,19 +119,15 @@ int AddFrame(WebPAnimEncoder** const enc, std::abort(); } - WebPPictureFree(&pic); return 1; } -void AnimEncoderTest(std::string_view blob, bool minimize_size, - std::pair kmin_kmax, bool allow_mixed, - const std::vector& frame_configs, +void AnimEncoderTest(bool minimize_size, std::pair kmin_kmax, + bool allow_mixed, std::vector frame_configs, int optimization_index) { WebPAnimEncoder* enc = nullptr; int width = 0, height = 0, timestamp_ms = 0; uint32_t bit_pos = 0; - const uint8_t* const data = reinterpret_cast(blob.data()); - const size_t size = blob.size(); fuzz_utils::SetOptimization(default_VP8GetCPUInfo, optimization_index); @@ -156,9 +144,9 @@ void AnimEncoderTest(std::string_view blob, bool minimize_size, anim_config.verbose = 0; // For each frame. - for (const FrameConfig& frame_config : frame_configs) { + for (FrameConfig& frame_config : frame_configs) { if (!AddFrame(&enc, anim_config, &width, &height, timestamp_ms, - frame_config, data, size, &bit_pos)) { + frame_config, &bit_pos)) { return; } @@ -191,17 +179,31 @@ void AnimEncoderTest(std::string_view blob, bool minimize_size, } // namespace -FUZZ_TEST(AnimEncoder, AnimEncoderTest) +FUZZ_TEST(AnimIndexEncoder, AnimEncoderTest) .WithDomains( - fuzztest::String(), /*minimize_size=*/fuzztest::Arbitrary(), ArbitraryKMinKMax(), /*allow_mixed=*/fuzztest::Arbitrary(), - fuzztest::VectorOf( - fuzztest::StructOf( - fuzztest::InRange(0, 1), fuzztest::InRange(0, 131073), - fuzz_utils::ArbitraryWebPConfig(), - fuzz_utils::ArbitraryCropOrScaleParams(), - fuzztest::InRange(0, fuzz_utils::kNumSourceImages - 1))) + fuzztest::VectorOf(fuzztest::StructOf( + fuzztest::InRange(0, 1), + fuzztest::InRange(0, 131073), + fuzz_utils::ArbitraryWebPConfig(), + fuzz_utils::ArbitraryCropOrScaleParams(), + fuzz_utils::ArbitraryWebPPictureFromIndex())) + .WithMinSize(1) + .WithMaxSize(15), + /*optimization_index=*/ + fuzztest::InRange(0, fuzz_utils::kMaxOptimizationIndex)); + +FUZZ_TEST(AnimArbitraryEncoder, AnimEncoderTest) + .WithDomains( + /*minimize_size=*/fuzztest::Arbitrary(), ArbitraryKMinKMax(), + /*allow_mixed=*/fuzztest::Arbitrary(), + fuzztest::VectorOf(fuzztest::StructOf( + fuzztest::InRange(0, 1), + fuzztest::InRange(0, 131073), + fuzz_utils::ArbitraryWebPConfig(), + fuzz_utils::ArbitraryCropOrScaleParams(), + fuzz_utils::ArbitraryWebPPicture())) .WithMinSize(1) .WithMaxSize(15), /*optimization_index=*/ diff --git a/tests/fuzzer/enc_dec_fuzzer.cc b/tests/fuzzer/enc_dec_fuzzer.cc index 55bf0859..62c3c702 100644 --- a/tests/fuzzer/enc_dec_fuzzer.cc +++ b/tests/fuzzer/enc_dec_fuzzer.cc @@ -73,16 +73,15 @@ void Enc(const fuzz_utils::CropOrScaleParams& crop_or_scale_params, } } -void EncDecValidTest(bool use_argb, int source_image_index, WebPConfig config, - int optimization_index, +void EncDecValidTest(bool use_argb, fuzz_utils::WebPPictureCpp pic_cpp, + WebPConfig config, int optimization_index, const fuzz_utils::CropOrScaleParams& crop_or_scale_params, int colorspace, const fuzz_utils::WebPDecoderOptionsCpp& decoder_options) { fuzz_utils::SetOptimization(default_VP8GetCPUInfo, optimization_index); // Init the source picture. - WebPPicture pic = fuzz_utils::GetSourcePicture(source_image_index, use_argb); - std::unique_ptr pic_owner(&pic); + WebPPicture& pic = pic_cpp.ref(); WebPMemoryWriter memory_writer; WebPMemoryWriterInit(&memory_writer); @@ -164,16 +163,17 @@ void EncDecValidTest(bool use_argb, int source_image_index, WebPConfig config, } } -void EncDecTest(bool use_argb, int source_image_index, WebPConfig config, - int optimization_index, +//////////////////////////////////////////////////////////////////////////////// + +void EncDecTest(bool use_argb, fuzz_utils::WebPPictureCpp pic_cpp, + WebPConfig config, int optimization_index, const fuzz_utils::CropOrScaleParams& crop_or_scale_params, int colorspace, const fuzz_utils::WebPDecoderOptionsCpp& decoder_options) { fuzz_utils::SetOptimization(default_VP8GetCPUInfo, optimization_index); // Init the source picture. - WebPPicture pic = fuzz_utils::GetSourcePicture(source_image_index, use_argb); - std::unique_ptr pic_owner(&pic); + WebPPicture& pic = pic_cpp.ref(); WebPMemoryWriter memory_writer; WebPMemoryWriterInit(&memory_writer); std::unique_ptr @@ -204,10 +204,9 @@ void EncDecTest(bool use_argb, int source_image_index, WebPConfig config, } // namespace -FUZZ_TEST(EncDec, EncDecValidTest) +FUZZ_TEST(EncIndexDec, EncDecValidTest) .WithDomains(/*use_argb=*/fuzztest::Arbitrary(), - /*source_image_index=*/ - fuzztest::InRange(0, fuzz_utils::kNumSourceImages - 1), + fuzz_utils::ArbitraryWebPPictureFromIndex(), fuzz_utils::ArbitraryWebPConfig(), /*optimization_index=*/ fuzztest::InRange(0, @@ -216,10 +215,31 @@ FUZZ_TEST(EncDec, EncDecValidTest) /*colorspace=*/fuzztest::InRange(0, MODE_LAST - 1), fuzz_utils::ArbitraryValidWebPDecoderOptions()); -FUZZ_TEST(EncDec, EncDecTest) +FUZZ_TEST(EncArbitraryDec, EncDecValidTest) .WithDomains(/*use_argb=*/fuzztest::Arbitrary(), - /*source_image_index=*/ - fuzztest::InRange(0, fuzz_utils::kNumSourceImages - 1), + fuzz_utils::ArbitraryWebPPicture(), + fuzz_utils::ArbitraryWebPConfig(), + /*optimization_index=*/ + fuzztest::InRange(0, + fuzz_utils::kMaxOptimizationIndex), + fuzz_utils::ArbitraryCropOrScaleParams(), + /*colorspace=*/fuzztest::InRange(0, MODE_LAST - 1), + fuzz_utils::ArbitraryValidWebPDecoderOptions()); + +FUZZ_TEST(EncIndexDec, EncDecTest) + .WithDomains(/*use_argb=*/fuzztest::Arbitrary(), + fuzz_utils::ArbitraryWebPPictureFromIndex(), + fuzz_utils::ArbitraryWebPConfig(), + /*optimization_index=*/ + fuzztest::InRange(0, + fuzz_utils::kMaxOptimizationIndex), + fuzz_utils::ArbitraryCropOrScaleParams(), + /*colorspace=*/fuzztest::Arbitrary(), + fuzz_utils::ArbitraryWebPDecoderOptions()); + +FUZZ_TEST(EncArbitraryDec, EncDecTest) + .WithDomains(/*use_argb=*/fuzztest::Arbitrary(), + fuzz_utils::ArbitraryWebPPicture(), fuzz_utils::ArbitraryWebPConfig(), /*optimization_index=*/ fuzztest::InRange(0, diff --git a/tests/fuzzer/enc_fuzzer.cc b/tests/fuzzer/enc_fuzzer.cc index d76f543f..3bdbc445 100644 --- a/tests/fuzzer/enc_fuzzer.cc +++ b/tests/fuzzer/enc_fuzzer.cc @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -32,29 +33,14 @@ namespace { const VP8CPUInfo default_VP8GetCPUInfo = fuzz_utils::VP8GetCPUInfo; -void EncTest(std::string_view file, uint32_t optimization_index, bool use_argb, - WebPConfig config, - const fuzz_utils::CropOrScaleParams& crop_or_scale_params) { +void EncTestImpl(WebPPicture& pic, uint32_t optimization_index, bool use_argb, + WebPConfig config, + const fuzz_utils::CropOrScaleParams& crop_or_scale_params) { fuzz_utils::SetOptimization(default_VP8GetCPUInfo, optimization_index); - // Init the source picture. - WebPPicture pic; - if (!WebPPictureInit(&pic)) { - std::cerr << "WebPPictureInit failed.\n"; - std::abort(); - } - pic.use_argb = use_argb; - - const uint8_t* const file_data = - reinterpret_cast(file.data()); - if (fuzz_utils::IsImageTooBig(file_data, file.size())) return; - WebPImageReader reader = WebPGuessImageReader(file_data, file.size()); - if (!reader(file_data, file.size(), &pic, 1, NULL)) return; - // Crop and scale. if (!CropOrScale(&pic, crop_or_scale_params)) { const WebPEncodingError error_code = pic.error_code; - WebPPictureFree(&pic); if (error_code == VP8_ENC_ERROR_OUT_OF_MEMORY) return; std::cerr << "CropOrScale failed. Error code: " << error_code << "\n"; std::abort(); @@ -74,10 +60,8 @@ void EncTest(std::string_view file, uint32_t optimization_index, bool use_argb, if (!WebPEncode(&config, &pic)) { const WebPEncodingError error_code = pic.error_code; WebPMemoryWriterClear(&memory_writer); - WebPPictureFree(&pic); if (error_code == VP8_ENC_ERROR_OUT_OF_MEMORY) return; - std::cerr << "WebPEncode failed. Error code: " << error_code - << " \nFile starts with: " << file.substr(0, 20) << "\n"; + std::cerr << "WebPEncode failed. Error code: " << error_code << "\n"; std::abort(); } @@ -87,11 +71,9 @@ void EncTest(std::string_view file, uint32_t optimization_index, bool use_argb, 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) { - std::cerr << "WebPDecodeBGRA failed.\nFile starts with: " - << file.substr(0, 20) << "\n"; + std::cerr << "WebPDecodeBGRA failed.\n"; WebPFree(rgba); WebPMemoryWriterClear(&memory_writer); - WebPPictureFree(&pic); std::abort(); } @@ -110,13 +92,9 @@ void EncTest(std::string_view file, uint32_t optimization_index, bool use_argb, } } if (v1 != v2) { - std::cerr - << "Lossless compression failed pixel-exactness.\nFile starts " - "with: " - << file.substr(0, 20) << "\n"; + std::cerr << "Lossless compression failed pixel-exactness.\n"; WebPFree(rgba); WebPMemoryWriterClear(&memory_writer); - WebPPictureFree(&pic); std::abort(); } } @@ -128,10 +106,41 @@ void EncTest(std::string_view file, uint32_t optimization_index, bool use_argb, WebPPictureFree(&pic); } +// For open source compatibility, maybe_unused is necessary. +[[maybe_unused]] void EncFileTest( + std::string_view file, uint32_t optimization_index, bool use_argb, + WebPConfig config, + const fuzz_utils::CropOrScaleParams& crop_or_scale_params) { + // Init the source picture. + WebPPicture pic; + if (!WebPPictureInit(&pic)) { + std::cerr << "WebPPictureInit failed.\n"; + std::abort(); + } + pic.use_argb = use_argb; + + const uint8_t* const file_data = + reinterpret_cast(file.data()); + if (fuzz_utils::IsImageTooBig(file_data, file.size())) return; + WebPImageReader reader = WebPGuessImageReader(file_data, file.size()); + if (!reader(file_data, file.size(), &pic, 1, NULL)) return; + + EncTestImpl(pic, optimization_index, use_argb, config, crop_or_scale_params); +} + +void EncArbitraryTest( + fuzz_utils::WebPPictureCpp pic_cpp, uint32_t optimization_index, + bool use_argb, WebPConfig config, + const fuzz_utils::CropOrScaleParams& crop_or_scale_params) { + WebPPicture& pic = pic_cpp.ref(); + + EncTestImpl(pic, optimization_index, use_argb, config, crop_or_scale_params); +} + } // namespace -FUZZ_TEST(Enc, EncTest) - .WithDomains(fuzztest::Arbitrary(), +FUZZ_TEST(Enc, EncArbitraryTest) + .WithDomains(fuzz_utils::ArbitraryWebPPicture(), /*optimization_index=*/ fuzztest::InRange(0, fuzz_utils::kMaxOptimizationIndex), diff --git a/tests/fuzzer/fuzz_utils.h b/tests/fuzzer/fuzz_utils.h index 07415ec1..2aeb9d09 100644 --- a/tests/fuzzer/fuzz_utils.h +++ b/tests/fuzzer/fuzz_utils.h @@ -17,6 +17,7 @@ #ifndef WEBP_TESTS_FUZZER_FUZZ_UTILS_H_ #define WEBP_TESTS_FUZZER_FUZZ_UTILS_H_ +#include #include #include #include @@ -94,6 +95,114 @@ struct UniquePtrDeleter { } }; +// Like WebPPicture but with no C array. +// This can be removed once b/294098900 is fixed. +struct WebPPictureCpp { + inline WebPPictureCpp(int use_argb, WebPEncCSP colorspace, int width, + int height, uint8_t* y, uint8_t* u, uint8_t* v, + int y_stride, int uv_stride, uint8_t* a, int a_stride, + uint32_t* argb, int argb_stride, void* memory, + void* memory_argb) { + pic.reset(new WebPPicture(), [](WebPPicture* pic) { + WebPPictureFree(pic); + delete pic; + }); + if (!WebPPictureInit(pic.get())) assert(false); + pic->use_argb = use_argb; + pic->colorspace = colorspace; + pic->width = width; + pic->height = height; + pic->y = y; + pic->u = u; + pic->v = v; + pic->a = a; + pic->y_stride = y_stride; + pic->uv_stride = uv_stride; + pic->a_stride = a_stride; + pic->argb = argb; + pic->argb_stride = argb_stride; + pic->memory_ = memory; + pic->memory_argb_ = memory_argb; + } + WebPPicture& ref() const { return const_cast(*pic); } + std::shared_ptr pic; +}; + +static inline auto ArbitraryWebPPicture() { + return fuzztest::FlatMap( + // colorspace of 0 is use_argb, 1 is YUV420, 2 is YUV420A. + [](int colorspace, int width, int height) { + const int uv_width = (int)(((int64_t)width + 1) >> 1); + const int uv_height = (int)(((int64_t)height + 1) >> 1); + // Create a domain for the vector that strictly obeys w * h * 4. + size_t size = width * height; + if (colorspace == 0) size *= 4; + if (colorspace == 1) size += 2 * uv_width * uv_height; + if (colorspace == 2) size += 2 * uv_width * uv_height + size; + auto DataDomain = + fuzztest::VectorOf(fuzztest::Arbitrary()).WithSize(size); + + // Map the vector domain back into our Image struct, injecting w and h + return fuzztest::Map( + [colorspace, width, + height](const std::vector& data) -> WebPPictureCpp { + WebPPicture pic; + if (!WebPPictureInit(&pic)) assert(false); + pic.use_argb = colorspace == 0 ? 1 : 0; + pic.colorspace = static_cast( + colorspace <= 1 ? WEBP_YUV420 : WEBP_YUV420A); + pic.width = width; + pic.height = height; + if (!WebPPictureAlloc(&pic)) assert(false); + size_t size = width * height; + if (pic.use_argb) { + std::copy(data.begin(), data.begin() + size, + (uint32_t*)pic.argb); + } else { + // Y. + auto iter = data.begin(); + std::copy(iter, iter + size, (uint8_t*)pic.y); + iter += size; + // A. + if ((int)pic.colorspace & WEBP_CSP_ALPHA_BIT) { + std::copy(iter, iter + size, (uint8_t*)pic.a); + iter += size; + } + // U and V. + const int uv_width = (int)(((int64_t)width + 1) >> 1); + const int uv_height = (int)(((int64_t)height + 1) >> 1); + size = uv_width * uv_height; + std::copy(iter, iter + size, (uint8_t*)pic.u); + iter += size; + std::copy(iter, iter + size, (uint8_t*)pic.v); + } + return WebPPictureCpp(pic.use_argb, pic.colorspace, pic.width, + pic.height, pic.y, pic.u, pic.v, + pic.y_stride, pic.uv_stride, pic.a, + pic.a_stride, pic.argb, pic.argb_stride, + pic.memory_, pic.memory_argb_); + }, + DataDomain); + }, + /*colorspace=*/fuzztest::InRange(0, 2), + /*width=*/fuzztest::InRange(1, 128), + /*height=*/fuzztest::InRange(1, 128)); +} + +static inline auto ArbitraryWebPPictureFromIndex() { + return fuzztest::Map( + [](int index, bool use_argb) -> WebPPictureCpp { + // Map the vector domain back into our Image struct, injecting w and h + WebPPicture pic = fuzz_utils::GetSourcePicture(index, use_argb); + return WebPPictureCpp(use_argb, pic.colorspace, pic.width, pic.height, + pic.y, pic.u, pic.v, pic.y_stride, pic.uv_stride, + pic.a, pic.a_stride, pic.argb, pic.argb_stride, + pic.memory_, pic.memory_argb_); + }, + /*index=*/fuzztest::InRange(0, fuzz_utils::kNumSourceImages - 1), + /*use_argb=*/fuzztest::Arbitrary()); +} + static inline auto ArbitraryWebPConfig() { return fuzztest::Map( [](int lossless, int quality, int method, int image_hint, int segments,