From 4b43b8dacc96fd538254e17a69abc9745c3a2ed4 Mon Sep 17 00:00:00 2001
From: Sebastian Pipping <sebastian@pipping.org>
Date: Fri, 19 Sep 2025 23:32:46 +0200
Subject: [PATCH] lib: Fix alignment of internal allocations for some non-amd64
 architectures

sparc32 is known to be affected.

CVE: CVE-2025-59375
Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/4b43b8dacc96fd538254e17a69abc9745c3a2ed4]
Signed-off-by: Peter Marko <peter.marko@siemens.com>
---
 lib/internal.h      |  6 ++++++
 lib/xmlparse.c      | 38 ++++++++++++++++++++++----------------
 tests/alloc_tests.c | 13 ++++++++++---
 3 files changed, 38 insertions(+), 19 deletions(-)

diff --git a/lib/internal.h b/lib/internal.h
index 6e087858..8f5edf48 100644
--- a/lib/internal.h
+++ b/lib/internal.h
@@ -108,6 +108,7 @@
 #endif
 
 #include <limits.h> // ULONG_MAX
+#include <stddef.h> // size_t
 
 #if defined(_WIN32)                                                            \
     && (! defined(__USE_MINGW_ANSI_STDIO)                                      \
@@ -150,6 +151,11 @@
 #define EXPAT_ALLOC_TRACKER_ACTIVATION_THRESHOLD_DEFAULT                       \
   67108864 // 64 MiB, 2^26
 
+// NOTE: If function expat_alloc was user facing, EXPAT_MALLOC_ALIGNMENT would
+//       have to take sizeof(long double) into account
+#define EXPAT_MALLOC_ALIGNMENT sizeof(long long) // largest parser (sub)member
+#define EXPAT_MALLOC_PADDING ((EXPAT_MALLOC_ALIGNMENT) - sizeof(size_t))
+
 /* NOTE END */
 
 #include "expat.h" // so we can use type XML_Parser below
diff --git a/lib/xmlparse.c b/lib/xmlparse.c
index 24fd7b97..ce29ab6f 100644
--- a/lib/xmlparse.c
+++ b/lib/xmlparse.c
@@ -850,14 +850,14 @@ static void *
 #  endif
 expat_malloc(XML_Parser parser, size_t size, int sourceLine) {
   // Detect integer overflow
-  if (SIZE_MAX - size < sizeof(size_t)) {
+  if (SIZE_MAX - size < sizeof(size_t) + EXPAT_MALLOC_PADDING) {
     return NULL;
   }
 
   const XML_Parser rootParser = getRootParserOf(parser, NULL);
   assert(rootParser->m_parentParser == NULL);
 
-  const size_t bytesToAllocate = sizeof(size_t) + size;
+  const size_t bytesToAllocate = sizeof(size_t) + EXPAT_MALLOC_PADDING + size;
 
   if ((XmlBigCount)-1 - rootParser->m_alloc_tracker.bytesAllocated
       < bytesToAllocate) {
@@ -894,7 +894,7 @@ expat_malloc(XML_Parser parser, size_t size, int sourceLine) {
                     rootParser->m_alloc_tracker.peakBytesAllocated, sourceLine);
   }
 
-  return (char *)mallocedPtr + sizeof(size_t);
+  return (char *)mallocedPtr + sizeof(size_t) + EXPAT_MALLOC_PADDING;
 }
 
 #  if defined(XML_TESTING)
@@ -914,8 +914,9 @@ expat_free(XML_Parser parser, void *ptr, int sourceLine) {
 
   // Extract size (to the eyes of malloc_fcn/realloc_fcn) and
   // the original pointer returned by malloc/realloc
-  void *const mallocedPtr = (char *)ptr - sizeof(size_t);
-  const size_t bytesAllocated = sizeof(size_t) + *(size_t *)mallocedPtr;
+  void *const mallocedPtr = (char *)ptr - EXPAT_MALLOC_PADDING - sizeof(size_t);
+  const size_t bytesAllocated
+      = sizeof(size_t) + EXPAT_MALLOC_PADDING + *(size_t *)mallocedPtr;
 
   // Update accounting
   assert(rootParser->m_alloc_tracker.bytesAllocated >= bytesAllocated);
@@ -954,7 +955,7 @@ expat_realloc(XML_Parser parser, void *ptr, size_t size, int sourceLine) {
 
   // Extract original size (to the eyes of the caller) and the original
   // pointer returned by malloc/realloc
-  void *mallocedPtr = (char *)ptr - sizeof(size_t);
+  void *mallocedPtr = (char *)ptr - EXPAT_MALLOC_PADDING - sizeof(size_t);
   const size_t prevSize = *(size_t *)mallocedPtr;
 
   // Classify upcoming change
@@ -971,10 +972,11 @@ expat_realloc(XML_Parser parser, void *ptr, size_t size, int sourceLine) {
 
   // NOTE: Integer overflow detection has already been done for us
   //       by expat_heap_increase_tolerable(..) above
-  assert(SIZE_MAX - sizeof(size_t) >= size);
+  assert(SIZE_MAX - sizeof(size_t) - EXPAT_MALLOC_PADDING >= size);
 
   // Actually allocate
-  mallocedPtr = parser->m_mem.realloc_fcn(mallocedPtr, sizeof(size_t) + size);
+  mallocedPtr = parser->m_mem.realloc_fcn(
+      mallocedPtr, sizeof(size_t) + EXPAT_MALLOC_PADDING + size);
 
   if (mallocedPtr == NULL) {
     return NULL;
@@ -1005,7 +1007,7 @@ expat_realloc(XML_Parser parser, void *ptr, size_t size, int sourceLine) {
   // Update in-block recorded size
   *(size_t *)mallocedPtr = size;
 
-  return (char *)mallocedPtr + sizeof(size_t);
+  return (char *)mallocedPtr + sizeof(size_t) + EXPAT_MALLOC_PADDING;
 }
 #endif // XML_GE == 1
 
@@ -1337,7 +1339,8 @@ parserCreate(const XML_Char *encodingName,
   XML_Parser parser = NULL;
 
 #if XML_GE == 1
-  const size_t increase = sizeof(size_t) + sizeof(struct XML_ParserStruct);
+  const size_t increase
+      = sizeof(size_t) + EXPAT_MALLOC_PADDING + sizeof(struct XML_ParserStruct);
 
   if (parentParser != NULL) {
     const XML_Parser rootParser = getRootParserOf(parentParser, NULL);
@@ -1352,11 +1355,13 @@ parserCreate(const XML_Char *encodingName,
   if (memsuite) {
     XML_Memory_Handling_Suite *mtemp;
 #if XML_GE == 1
-    void *const sizeAndParser = memsuite->malloc_fcn(
-        sizeof(size_t) + sizeof(struct XML_ParserStruct));
+    void *const sizeAndParser
+        = memsuite->malloc_fcn(sizeof(size_t) + EXPAT_MALLOC_PADDING
+                               + sizeof(struct XML_ParserStruct));
     if (sizeAndParser != NULL) {
       *(size_t *)sizeAndParser = sizeof(struct XML_ParserStruct);
-      parser = (XML_Parser)((char *)sizeAndParser + sizeof(size_t));
+      parser = (XML_Parser)((char *)sizeAndParser + sizeof(size_t)
+                            + EXPAT_MALLOC_PADDING);
 #else
     parser = memsuite->malloc_fcn(sizeof(struct XML_ParserStruct));
     if (parser != NULL) {
@@ -1369,11 +1374,12 @@ parserCreate(const XML_Char *encodingName,
   } else {
     XML_Memory_Handling_Suite *mtemp;
 #if XML_GE == 1
-    void *const sizeAndParser
-        = malloc(sizeof(size_t) + sizeof(struct XML_ParserStruct));
+    void *const sizeAndParser = malloc(sizeof(size_t) + EXPAT_MALLOC_PADDING
+                                       + sizeof(struct XML_ParserStruct));
     if (sizeAndParser != NULL) {
       *(size_t *)sizeAndParser = sizeof(struct XML_ParserStruct);
-      parser = (XML_Parser)((char *)sizeAndParser + sizeof(size_t));
+      parser = (XML_Parser)((char *)sizeAndParser + sizeof(size_t)
+                            + EXPAT_MALLOC_PADDING);
 #else
     parser = malloc(sizeof(struct XML_ParserStruct));
     if (parser != NULL) {
diff --git a/tests/alloc_tests.c b/tests/alloc_tests.c
index 644a4952..dabdf0da 100644
--- a/tests/alloc_tests.c
+++ b/tests/alloc_tests.c
@@ -2091,6 +2091,13 @@ START_TEST(test_alloc_reset_after_external_entity_parser_create_fail) {
 }
 END_TEST
 
+#if XML_GE == 1
+static size_t
+sizeRecordedFor(void *ptr) {
+  return *(size_t *)((char *)ptr - EXPAT_MALLOC_PADDING - sizeof(size_t));
+}
+#endif // XML_GE == 1
+
 START_TEST(test_alloc_tracker_size_recorded) {
   XML_Memory_Handling_Suite memsuite = {malloc, realloc, free};
 
@@ -2106,16 +2113,16 @@ START_TEST(test_alloc_tracker_size_recorded) {
     void *ptr = expat_malloc(parser, 10, -1);
 
     assert_true(ptr != NULL);
-    assert_true(*((size_t *)ptr - 1) == 10);
+    assert_true(sizeRecordedFor(ptr) == 10);
 
     assert_true(expat_realloc(parser, ptr, SIZE_MAX / 2, -1) == NULL);
 
-    assert_true(*((size_t *)ptr - 1) == 10); // i.e. unchanged
+    assert_true(sizeRecordedFor(ptr) == 10); // i.e. unchanged
 
     ptr = expat_realloc(parser, ptr, 20, -1);
 
     assert_true(ptr != NULL);
-    assert_true(*((size_t *)ptr - 1) == 20);
+    assert_true(sizeRecordedFor(ptr) == 20);
 
     expat_free(parser, ptr, -1);
 #endif
