expat/backport-007-CVE-2024-8176.patch
2025-03-29 17:10:03 +08:00

250 lines
10 KiB
Diff

From fabae41d28399e1d1623bde9ebd300c962e62ebe Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Berkay=20Eren=20=C3=9Cr=C3=BCn?= <berkay.ueruen@siemens.com>
Date: Wed, 18 Sep 2024 13:52:06 +0200
Subject: [PATCH 7/7] Fix storeEntityValue recursion
Reference: https://github.com/libexpat/libexpat/pull/973/commits/fabae41d28399e1d1623bde9ebd300c962e62ebe
Conflict: adapt doProlog
---
lib/xmlparse.c | 153 ++++++++++++++++++++++++++++++++++++++++++++-----
1 file changed, 140 insertions(+), 13 deletions(-)
diff --git a/lib/xmlparse.c b/lib/xmlparse.c
index 8f556d9..6c81713 100644
--- a/lib/xmlparse.c
+++ b/lib/xmlparse.c
@@ -459,6 +459,10 @@ static enum XML_Error processInternalEntity(XML_Parser parser, ENTITY *entity,
XML_Bool betweenDecl);
static enum XML_Error processAttributeEntity(XML_Parser parser, ENTITY *entity,
XML_Bool betweenDecl);
+#ifdef XML_DTD
+static enum XML_Error processValueEntity(XML_Parser parser, ENTITY *entity,
+ XML_Bool betweenDecl);
+#endif /* XML_DTD */
static enum XML_Error doContent(XML_Parser parser, int startTagLevel,
const ENCODING *enc, const char *start,
const char *end, const char **endPtr,
@@ -500,6 +504,10 @@ static enum XML_Error storeEntityValue(XML_Parser parser, const ENCODING *enc,
const char *start, const char *end,
enum XML_Account account,
const char **nextPtr);
+static enum XML_Error callStoreEntityValue(XML_Parser parser,
+ const ENCODING *enc,
+ const char *start, const char *end,
+ enum XML_Account account);
#else
static enum XML_Error storeSelfEntityValue(XML_Parser parser, ENTITY *entity);
#endif
@@ -670,6 +678,8 @@ struct XML_ParserStruct {
OPEN_INTERNAL_ENTITY *m_freeInternalEntities;
OPEN_INTERNAL_ENTITY *m_openAttributeEntities;
OPEN_INTERNAL_ENTITY *m_freeAttributeEntities;
+ OPEN_INTERNAL_ENTITY *m_openValueEntities;
+ OPEN_INTERNAL_ENTITY *m_freeValueEntities;
XML_Bool m_defaultExpandInternalEntities;
int m_tagLevel;
ENTITY *m_declEntity;
@@ -1109,6 +1119,7 @@ parserCreate(const XML_Char *encodingName,
parser->m_freeTagList = NULL;
parser->m_freeInternalEntities = NULL;
parser->m_freeAttributeEntities = NULL;
+ parser->m_freeValueEntities = NULL;
parser->m_groupSize = 0;
parser->m_groupConnector = NULL;
@@ -1211,6 +1222,7 @@ parserInit(XML_Parser parser, const XML_Char *encodingName) {
parser->m_positionPtr = NULL;
parser->m_openInternalEntities = NULL;
parser->m_openAttributeEntities = NULL;
+ parser->m_openValueEntities = NULL;
parser->m_defaultExpandInternalEntities = XML_TRUE;
parser->m_tagLevel = 0;
parser->m_tagStack = NULL;
@@ -1291,6 +1303,15 @@ XML_ParserReset(XML_Parser parser, const XML_Char *encodingName) {
openEntity->next = parser->m_freeAttributeEntities;
parser->m_freeAttributeEntities = openEntity;
}
+ /* move m_openValueEntities to m_freeValueEntities (i.e. same task but
+ * for value entities) */
+ openEntityList = parser->m_openValueEntities;
+ while (openEntityList) {
+ OPEN_INTERNAL_ENTITY *openEntity = openEntityList;
+ openEntityList = openEntity->next;
+ openEntity->next = parser->m_freeValueEntities;
+ parser->m_freeValueEntities = openEntity;
+ }
moveToFreeBindingList(parser, parser->m_inheritedBindings);
FREE(parser, parser->m_unknownEncodingMem);
if (parser->m_unknownEncodingRelease)
@@ -1561,6 +1582,20 @@ XML_ParserFree(XML_Parser parser) {
entityList = entityList->next;
FREE(parser, openEntity);
}
+ /* free m_openValueEntities and m_freeValueEntities */
+ entityList = parser->m_openValueEntities;
+ for (;;) {
+ OPEN_INTERNAL_ENTITY *openEntity;
+ if (entityList == NULL) {
+ if (parser->m_freeValueEntities == NULL)
+ break;
+ entityList = parser->m_freeValueEntities;
+ parser->m_freeValueEntities = NULL;
+ }
+ openEntity = entityList;
+ entityList = entityList->next;
+ FREE(parser, openEntity);
+ }
destroyBindings(parser->m_freeBindingList, parser);
destroyBindings(parser->m_inheritedBindings, parser);
poolDestroy(&parser->m_tempPool);
@@ -5165,9 +5200,9 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end,
#if defined(XML_DTD) || XML_GE == 1
// This will store the given replacement text in
// parser->m_declEntity->textPtr.
- enum XML_Error result
- = storeEntityValue(parser, enc, s + enc->minBytesPerChar,
- next - enc->minBytesPerChar, XML_ACCOUNT_NONE, NULL);
+ enum XML_Error result = callStoreEntityValue(
+ parser, enc, s + enc->minBytesPerChar, next - enc->minBytesPerChar,
+ XML_ACCOUNT_NONE);
if (parser->m_declEntity) {
parser->m_declEntity->textPtr = poolStart(&dtd->entityValuePool);
parser->m_declEntity->textLen
@@ -5951,6 +5986,38 @@ processAttributeEntity(XML_Parser parser, ENTITY *entity,
return XML_ERROR_NONE;
}
+/* KEEP IN SYNC with processInternalEntity */
+#ifdef XML_DTD
+static enum XML_Error
+processValueEntity(XML_Parser parser, ENTITY *entity, XML_Bool betweenDecl) {
+ OPEN_INTERNAL_ENTITY *openEntity;
+
+ if (parser->m_freeValueEntities) {
+ openEntity = parser->m_freeValueEntities;
+ parser->m_freeValueEntities = openEntity->next;
+ } else {
+ openEntity
+ = (OPEN_INTERNAL_ENTITY *)MALLOC(parser, sizeof(OPEN_INTERNAL_ENTITY));
+ if (! openEntity)
+ return XML_ERROR_NO_MEMORY;
+ }
+ entity->open = XML_TRUE;
+# if XML_GE == 1
+ entityTrackingOnOpen(parser, entity, __LINE__);
+# endif
+ entity->processed = 0;
+ openEntity->next = parser->m_openValueEntities;
+ parser->m_openValueEntities = openEntity;
+ openEntity->entity = entity;
+ openEntity->startTagLevel = parser->m_tagLevel;
+ openEntity->betweenDecl = betweenDecl;
+ openEntity->internalEventPtr = NULL;
+ openEntity->internalEventEndPtr = NULL;
+
+ return XML_ERROR_NONE;
+}
+#endif /* XML_DTD */
+
static enum XML_Error PTRCALL
internalEntityProcessor(XML_Parser parser, const char *s, const char *end,
const char **nextPtr) {
@@ -6409,16 +6476,8 @@ storeEntityValue(XML_Parser parser, const ENCODING *enc,
} else
dtd->keepProcessing = dtd->standalone;
} else {
- entity->open = XML_TRUE;
- entityTrackingOnOpen(parser, entity, __LINE__);
- result = storeEntityValue(
- parser, parser->m_internalEncoding, (const char *)entity->textPtr,
- (const char *)(entity->textPtr + entity->textLen),
- XML_ACCOUNT_ENTITY_EXPANSION, NULL);
- entityTrackingOnClose(parser, entity, __LINE__);
- entity->open = XML_FALSE;
- if (result)
- goto endEntityValue;
+ result = processValueEntity(parser, entity, XML_FALSE);
+ goto endEntityValue;
}
break;
}
@@ -6513,6 +6572,74 @@ endEntityValue:
return result;
}
+static enum XML_Error
+callStoreEntityValue(XML_Parser parser, const ENCODING *enc,
+ const char *entityTextPtr, const char *entityTextEnd,
+ enum XML_Account account) {
+ const char *next = entityTextPtr;
+ enum XML_Error result = XML_ERROR_NONE;
+ while (1) {
+ if (! parser->m_openValueEntities) {
+ result
+ = storeEntityValue(parser, enc, next, entityTextEnd, account, &next);
+ } else {
+ OPEN_INTERNAL_ENTITY *const openEntity = parser->m_openValueEntities;
+ if (! openEntity)
+ return XML_ERROR_UNEXPECTED_STATE;
+
+ ENTITY *const entity = openEntity->entity;
+ const char *const textStart
+ = ((const char *)entity->textPtr) + entity->processed;
+ const char *const textEnd
+ = (const char *)(entity->textPtr + entity->textLen);
+ /* Set a safe default value in case 'next' does not get set */
+ const char *nextInEntity = textStart;
+ result = storeEntityValue(parser, parser->m_internalEncoding, textStart,
+ textEnd, XML_ACCOUNT_ENTITY_EXPANSION,
+ &nextInEntity);
+ if (result != XML_ERROR_NONE)
+ break;
+ // Check if entity is complete, if not, mark down how much of it is
+ // processed. A XML_SUSPENDED check here is not required as
+ // appendAttributeValue will never suspend the parser.
+ if (textEnd != nextInEntity) {
+ entity->processed = (int)(nextInEntity - (const char *)entity->textPtr);
+ continue;
+ }
+
+# if XML_GE == 1
+ entityTrackingOnClose(parser, entity, __LINE__);
+# endif
+ entity->open = XML_FALSE;
+ // Remove fully processed openEntity from open entity list.
+ // appendAttributeValue call can add maximum one new entity to
+ // m_openValueEntities since when a new entity is detected, parser
+ // will set the 'reenter' flag and return. Therefore openEntity is either
+ // the head or next to the new head.
+ if (parser->m_openValueEntities == openEntity) {
+ // No new entity detected during entity processing,
+ // openEntity is still the head.
+ parser->m_openValueEntities = parser->m_openValueEntities->next;
+ } else {
+ // New entity detected since list has a new head. openEntity is the
+ // second element.
+ parser->m_openValueEntities->next = openEntity->next;
+ }
+ /* put openEntity back in list of free instances */
+ openEntity->next = parser->m_freeValueEntities;
+ parser->m_freeValueEntities = openEntity;
+ }
+
+ // Break if an error occurred or there is nothing left to process
+ if (result
+ || (parser->m_openValueEntities == NULL && entityTextEnd == next)) {
+ break;
+ }
+ }
+
+ return result;
+}
+
#else /* XML_GE == 0 */
static enum XML_Error
--
2.33.0