From 2af3c2a8b974cb5896cd3beb74561ba979de9f34 Mon Sep 17 00:00:00 2001 From: Nick Wellnhofer 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