| // Copyright 2024 Google LLC | |
| // SPDX-License-Identifier: BSD-2-Clause | |
| #include <algorithm> | |
| #include <cstring> | |
| #include <iostream> | |
| #include <string> | |
| #include "avif/avif.h" | |
| #include "aviftest_helpers.h" | |
| #include "gtest/gtest.h" | |
| namespace avif { | |
| namespace { | |
| // Used to pass the data folder path to the GoogleTest suites. | |
| const char* data_path = nullptr; | |
| //------------------------------------------------------------------------------ | |
| TEST(AvifDecodeTest, SingleWhitePixel) { | |
| const char* file_name = "white_1x1.avif"; | |
| DecoderPtr decoder(avifDecoderCreate()); | |
| ASSERT_NE(decoder, nullptr); | |
| ASSERT_EQ(avifDecoderSetIOFile(decoder.get(), | |
| (std::string(data_path) + file_name).c_str()), | |
| AVIF_RESULT_OK); | |
| ASSERT_EQ(avifDecoderParse(decoder.get()), AVIF_RESULT_OK); | |
| if (testutil::Av1DecoderAvailable()) { | |
| EXPECT_EQ(avifDecoderNextImage(decoder.get()), AVIF_RESULT_OK); | |
| } | |
| } | |
| TEST(AvifDecodeTest, MdatSize0) { | |
| testutil::AvifRwData avif = | |
| testutil::ReadFile(std::string(data_path) + "white_1x1.avif"); | |
| // Edit the file to simulate an 'mdat' box with size 0 (meaning ending at EOF) | |
| const uint8_t* kMdat = reinterpret_cast<const uint8_t*>("mdat"); | |
| uint8_t* mdat_position = | |
| std::search(avif.data, avif.data + avif.size, kMdat, kMdat + 4); | |
| ASSERT_NE(mdat_position, avif.data + avif.size); | |
| mdat_position[-1] = '\0'; | |
| DecoderPtr decoder(avifDecoderCreate()); | |
| ASSERT_NE(decoder, nullptr); | |
| ASSERT_EQ(avifDecoderSetIOMemory(decoder.get(), avif.data, avif.size), | |
| AVIF_RESULT_OK); | |
| ASSERT_EQ(avifDecoderParse(decoder.get()), AVIF_RESULT_OK); | |
| if (testutil::Av1DecoderAvailable()) { | |
| EXPECT_EQ(avifDecoderNextImage(decoder.get()), AVIF_RESULT_OK); | |
| } | |
| } | |
| TEST(AvifDecodeTest, MetaSize0) { | |
| testutil::AvifRwData avif = | |
| testutil::ReadFile(std::string(data_path) + "white_1x1.avif"); | |
| // Edit the file to simulate a 'meta' box with size 0 (invalid). | |
| const uint8_t* kMeta = reinterpret_cast<const uint8_t*>("meta"); | |
| uint8_t* meta_position = | |
| std::search(avif.data, avif.data + avif.size, kMeta, kMeta + 4); | |
| ASSERT_NE(meta_position, avif.data + avif.size); | |
| meta_position[-1] = '\0'; | |
| DecoderPtr decoder(avifDecoderCreate()); | |
| ASSERT_NE(decoder, nullptr); | |
| ASSERT_EQ(avifDecoderSetIOMemory(decoder.get(), avif.data, avif.size), | |
| AVIF_RESULT_OK); | |
| // This should fail because the meta box contains the mdat box. | |
| // However, the section 8.11.3.1 of ISO/IEC 14496-12 does not explicitly | |
| // require the coded image item extents to be read from the MediaDataBox if | |
| // the construction_method is 0. | |
| // Maybe another section or specification enforces that. | |
| ASSERT_EQ(avifDecoderParse(decoder.get()), AVIF_RESULT_OK); | |
| if (testutil::Av1DecoderAvailable()) { | |
| EXPECT_EQ(avifDecoderNextImage(decoder.get()), AVIF_RESULT_OK); | |
| } | |
| } | |
| TEST(AvifDecodeTest, FtypSize0) { | |
| testutil::AvifRwData avif = | |
| testutil::ReadFile(std::string(data_path) + "white_1x1.avif"); | |
| // Edit the file to simulate a 'ftyp' box with size 0 (invalid). | |
| avif.data[3] = '\0'; | |
| DecoderPtr decoder(avifDecoderCreate()); | |
| ASSERT_NE(decoder, nullptr); | |
| ASSERT_EQ(avifDecoderSetIOMemory(decoder.get(), avif.data, avif.size), | |
| AVIF_RESULT_OK); | |
| ASSERT_EQ(avifDecoderParse(decoder.get()), AVIF_RESULT_BMFF_PARSE_FAILED); | |
| } | |
| TEST(AvifDecodeTest, UnknownTopLevelBoxSize0) { | |
| testutil::AvifRwData avif = | |
| testutil::ReadFile(std::string(data_path) + "white_1x1.avif"); | |
| // Edit the file to insert an unknown top level box with size 0 after ftyp | |
| // (invalid). | |
| testutil::AvifRwData avif_edited; | |
| ASSERT_EQ(avifRWDataRealloc(&avif_edited, avif.size + 8), AVIF_RESULT_OK); | |
| // Copy the ftyp box. | |
| std::memcpy(avif_edited.data, avif.data, 32); | |
| // Set 8 bytes to 0 (box type and size all 0s). | |
| std::memset(avif_edited.data + 32, 0, 8); | |
| // Copy the other boxes. | |
| std::memcpy(avif_edited.data + 40, avif.data + 32, avif.size - 32); | |
| DecoderPtr decoder(avifDecoderCreate()); | |
| ASSERT_NE(decoder, nullptr); | |
| ASSERT_EQ( | |
| avifDecoderSetIOMemory(decoder.get(), avif_edited.data, avif_edited.size), | |
| AVIF_RESULT_OK); | |
| ASSERT_EQ(avifDecoderParse(decoder.get()), AVIF_RESULT_BMFF_PARSE_FAILED); | |
| } | |
| //------------------------------------------------------------------------------ | |
| } // namespace | |
| } // namespace avif | |
| int main(int argc, char** argv) { | |
| ::testing::InitGoogleTest(&argc, argv); | |
| if (argc != 2) { | |
| std::cerr << "There must be exactly one argument containing the path to " | |
| "the test data folder" | |
| << std::endl; | |
| return 1; | |
| } | |
| avif::data_path = argv[1]; | |
| return RUN_ALL_TESTS(); | |
| } |