From f2cc9193849ce52876ca326c8aab5fa5d190ae6e Mon Sep 17 00:00:00 2001 From: Brad Hards Date: Sat, 30 Nov 2024 19:57:15 +1100 Subject: [PATCH] properties: sanity check boxtype and UUID values --- CMakeLists.txt | 1 + include/avif/internal.h | 5 +++ src/avif.c | 6 ++++ src/properties.c | 54 +++++++++++++++++++++++++++++ tests/gtest/avifpropertytest.cc | 4 +++ tests/gtest/avifpropinternaltest.cc | 54 +++++++++++++++++++++++++++++ 6 files changed, 124 insertions(+) create mode 100644 src/properties.c create mode 100644 tests/gtest/avifpropinternaltest.cc diff --git a/CMakeLists.txt b/CMakeLists.txt index b54a208b57..72e10a07ab 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -386,6 +386,7 @@ set(AVIF_SRCS src/io.c src/mem.c src/obu.c + src/properties.c src/rawdata.c src/read.c src/reformat.c diff --git a/include/avif/internal.h b/include/avif/internal.h index 26e7e50689..85a006f4a9 100644 --- a/include/avif/internal.h +++ b/include/avif/internal.h @@ -156,6 +156,11 @@ AVIF_API avifResult avifImagePushProperty(avifImage * image, const uint8_t * boxPayload, size_t boxPayloadSize); +// Check if the FourCC property value is a known value +AVIF_NODISCARD avifBool avifIsKnownPropertyType(const uint8_t boxtype[4]); +// Check if the extended property (UUID) is valid +AVIF_NODISCARD avifBool avifIsValidUUID(const uint8_t uuid[16]); + // --------------------------------------------------------------------------- #if defined(AVIF_ENABLE_EXPERIMENTAL_SAMPLE_TRANSFORM) diff --git a/src/avif.c b/src/avif.c index bb0f525b54..5bef7e4cac 100644 --- a/src/avif.c +++ b/src/avif.c @@ -415,12 +415,18 @@ avifResult avifImagePushProperty(avifImage * image, const uint8_t boxtype[4], co avifResult avifImageAddOpaqueProperty(avifImage * image, const uint8_t boxtype[4], const uint8_t * data, size_t dataSize) { const uint8_t uuid[16] = { 0 }; + if (avifIsKnownPropertyType(boxtype)) { + return AVIF_RESULT_INVALID_ARGUMENT; + } return avifImagePushProperty(image, boxtype, uuid, data, dataSize); } avifResult avifImageAddUUIDProperty(avifImage * image, const uint8_t uuid[16], const uint8_t * data, size_t dataSize) { const uint8_t boxtype[4] = { 'u', 'u', 'i', 'd' }; + if (!avifIsValidUUID(uuid)) { + return AVIF_RESULT_INVALID_ARGUMENT; + } return avifImagePushProperty(image, boxtype, uuid, data, dataSize); } diff --git a/src/properties.c b/src/properties.c new file mode 100644 index 0000000000..97439fe0f1 --- /dev/null +++ b/src/properties.c @@ -0,0 +1,54 @@ +// Copyright 2024 Brad Hards. All rights reserved. +// SPDX-License-Identifier: BSD-2-Clause + +#include "avif/internal.h" +#include + +struct avifKnownProperty +{ + const uint8_t fourcc[4]; +}; + +static const struct avifKnownProperty avifKnownProperties[] = { + { "ftyp" }, { "uuid" }, { "meta" }, { "hdlr" }, { "pitm" }, { "dinf" }, { "dref" }, { "idat" }, { "iloc" }, + { "iinf" }, { "infe" }, { "iprp" }, { "ipco" }, { "av1C" }, { "av2C" }, { "ispe" }, { "pixi" }, { "pasp" }, + { "colr" }, { "auxC" }, { "clap" }, { "irot" }, { "imir" }, { "clli" }, { "cclv" }, { "mdcv" }, { "amve" }, + { "reve" }, { "ndwt" }, { "a1op" }, { "lsel" }, { "a1lx" }, { "cmin" }, { "cmex" }, { "ipma" }, { "iref" }, + { "auxl" }, { "thmb" }, { "dimg" }, { "prem" }, { "cdsc" }, { "grpl" }, { "altr" }, { "ster" }, { "mdat" }, +}; + +static const size_t numKnownProperties = sizeof(avifKnownProperties) / sizeof(avifKnownProperties[0]); + +static const size_t FOURCC_BYTES = 4; +static const size_t UUID_BYTES = 16; + +static const uint8_t ISO_UUID_SUFFIX[12] = { 0x00, 0x01, 0x00, 0x10, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9b, 0x71 }; + +avifBool avifIsKnownPropertyType(const uint8_t boxtype[4]) +{ + for (size_t i = 0; i < numKnownProperties; i++) { + if (memcmp(avifKnownProperties[i].fourcc, boxtype, FOURCC_BYTES) == 0) { + return AVIF_TRUE; + } + } + return AVIF_FALSE; +} + +avifBool avifIsValidUUID(const uint8_t uuid[16]) +{ + for (size_t i = 0; i < numKnownProperties; i++) { + if ((memcmp(avifKnownProperties[i].fourcc, uuid, FOURCC_BYTES) == 0) && + (memcmp(ISO_UUID_SUFFIX, uuid + FOURCC_BYTES, UUID_BYTES - FOURCC_BYTES) == 0)) { + return AVIF_FALSE; + } + } + uint8_t variant = uuid[8] >> 4; + if ((variant < 0x08) || (variant > 0x0b)) { + return AVIF_FALSE; + } + uint8_t version = uuid[6] >> 4; + if ((version < 1) || (version > 8)) { + return AVIF_FALSE; + } + return AVIF_TRUE; +} diff --git a/tests/gtest/avifpropertytest.cc b/tests/gtest/avifpropertytest.cc index 404f5f865d..a5d2356303 100644 --- a/tests/gtest/avifpropertytest.cc +++ b/tests/gtest/avifpropertytest.cc @@ -59,6 +59,10 @@ TEST(AvifPropertyTest, Serialise) { ASSERT_EQ(avifImageAddOpaqueProperty(image.get(), (uint8_t*)"efgh", efgh_data.data(), efgh_data.size()), AVIF_RESULT_OK); + // Should not be added + ASSERT_EQ(avifImageAddOpaqueProperty(image.get(), (uint8_t*)"mdat", + efgh_data.data(), efgh_data.size()), + AVIF_RESULT_INVALID_ARGUMENT); ASSERT_EQ(avifImageAddUUIDProperty(image.get(), uuid, uuid_data.data(), uuid_data.size()), AVIF_RESULT_OK); diff --git a/tests/gtest/avifpropinternaltest.cc b/tests/gtest/avifpropinternaltest.cc new file mode 100644 index 0000000000..31ed3e17fd --- /dev/null +++ b/tests/gtest/avifpropinternaltest.cc @@ -0,0 +1,54 @@ +// Copyright 2023 Google LLC +// SPDX-License-Identifier: BSD-2-Clause + +#include + +#include "avif/internal.h" +#include "aviftest_helpers.h" +#include "gtest/gtest.h" + +namespace avif { +namespace { + +TEST(InternalPropertiesTest, KnownFound) { + const uint8_t FTYP[4]{'f', 't', 'y', 'p'}; + ASSERT_EQ(avifIsKnownPropertyType(FTYP), AVIF_TRUE); + const uint8_t MDAT[4]{'m', 'd', 'a', 't'}; + ASSERT_EQ(avifIsKnownPropertyType(MDAT), AVIF_TRUE); + const uint8_t ISPE[4]{'i', 's', 'p', 'e'}; + ASSERT_EQ(avifIsKnownPropertyType(ISPE), AVIF_TRUE); +} + +TEST(InternalPropertiesTest, UnknownNotFound) { + const uint8_t SIEP[4]{'s', 'i', 'e', 'p'}; + ASSERT_EQ(avifIsKnownPropertyType(SIEP), AVIF_FALSE); + const uint8_t MTXF[4]{'m', 't', 'x', 'f'}; + ASSERT_EQ(avifIsKnownPropertyType(MTXF), AVIF_FALSE); +} + +TEST(InternalPropertiesTest, UuidValid) { + const uint8_t uuid[16]{0x98, 0x10, 0xd7, 0xfc, 0xa5, 0xd2, 0x4c, 0x4b, + 0x9a, 0x4f, 0x05, 0x99, 0x02, 0xf4, 0x9b, 0xfd}; + ASSERT_EQ(avifIsValidUUID(uuid), AVIF_TRUE); +} + +TEST(InternalPropertiesTest, UuidInvalidISO) { + const uint8_t uuid[16]{'m', 'd', 'a', 't', 0x00, 0x01, 0x00, 0x10, + 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}; + ASSERT_EQ(avifIsValidUUID(uuid), AVIF_FALSE); +} + +TEST(InternalPropertiesTest, UuidInvalidVariant) { + const uint8_t uuid[16]{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, + 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + ASSERT_EQ(avifIsValidUUID(uuid), AVIF_FALSE); +} + +TEST(InternalPropertiesTest, UuidInvalidVersion) { + const uint8_t uuid[16]{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x00, + 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + ASSERT_EQ(avifIsValidUUID(uuid), AVIF_FALSE); +} + +} // namespace +} // namespace avif