Add arbitrary data for the encoding fuzzers

Change-Id: Ia264e0edf742630f45d22670f5e1ead140036046
This commit is contained in:
Vincent Rabaud
2025-11-24 13:54:05 +01:00
parent 4ebf0b0ac8
commit 5465220b03
4 changed files with 212 additions and 72 deletions

View File

@@ -14,11 +14,9 @@
//
////////////////////////////////////////////////////////////////////////////////
#include <cstddef>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <string_view>
#include <utility>
#include <vector>
@@ -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<int, int> kmin_kmax, bool allow_mixed,
const std::vector<FrameConfig>& frame_configs,
void AnimEncoderTest(bool minimize_size, std::pair<int, int> kmin_kmax,
bool allow_mixed, std::vector<FrameConfig> 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<const uint8_t*>(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<bool>(), ArbitraryKMinKMax(),
/*allow_mixed=*/fuzztest::Arbitrary<bool>(),
fuzztest::VectorOf(
fuzztest::StructOf<FrameConfig>(
fuzztest::InRange<int>(0, 1), fuzztest::InRange<int>(0, 131073),
fuzz_utils::ArbitraryWebPConfig(),
fuzz_utils::ArbitraryCropOrScaleParams(),
fuzztest::InRange<int>(0, fuzz_utils::kNumSourceImages - 1)))
fuzztest::VectorOf(fuzztest::StructOf<FrameConfig>(
fuzztest::InRange<int>(0, 1),
fuzztest::InRange<int>(0, 131073),
fuzz_utils::ArbitraryWebPConfig(),
fuzz_utils::ArbitraryCropOrScaleParams(),
fuzz_utils::ArbitraryWebPPictureFromIndex()))
.WithMinSize(1)
.WithMaxSize(15),
/*optimization_index=*/
fuzztest::InRange<uint32_t>(0, fuzz_utils::kMaxOptimizationIndex));
FUZZ_TEST(AnimArbitraryEncoder, AnimEncoderTest)
.WithDomains(
/*minimize_size=*/fuzztest::Arbitrary<bool>(), ArbitraryKMinKMax(),
/*allow_mixed=*/fuzztest::Arbitrary<bool>(),
fuzztest::VectorOf(fuzztest::StructOf<FrameConfig>(
fuzztest::InRange<int>(0, 1),
fuzztest::InRange<int>(0, 131073),
fuzz_utils::ArbitraryWebPConfig(),
fuzz_utils::ArbitraryCropOrScaleParams(),
fuzz_utils::ArbitraryWebPPicture()))
.WithMinSize(1)
.WithMaxSize(15),
/*optimization_index=*/

View File

@@ -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<WebPPicture, fuzz_utils::UniquePtrDeleter> 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<WebPPicture, fuzz_utils::UniquePtrDeleter> pic_owner(&pic);
WebPPicture& pic = pic_cpp.ref();
WebPMemoryWriter memory_writer;
WebPMemoryWriterInit(&memory_writer);
std::unique_ptr<WebPMemoryWriter, fuzz_utils::UniquePtrDeleter>
@@ -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<bool>(),
/*source_image_index=*/
fuzztest::InRange<int>(0, fuzz_utils::kNumSourceImages - 1),
fuzz_utils::ArbitraryWebPPictureFromIndex(),
fuzz_utils::ArbitraryWebPConfig(),
/*optimization_index=*/
fuzztest::InRange<uint32_t>(0,
@@ -216,10 +215,31 @@ FUZZ_TEST(EncDec, EncDecValidTest)
/*colorspace=*/fuzztest::InRange<int>(0, MODE_LAST - 1),
fuzz_utils::ArbitraryValidWebPDecoderOptions());
FUZZ_TEST(EncDec, EncDecTest)
FUZZ_TEST(EncArbitraryDec, EncDecValidTest)
.WithDomains(/*use_argb=*/fuzztest::Arbitrary<bool>(),
/*source_image_index=*/
fuzztest::InRange<int>(0, fuzz_utils::kNumSourceImages - 1),
fuzz_utils::ArbitraryWebPPicture(),
fuzz_utils::ArbitraryWebPConfig(),
/*optimization_index=*/
fuzztest::InRange<uint32_t>(0,
fuzz_utils::kMaxOptimizationIndex),
fuzz_utils::ArbitraryCropOrScaleParams(),
/*colorspace=*/fuzztest::InRange<int>(0, MODE_LAST - 1),
fuzz_utils::ArbitraryValidWebPDecoderOptions());
FUZZ_TEST(EncIndexDec, EncDecTest)
.WithDomains(/*use_argb=*/fuzztest::Arbitrary<bool>(),
fuzz_utils::ArbitraryWebPPictureFromIndex(),
fuzz_utils::ArbitraryWebPConfig(),
/*optimization_index=*/
fuzztest::InRange<uint32_t>(0,
fuzz_utils::kMaxOptimizationIndex),
fuzz_utils::ArbitraryCropOrScaleParams(),
/*colorspace=*/fuzztest::Arbitrary<int>(),
fuzz_utils::ArbitraryWebPDecoderOptions());
FUZZ_TEST(EncArbitraryDec, EncDecTest)
.WithDomains(/*use_argb=*/fuzztest::Arbitrary<bool>(),
fuzz_utils::ArbitraryWebPPicture(),
fuzz_utils::ArbitraryWebPConfig(),
/*optimization_index=*/
fuzztest::InRange<uint32_t>(0,

View File

@@ -18,6 +18,7 @@
#include <cstdint>
#include <cstdlib>
#include <iostream>
#include <memory>
#include <string>
#include <string_view>
@@ -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<const uint8_t*>(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<const uint8_t*>(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<std::string>(),
FUZZ_TEST(Enc, EncArbitraryTest)
.WithDomains(fuzz_utils::ArbitraryWebPPicture(),
/*optimization_index=*/
fuzztest::InRange<uint32_t>(0,
fuzz_utils::kMaxOptimizationIndex),

View File

@@ -17,6 +17,7 @@
#ifndef WEBP_TESTS_FUZZER_FUZZ_UTILS_H_
#define WEBP_TESTS_FUZZER_FUZZ_UTILS_H_
#include <algorithm>
#include <array>
#include <cassert>
#include <cstddef>
@@ -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<WebPPicture&>(*pic); }
std::shared_ptr<WebPPicture> 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<uint8_t>()).WithSize(size);
// Map the vector domain back into our Image struct, injecting w and h
return fuzztest::Map(
[colorspace, width,
height](const std::vector<uint8_t>& data) -> WebPPictureCpp {
WebPPicture pic;
if (!WebPPictureInit(&pic)) assert(false);
pic.use_argb = colorspace == 0 ? 1 : 0;
pic.colorspace = static_cast<WebPEncCSP>(
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<int>(0, 2),
/*width=*/fuzztest::InRange<int>(1, 128),
/*height=*/fuzztest::InRange<int>(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<int>(0, fuzz_utils::kNumSourceImages - 1),
/*use_argb=*/fuzztest::Arbitrary<bool>());
}
static inline auto ArbitraryWebPConfig() {
return fuzztest::Map(
[](int lossless, int quality, int method, int image_hint, int segments,