Port the PoC for b/506387278 to libavif
See https://github.com/webmproject/CrabbyAvif/pull/812. The bug doesn't
affect libavif, but it is good to add the test to libavif's test suite.
Bug: https://b.corp.google.com/issues/506387278
diff --git a/tests/data/README.md b/tests/data/README.md
index 0a370bd..8c7ede2 100644
--- a/tests/data/README.md
+++ b/tests/data/README.md
@@ -981,6 +981,14 @@
Source: Random frames generated with libavif.
+### File [poc_b_506387278.avif](poc_b_506387278.avif)
+
+
+
+License: [same as libavif](https://github.com/AOMediaCodec/libavif/blob/main/LICENSE)
+
+Source: https://b.corp.google.com/issues/506387278
+
### File [webp_logo_animated.y4m](webp_logo_animated.y4m)

diff --git a/tests/data/poc_b_506387278.avif b/tests/data/poc_b_506387278.avif
new file mode 100644
index 0000000..66b4380
--- /dev/null
+++ b/tests/data/poc_b_506387278.avif
Binary files differ
diff --git a/tests/gtest/avifdecodetest.cc b/tests/gtest/avifdecodetest.cc
index 699f01a..4921c32 100644
--- a/tests/gtest/avifdecodetest.cc
+++ b/tests/gtest/avifdecodetest.cc
@@ -3,6 +3,7 @@
#include <iostream>
#include <string>
+#include <vector>
#include "avif/avif.h"
#include "aviftest_helpers.h"
@@ -128,6 +129,59 @@
EXPECT_FALSE(avifPeekCompatibleFileType(&input));
}
+struct NonPersistentIO {
+ std::vector<uint8_t> data;
+ avifROData ro_data;
+};
+
+// Every read call will explicitly invalidate the pointer returned by the
+// previous read call to ensure non-persistent IO.
+avifResult NonPersistentRead(struct avifIO* io, uint32_t flags, uint64_t offset,
+ size_t size, avifROData* out) {
+ NonPersistentIO* io_data = reinterpret_cast<NonPersistentIO*>(io->data);
+ if (flags != 0 || offset > io_data->ro_data.size) {
+ return AVIF_RESULT_IO_ERROR;
+ }
+ uint64_t available_size = io_data->ro_data.size - offset;
+ if (size > available_size) {
+ size = static_cast<size_t>(available_size);
+ }
+ // Clear the existing vector.
+ std::vector<uint8_t>().swap(io_data->data);
+ // Copy new data into the vector.
+ io_data->data.reserve(size);
+ io_data->data.assign(io_data->ro_data.data + offset,
+ io_data->ro_data.data + offset + size);
+ // Set the output.
+ out->data = io_data->data.data();
+ out->size = size;
+ return AVIF_RESULT_OK;
+}
+
+TEST(AvifDecodeTest, NonPersistentIOBug506387278) {
+ const testutil::AvifRwData avif =
+ testutil::ReadFile(std::string(data_path) + "poc_b_506387278.avif");
+ NonPersistentIO io_data;
+ io_data.ro_data = {/*.data=*/avif.data, /*.size=*/avif.size};
+ avifIO io = {/*.destroy=*/nullptr,
+ /*.read=*/NonPersistentRead,
+ /*.write=*/nullptr,
+ /*.sizeHint=*/avif.size,
+ /*.persistent=*/false,
+ /*.data=*/&io_data};
+ // |io| must outlive the decoder.
+ DecoderPtr decoder(avifDecoderCreate());
+ ASSERT_NE(decoder, nullptr);
+ avifDecoderSetIO(decoder.get(), &io);
+ ASSERT_EQ(avifDecoderParse(decoder.get()), AVIF_RESULT_OK);
+ EXPECT_EQ(decoder->imageSequenceTrackPresent, AVIF_TRUE);
+ EXPECT_EQ(decoder->imageCount, 2);
+ if (testutil::Av1DecoderAvailable()) {
+ EXPECT_EQ(avifDecoderNextImage(decoder.get()), AVIF_RESULT_OK);
+ EXPECT_EQ(avifDecoderNextImage(decoder.get()), AVIF_RESULT_OK);
+ }
+}
+
} // namespace
} // namespace avif