[libxml2] Fix use-after-free with validating reader



commit 2af3c2a8b974cb5896cd3beb74561ba979de9f34
Author: Nick Wellnhofer <wellnhofer aevum de>
Date:   Mon Jun 8 12:49:51 2020 +0200

    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 3fd9aa4c..6ae6e922 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);


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]