ipma box parsing accepts invalid inputs Fixes: #485
diff --git a/src/read.c b/src/read.c index 3175847..f8ebf18 100644 --- a/src/read.c +++ b/src/read.c
@@ -32,6 +32,10 @@ static const char xmpContentType[] = CONTENT_TYPE_XMP; static const size_t xmpContentTypeSize = sizeof(xmpContentType); +// The only supported ipma box values for both version and flags are [0,1], so there technically +// can't be more than 4 unique tuples right now. +#define MAX_IPMA_VERSION_AND_FLAGS_SEEN 4 + // --------------------------------------------------------------------------- // Box data structures @@ -1436,14 +1440,18 @@ return AVIF_TRUE; } -static avifBool avifParseItemPropertyAssociation(avifMeta * meta, const uint8_t * raw, size_t rawLen) +static avifBool avifParseItemPropertyAssociation(avifMeta * meta, const uint8_t * raw, size_t rawLen, uint32_t * outVersionAndFlags) { + // NOTE: If this function ever adds support for versions other than [0,1] or flags other than + // [0,1], please increase the value of MAX_IPMA_VERSION_AND_FLAGS_SEEN accordingly. + BEGIN_STREAM(s, raw, rawLen); uint8_t version; uint32_t flags; CHECK(avifROStreamReadVersionAndFlags(&s, &version, &flags)); avifBool propertyIndexIsU16 = ((flags & 0x1) != 0); + *outVersionAndFlags = (version << 24) | flags; uint32_t entryCount; CHECK(avifROStreamReadU32(&s, &entryCount)); @@ -1580,13 +1588,30 @@ CHECK(avifParseItemPropertyContainerBox(&meta->properties, avifROStreamCurrent(&s), ipcoHeader.size)); CHECK(avifROStreamSkip(&s, ipcoHeader.size)); + uint32_t versionAndFlagsSeen[MAX_IPMA_VERSION_AND_FLAGS_SEEN]; + uint32_t versionAndFlagsSeenCount = 0; + // Now read all ItemPropertyAssociation until the end of the box, and make associations while (avifROStreamHasBytesLeft(&s, 1)) { avifBoxHeader ipmaHeader; CHECK(avifROStreamReadBoxHeader(&s, &ipmaHeader)); if (!memcmp(ipmaHeader.type, "ipma", 4)) { - CHECK(avifParseItemPropertyAssociation(meta, avifROStreamCurrent(&s), ipmaHeader.size)); + uint32_t versionAndFlags; + CHECK(avifParseItemPropertyAssociation(meta, avifROStreamCurrent(&s), ipmaHeader.size, &versionAndFlags)); + for (uint32_t i = 0; i < versionAndFlagsSeenCount; ++i) { + if (versionAndFlagsSeen[i] == versionAndFlags) { + // HEIF (ISO 23008-12:2017) 9.3.1 - There shall be at most one + // ItemPropertyAssociation box with a given pair of values of version and + // flags. + return AVIF_FALSE; + } + } + if (versionAndFlagsSeenCount == MAX_IPMA_VERSION_AND_FLAGS_SEEN) { + return AVIF_FALSE; + } + versionAndFlagsSeen[versionAndFlagsSeenCount] = versionAndFlags; + ++versionAndFlagsSeenCount; } else { // These must all be type ipma return AVIF_FALSE;