blob: 5d7814f01ac2097a7f683738b643ef3ac9349721 [file] [log] [blame]
// Copyright 2022 Google LLC
// SPDX-License-Identifier: BSD-2-Clause
// Compare non-incremental and incremental decode results of an arbitrary byte
// sequence.
#include <algorithm>
#include <cstdint>
#include <string>
#include "avif/avif.h"
#include "avif_fuzztest_helpers.h"
#include "avifincrtest_helpers.h"
#include "fuzztest/fuzztest.h"
#include "gtest/gtest.h"
using ::fuzztest::Arbitrary;
namespace avif {
namespace testutil {
namespace {
::testing::Environment* const kStackLimitEnv = SetStackLimitTo512x1024Bytes();
//------------------------------------------------------------------------------
struct DecoderInput {
const uint8_t* available_bytes;
size_t available_size;
size_t read_size;
};
// A custom reader is necessary to get the number of bytes read by libavif.
// See avifIOReadFunc() documentation.
avifResult AvifIoRead(struct avifIO* io, uint32_t read_flags, uint64_t offset,
size_t size, avifROData* out) {
DecoderInput* data = reinterpret_cast<DecoderInput*>(io->data);
if (read_flags != 0 || !data || data->available_size < offset) {
return AVIF_RESULT_IO_ERROR;
}
out->data = data->available_bytes + offset;
out->size =
std::min(size, data->available_size - static_cast<size_t>(offset));
data->read_size =
std::max(data->read_size, static_cast<size_t>(offset) + out->size);
return AVIF_RESULT_OK;
}
//------------------------------------------------------------------------------
void DecodeIncr(const std::string& arbitrary_bytes, bool is_persistent,
bool give_size_hint, bool use_nth_image_api) {
ASSERT_FALSE(GetSeedDataDirs().empty()); // Make sure seeds are available.
ImagePtr reference(avifImageCreateEmpty());
ASSERT_NE(reference.get(), nullptr);
DecoderInput data = {reinterpret_cast<const uint8_t*>(arbitrary_bytes.data()),
arbitrary_bytes.size(), 0};
avifIO io = {.destroy = nullptr,
.read = AvifIoRead,
.write = nullptr,
.sizeHint = arbitrary_bytes.size(),
.persistent = AVIF_TRUE,
.data = &data};
DecoderPtr decoder(avifDecoderCreate());
ASSERT_NE(decoder.get(), nullptr);
avifDecoderSetIO(decoder.get(), &io);
// OSS-Fuzz limits the allocated memory to 2560 MB.
constexpr uint32_t kMaxMem = 2560u * 1024 * 1024;
// Consider at most four planes of 16-bit samples.
constexpr uint32_t kMaxImageSize =
kMaxMem / (AVIF_PLANE_COUNT_YUV + 1) / sizeof(uint16_t);
// Reduce the limit further to include pixel buffer copies and other memory
// allocations.
constexpr uint32_t kImageSizeLimit = kMaxImageSize / 4;
// avifDecoderParse returns AVIF_RESULT_NOT_IMPLEMENTED if kImageSizeLimit is
// bigger than AVIF_DEFAULT_IMAGE_SIZE_LIMIT.
static_assert(kImageSizeLimit <= AVIF_DEFAULT_IMAGE_SIZE_LIMIT,
"Too big an image size limit");
decoder->imageSizeLimit = kImageSizeLimit;
if (avifDecoderRead(decoder.get(), reference.get()) == AVIF_RESULT_OK) {
// Avoid timeouts by discarding big images decoded many times.
// TODO(yguyon): Increase this arbitrary threshold but decode incrementally
// fewer times than as many bytes.
if (reference->width * reference->height * data.read_size >
8 * 1024 * 1024) {
return;
}
// decodeIncrementally() will fail if there are leftover bytes.
const avifRWData encoded_data = {const_cast<uint8_t*>(data.available_bytes),
data.read_size};
// No clue on whether encoded_data is tiled so use a lower bound of a single
// tile for the whole image.
// Note that an AVIF tile is at most as high as an AV1 frame
// (aomediacodec.github.io/av1-spec says max_frame_height_minus_1 < 65536)
// but libavif successfully decodes AVIF files with dimensions unrelated to
// the underlying AV1 frame (for example a 1x1000000 AVIF for a 1x1 AV1).
// Otherwise we could use the minimum of reference->height and 65536u below.
const uint32_t max_cell_height = reference->height;
const avifResult result = DecodeIncrementally(
encoded_data, decoder.get(), is_persistent, give_size_hint,
use_nth_image_api, *reference, max_cell_height);
// The result does not matter, as long as we do not crash.
(void)result;
}
}
FUZZ_TEST(DecodeAvifFuzzTest, DecodeIncr)
.WithDomains(ArbitraryImageWithSeeds({AVIF_APP_FILE_FORMAT_AVIF}),
Arbitrary<bool>(), Arbitrary<bool>(), Arbitrary<bool>());
//------------------------------------------------------------------------------
} // namespace
} // namespace testutil
} // namespace avif