Guard JPEG XMP parsing sizes (#3249)
diff --git a/apps/shared/avifjpeg.c b/apps/shared/avifjpeg.c
index 4a557cb..c028575 100644
--- a/apps/shared/avifjpeg.c
+++ b/apps/shared/avifjpeg.c
@@ -7,6 +7,7 @@
 
 #include <assert.h>
 #include <ctype.h>
+#include <limits.h>
 #include <math.h>
 #include <setjmp.h>
 #include <stdint.h>
@@ -579,6 +580,14 @@
 #define XML_NAME_SPACE_RDF "http://www.w3.org/1999/02/22-rdf-syntax-ns#"
 #define XML_NAME_SPACE_XMP_NOTE "http://ns.adobe.com/xmp/note/"
 
+static xmlDoc * avifJPEGReadXMLMemory(const uint8_t * data, size_t size, const char * url)
+{
+    if (size > INT_MAX) {
+        return NULL;
+    }
+    return xmlReadMemory((const char *)data, (int)size, url, NULL, /*options=*/0);
+}
+
 // Finds an 'rdf:Description' node containing a gain map version attribute (hdrgm:Version="1.0").
 // Returns NULL if not found.
 static const xmlNode * avifJPEGFindIsoGainMapXMPNode(const xmlNode * rootNode)
@@ -664,7 +673,7 @@
 // If not null, isAppleGainMap is set to AVIF_TRUE for an Apple style gain map, and AVIF_FALSE for an ISO gain map.
 static avifBool avifJPEGHasGainMapXMPNode(const uint8_t * xmpData, size_t xmpSize, avifBool * isAppleGainMap)
 {
-    xmlDoc * document = xmlReadMemory((const char *)xmpData, (int)xmpSize, NULL, NULL, /*options=*/0);
+    xmlDoc * document = avifJPEGReadXMLMemory(xmpData, xmpSize, NULL);
     if (document == NULL) {
         return AVIF_FALSE; // Probably and out of memory error.
     }
@@ -885,7 +894,7 @@
 // Returns AVIF_TRUE if the gain map metadata was successfully read.
 avifBool avifJPEGParseGainMapXMP(const uint8_t * xmpData, size_t xmpSize, avifGainMap * gainMap, avifBool * isAppleGainMap)
 {
-    xmlDoc * document = xmlReadMemory((const char *)xmpData, (int)xmpSize, NULL, NULL, /*options=*/0);
+    xmlDoc * document = avifJPEGReadXMLMemory(xmpData, xmpSize, NULL);
     if (document == NULL) {
         return AVIF_FALSE; // Probably an out of memory error.
     }
@@ -1122,7 +1131,12 @@
     avifBool isValid = AVIF_TRUE;
     xmlDoc * extendedXMPDoc = NULL;
     xmlChar * xmlBuff = NULL;
-    xmlDoc * xmpDoc = xmlReadMemory((const char *)standardXMPData, (int)standardXMPSize, "standard.xml", NULL, /*options=*/0);
+    xmlDoc * xmpDoc = avifJPEGReadXMLMemory(standardXMPData, standardXMPSize, "standard.xml");
+    if (xmpDoc == NULL) {
+        fprintf(stderr, "XMP extraction failed: invalid standard XMP segment\n");
+        isValid = AVIF_FALSE;
+        goto cleanup_xml;
+    }
     xmlNode * xmpRdf = (xmlNode *)avifJPEGFindXMLNodeByName(xmlDocGetRootElement(xmpDoc),
                                                             XML_NAME_SPACE_RDF,
                                                             "RDF",
@@ -1174,11 +1188,12 @@
     }
 
     // Read the extended XMP.
-    extendedXMPDoc = xmlReadMemory((const char *)extendedXMP.data,
-                                   (int)extendedXMP.size,
-                                   "extended.xml",
-                                   NULL,
-                                   /*options=*/0);
+    extendedXMPDoc = avifJPEGReadXMLMemory(extendedXMP.data, extendedXMP.size, "extended.xml");
+    if (extendedXMPDoc == NULL) {
+        fprintf(stderr, "XMP extraction failed: invalid extended XMP segment\n");
+        isValid = AVIF_FALSE;
+        goto cleanup_xml;
+    }
     const xmlNode * extendedXMPRdf = avifJPEGFindXMLNodeByName(xmlDocGetRootElement(extendedXMPDoc),
                                                                XML_NAME_SPACE_RDF,
                                                                "RDF",
diff --git a/tests/gtest/avifjpeggainmaptest.cc b/tests/gtest/avifjpeggainmaptest.cc
index f3973b2..bb44231 100644
--- a/tests/gtest/avifjpeggainmaptest.cc
+++ b/tests/gtest/avifjpeggainmaptest.cc
@@ -3,6 +3,8 @@
 
 #include <math.h>
 
+#include <limits>
+
 #include "avif/avif.h"
 #include "avifjpeg.h"
 #include "aviftest_helpers.h"
@@ -333,6 +335,15 @@
                                        gain_map.get(), &is_avif_gain_map));
 }
 
+TEST(JpegTest, TooLargeXMP) {
+  const uint8_t xmp = 0;
+  GainMapPtr gain_map(avifGainMapCreate());
+  avifBool is_avif_gain_map;
+  EXPECT_FALSE(avifJPEGParseGainMapXMP(
+      &xmp, static_cast<size_t>(std::numeric_limits<int>::max()) + 1,
+      gain_map.get(), &is_avif_gain_map));
+}
+
 //------------------------------------------------------------------------------
 
 }  // namespace