Skip to content

Commit f0aac7d

Browse files
authored
Reject invalid depth in avifImageAllocatePlanes() (AOMediaCodec#3038)
Also reject >16-bit in avifParsePixelInformationProperty() to avoid returning the wrong AVIF_RESULT_OUT_OF_MEMORY error code later. Also reject depth 0 in avifParsePixelInformationProperty(). Add test.
1 parent ec2ee83 commit f0aac7d

3 files changed

Lines changed: 73 additions & 3 deletions

File tree

src/avif.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -426,7 +426,7 @@ avifResult avifImageAddUUIDProperty(avifImage * image, const uint8_t uuid[16], c
426426

427427
avifResult avifImageAllocatePlanes(avifImage * image, avifPlanesFlags planes)
428428
{
429-
if (image->width == 0 || image->height == 0) {
429+
if (image->width == 0 || image->height == 0 || image->depth == 0 || image->depth > 16) {
430430
return AVIF_RESULT_INVALID_ARGUMENT;
431431
}
432432
const uint32_t channelSize = avifImageUsesU16(image) ? 2 : 1;

src/read.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2708,12 +2708,14 @@ static avifResult avifParsePixelInformationProperty(avifProperty * prop, const u
27082708
}
27092709
for (uint8_t i = 0; i < pixi->planeCount; ++i) {
27102710
AVIF_CHECKERR(avifROStreamRead(&s, &pixi->planeDepths[i], 1), AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int (8) bits_per_channel;
2711-
#if defined(AVIF_ENABLE_EXPERIMENTAL_EXTENDED_PIXI)
27122711
if (pixi->planeDepths[i] == 0) {
27132712
avifDiagnosticsPrintf(diag, "Box[pixi] plane depth shall not be 0 for channel %u", i);
27142713
return AVIF_RESULT_BMFF_PARSE_FAILED;
27152714
}
2716-
#endif // AVIF_ENABLE_EXPERIMENTAL_EXTENDED_PIXI
2715+
if (pixi->planeDepths[i] > 16) {
2716+
avifDiagnosticsPrintf(diag, "Box[pixi] plane depth %d is not supported", (int)pixi->planeDepths[i]);
2717+
return AVIF_RESULT_NOT_IMPLEMENTED;
2718+
}
27172719
if (pixi->planeDepths[i] != pixi->planeDepths[0]) {
27182720
avifDiagnosticsPrintf(diag,
27192721
"Box[pixi] contains unsupported mismatched plane depths [%u != %u]",

tests/gtest/avif16bittest.cc

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
// SPDX-License-Identifier: BSD-2-Clause
33

44
#include <cstring>
5+
#include <filesystem>
6+
#include <fstream>
57

68
#include "avif/avif.h"
79
#include "avif/avif_cxx.h"
@@ -376,6 +378,72 @@ INSTANTIATE_TEST_SUITE_P(
376378

377379
//------------------------------------------------------------------------------
378380

381+
TEST(Avif16bitTest, SampleTransformWithOtherBitDepths) {
382+
if (!testutil::Av1DecoderAvailable()) {
383+
GTEST_SKIP() << "AV1 Codec unavailable, skip test.";
384+
}
385+
386+
const std::string file_path =
387+
std::string(data_path) + "weld_sato_12B_8B_q0.avif";
388+
std::vector<uint8_t> encoded_16bit(std::filesystem::file_size(file_path));
389+
std::ifstream(file_path, std::ios::binary)
390+
.read(reinterpret_cast<char*>(encoded_16bit.data()),
391+
encoded_16bit.size());
392+
393+
ImagePtr reference(avifImageCreateEmpty());
394+
ASSERT_NE(reference, nullptr);
395+
DecoderPtr decoder_reference(avifDecoderCreate());
396+
ASSERT_NE(decoder_reference, nullptr);
397+
decoder_reference->imageContentToDecode |=
398+
AVIF_IMAGE_CONTENT_SAMPLE_TRANSFORMS;
399+
ASSERT_EQ(avifDecoderReadMemory(decoder_reference.get(), reference.get(),
400+
encoded_16bit.data(), encoded_16bit.size()),
401+
AVIF_RESULT_OK);
402+
403+
for (uint8_t num_bits = 0; num_bits <= 32; ++num_bits) {
404+
if (num_bits == reference->depth) {
405+
continue;
406+
}
407+
std::vector<uint8_t> encoded(encoded_16bit);
408+
// Replace 'pixi' 3-channel 16-bit by another bit depth.
409+
bool found_subsequence_to_replace = false;
410+
for (size_t i = 0; i + 4 <= encoded.size(); ++i) {
411+
if (!std::memcmp(&encoded[i],
412+
"pixi" // PixelInformationProperty 4CC
413+
"\0\0\0\0" // version and flags
414+
"\3" // num_channels
415+
"\20\20\20" // bits_per_channels (16 is 20 in octal)
416+
,
417+
4 + 4 + 1 + 3)) {
418+
encoded[i + 9] = encoded[i + 10] = encoded[i + 11] = num_bits;
419+
found_subsequence_to_replace = true;
420+
break;
421+
}
422+
}
423+
ASSERT_TRUE(found_subsequence_to_replace);
424+
425+
ImagePtr decoded(avifImageCreateEmpty());
426+
ASSERT_NE(decoded, nullptr);
427+
DecoderPtr decoder(avifDecoderCreate());
428+
ASSERT_NE(decoder, nullptr);
429+
decoder->imageContentToDecode |= AVIF_IMAGE_CONTENT_SAMPLE_TRANSFORMS;
430+
const avifResult result = avifDecoderReadMemory(
431+
decoder.get(), decoded.get(), encoded.data(), encoded.size());
432+
const avifResult expected_result =
433+
num_bits == 0 ? AVIF_RESULT_BMFF_PARSE_FAILED
434+
: num_bits > 16 ? AVIF_RESULT_NOT_IMPLEMENTED
435+
: AVIF_RESULT_OK;
436+
ASSERT_EQ(result, expected_result) << "bits_per_channels " << num_bits;
437+
if (result == AVIF_RESULT_OK) {
438+
// The output image should be highly distorted because of the pixel value
439+
// clamping to (1<<num_bits)-1.
440+
EXPECT_LE(testutil::GetPsnr(*reference, *decoded), 10.0);
441+
}
442+
}
443+
}
444+
445+
//------------------------------------------------------------------------------
446+
379447
} // namespace
380448
} // namespace avif
381449

0 commit comments

Comments
 (0)