1From 1b41ec4e9433b05bb0376be4725804c54ef1d80b Mon Sep 17 00:00:00 2001
2From: Nick Wellnhofer <wellnhofer@aevum.de>
3Date: Wed, 31 Aug 2022 22:11:25 +0200
4Subject: [PATCH] [CVE-2022-40304] Fix dict corruption caused by entity
5 reference cycles
6
7When an entity reference cycle is detected, the entity content is
8cleared by setting its first byte to zero. But the entity content might
9be allocated from a dict. In this case, the dict entry becomes corrupted
10leading to all kinds of logic errors, including memory errors like
11double-frees.
12
13Stop storing entity content, orig, ExternalID and SystemID in a dict.
14These values are unlikely to occur multiple times in a document, so they
15shouldn't have been stored in a dict in the first place.
16
17Thanks to Ned Williamson and Nathan Wachholz working with Google Project
18Zero for the report!
19---
20 entities.c | 55 ++++++++++++++++--------------------------------------
21 1 file changed, 16 insertions(+), 39 deletions(-)
22
23diff --git a/entities.c b/entities.c
24index 84435515..d4e5412e 100644
25--- a/entities.c
26+++ b/entities.c
27@@ -128,36 +128,19 @@ xmlFreeEntity(xmlEntityPtr entity)
28     if ((entity->children) && (entity->owner == 1) &&
29         (entity == (xmlEntityPtr) entity->children->parent))
30         xmlFreeNodeList(entity->children);
31-    if (dict != NULL) {
32-        if ((entity->name != NULL) && (!xmlDictOwns(dict, entity->name)))
33-            xmlFree((char *) entity->name);
34-        if ((entity->ExternalID != NULL) &&
35-	    (!xmlDictOwns(dict, entity->ExternalID)))
36-            xmlFree((char *) entity->ExternalID);
37-        if ((entity->SystemID != NULL) &&
38-	    (!xmlDictOwns(dict, entity->SystemID)))
39-            xmlFree((char *) entity->SystemID);
40-        if ((entity->URI != NULL) && (!xmlDictOwns(dict, entity->URI)))
41-            xmlFree((char *) entity->URI);
42-        if ((entity->content != NULL)
43-            && (!xmlDictOwns(dict, entity->content)))
44-            xmlFree((char *) entity->content);
45-        if ((entity->orig != NULL) && (!xmlDictOwns(dict, entity->orig)))
46-            xmlFree((char *) entity->orig);
47-    } else {
48-        if (entity->name != NULL)
49-            xmlFree((char *) entity->name);
50-        if (entity->ExternalID != NULL)
51-            xmlFree((char *) entity->ExternalID);
52-        if (entity->SystemID != NULL)
53-            xmlFree((char *) entity->SystemID);
54-        if (entity->URI != NULL)
55-            xmlFree((char *) entity->URI);
56-        if (entity->content != NULL)
57-            xmlFree((char *) entity->content);
58-        if (entity->orig != NULL)
59-            xmlFree((char *) entity->orig);
60-    }
61+    if ((entity->name != NULL) &&
62+        ((dict == NULL) || (!xmlDictOwns(dict, entity->name))))
63+        xmlFree((char *) entity->name);
64+    if (entity->ExternalID != NULL)
65+        xmlFree((char *) entity->ExternalID);
66+    if (entity->SystemID != NULL)
67+        xmlFree((char *) entity->SystemID);
68+    if (entity->URI != NULL)
69+        xmlFree((char *) entity->URI);
70+    if (entity->content != NULL)
71+        xmlFree((char *) entity->content);
72+    if (entity->orig != NULL)
73+        xmlFree((char *) entity->orig);
74     xmlFree(entity);
75 }
76 
77@@ -193,18 +176,12 @@ xmlCreateEntity(xmlDictPtr dict, const xmlChar *name, int type,
78 	    ret->SystemID = xmlStrdup(SystemID);
79     } else {
80         ret->name = xmlDictLookup(dict, name, -1);
81-	if (ExternalID != NULL)
82-	    ret->ExternalID = xmlDictLookup(dict, ExternalID, -1);
83-	if (SystemID != NULL)
84-	    ret->SystemID = xmlDictLookup(dict, SystemID, -1);
85+	ret->ExternalID = xmlStrdup(ExternalID);
86+	ret->SystemID = xmlStrdup(SystemID);
87     }
88     if (content != NULL) {
89         ret->length = xmlStrlen(content);
90-	if ((dict != NULL) && (ret->length < 5))
91-	    ret->content = (xmlChar *)
92-	                   xmlDictLookup(dict, content, ret->length);
93-	else
94-	    ret->content = xmlStrndup(content, ret->length);
95+	ret->content = xmlStrndup(content, ret->length);
96      } else {
97         ret->length = 0;
98         ret->content = NULL;
99-- 
1002.27.0
101
102