92 lines
2.6 KiB
Diff
92 lines
2.6 KiB
Diff
|
|
From 2af3c2a8b974cb5896cd3beb74561ba979de9f34 Mon Sep 17 00:00:00 2001
|
||
|
|
From: Nick Wellnhofer <wellnhofer@aevum.de>
|
||
|
|
Date: Mon, 8 Jun 2020 12:49:51 +0200
|
||
|
|
Subject: [PATCH] Fix use-after-free with validating reader
|
||
|
|
|
||
|
|
Just like IDs, IDREF attributes must be removed from the document's
|
||
|
|
refs table when they're freed by a reader. This bug is often hidden
|
||
|
|
because xmlAttr structs are reused and strings are stored in a
|
||
|
|
dictionary unless XML_PARSE_NODICT is specified.
|
||
|
|
|
||
|
|
Found by OSS-Fuzz.
|
||
|
|
---
|
||
|
|
xmlreader.c | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||
|
|
1 file changed, 55 insertions(+)
|
||
|
|
|
||
|
|
diff --git a/xmlreader.c b/xmlreader.c
|
||
|
|
index 3fd9aa4c0..6ae6e9229 100644
|
||
|
|
--- a/xmlreader.c
|
||
|
|
+++ b/xmlreader.c
|
||
|
|
@@ -278,6 +278,59 @@ xmlTextReaderRemoveID(xmlDocPtr doc, xmlAttrPtr attr) {
|
||
|
|
return(0);
|
||
|
|
}
|
||
|
|
|
||
|
|
+/**
|
||
|
|
+ * xmlTextReaderWalkRemoveRef:
|
||
|
|
+ * @data: Contents of current link
|
||
|
|
+ * @user: Value supplied by the user
|
||
|
|
+ *
|
||
|
|
+ * Returns 0 to abort the walk or 1 to continue
|
||
|
|
+ */
|
||
|
|
+static int
|
||
|
|
+xmlTextReaderWalkRemoveRef(const void *data, void *user)
|
||
|
|
+{
|
||
|
|
+ xmlRefPtr ref = (xmlRefPtr)data;
|
||
|
|
+ xmlAttrPtr attr = (xmlAttrPtr)user;
|
||
|
|
+
|
||
|
|
+ if (ref->attr == attr) { /* Matched: remove and terminate walk */
|
||
|
|
+ ref->name = xmlStrdup(attr->name);
|
||
|
|
+ ref->attr = NULL;
|
||
|
|
+ return 0;
|
||
|
|
+ }
|
||
|
|
+ return 1;
|
||
|
|
+}
|
||
|
|
+
|
||
|
|
+/**
|
||
|
|
+ * xmlTextReaderRemoveRef:
|
||
|
|
+ * @doc: the document
|
||
|
|
+ * @attr: the attribute
|
||
|
|
+ *
|
||
|
|
+ * Remove the given attribute from the Ref table maintained internally.
|
||
|
|
+ *
|
||
|
|
+ * Returns -1 if the lookup failed and 0 otherwise
|
||
|
|
+ */
|
||
|
|
+static int
|
||
|
|
+xmlTextReaderRemoveRef(xmlDocPtr doc, xmlAttrPtr attr) {
|
||
|
|
+ xmlListPtr ref_list;
|
||
|
|
+ xmlRefTablePtr table;
|
||
|
|
+ xmlChar *ID;
|
||
|
|
+
|
||
|
|
+ if (doc == NULL) return(-1);
|
||
|
|
+ if (attr == NULL) return(-1);
|
||
|
|
+ table = (xmlRefTablePtr) doc->refs;
|
||
|
|
+ if (table == NULL)
|
||
|
|
+ return(-1);
|
||
|
|
+
|
||
|
|
+ ID = xmlNodeListGetString(doc, attr->children, 1);
|
||
|
|
+ if (ID == NULL)
|
||
|
|
+ return(-1);
|
||
|
|
+ ref_list = xmlHashLookup(table, ID);
|
||
|
|
+ xmlFree(ID);
|
||
|
|
+ if(ref_list == NULL)
|
||
|
|
+ return (-1);
|
||
|
|
+ xmlListWalk(ref_list, xmlTextReaderWalkRemoveRef, attr);
|
||
|
|
+ return(0);
|
||
|
|
+}
|
||
|
|
+
|
||
|
|
/**
|
||
|
|
* xmlTextReaderFreeProp:
|
||
|
|
* @reader: the xmlTextReaderPtr used
|
||
|
|
@@ -304,6 +357,8 @@ xmlTextReaderFreeProp(xmlTextReaderPtr reader, xmlAttrPtr cur) {
|
||
|
|
(cur->parent->doc->extSubset != NULL))) {
|
||
|
|
if (xmlIsID(cur->parent->doc, cur->parent, cur))
|
||
|
|
xmlTextReaderRemoveID(cur->parent->doc, cur);
|
||
|
|
+ if (xmlIsRef(cur->parent->doc, cur->parent, cur))
|
||
|
|
+ xmlTextReaderRemoveRef(cur->parent->doc, cur);
|
||
|
|
}
|
||
|
|
if (cur->children != NULL)
|
||
|
|
xmlTextReaderFreeNodeList(reader, cur->children);
|
||
|
|
--
|
||
|
|
GitLab
|
||
|
|
|