[libxml2/fix-ownership-of-xmlAttrPtr-name-in-xmlSetTreeDoc] Fix use-after-free bugs when calling xmlTextReaderClose() before xmlFreeTextReader() on post-validat
- From: David Kilzer <ddkilzer src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [libxml2/fix-ownership-of-xmlAttrPtr-name-in-xmlSetTreeDoc] Fix use-after-free bugs when calling xmlTextReaderClose() before xmlFreeTextReader() on post-validat
- Date: Wed, 18 May 2022 01:22:39 +0000 (UTC)
commit bc977ef53eee21273405ddffd1a220629b73bc1b
Author: David Kilzer <ddkilzer apple com>
Date: Sun Apr 10 20:02:47 2022 -0700
Fix use-after-free bugs when calling xmlTextReaderClose() before xmlFreeTextReader() on post-validating
parser
When creating an xmlTextReaderPtr using xmlReaderForMemory(),
there are two optional API functions that can be used:
- xmlTextReaderClose() may be called prior to calling
xmlFreeTextReader() to free parsing resources and close the
xmlTextReaderPtr without freeing it.
- xmlTextReaderCurrentDoc() may be called to return an
xmlDocPtr that's owned by the caller, and must be free using
xmlFreeDoc() after calling xmlFreeTextReader().
The use-after-free issues occur when calling
xmlTextReaderClose() before xmlFreeTextReader(), with different
issues occurring depending on whether xmlTextReaderCurrentDoc()
is also called.
* fuzz/xml.c:
(LLVMFuzzerTestOneInput):
- Add support for testing xmlTextReaderCurrentDoc()/xmlFreeDoc()
and xmlTextReaderClose() with xmlFreeTextReader(). This
change finds the remaining bug from 2009 almost immediately.
* xmlreader.c:
(xmlFreeTextReader):
- Move code to xmlTextReaderClose(), remove duplicate code, and
call xmlTextReaderClose() if it hasn't been called yet.
(xmlTextReaderClose):
- Move call to xmlFreeNode(reader->faketext) from
xmlFreeTextReader() to fix a use-after-free bug when calling
xmlTextReaderClose() before xmlFreeTextReader(), but not when
using xmlTextReaderCurrentDoc(). The bug was introduced in
2002 by commit beb70bd39. In 2009 commit f4653dcd8 fixed the
use-after-free that occurred every time xmlFreeTextReader()
was called, but not the case where xmlTextReaderClose() was
called first.
- Move post-parsing validation code from xmlFreeTextReader() to
fix a second use-after-free when calling xmlTextReaderClose()
before xmlFreeTextReader(). This regressed in v2.9.10 with
commit 57a3af56f.
fuzz/xml.c | 7 +++++++
xmlreader.c | 40 ++++++++++++++++++----------------------
2 files changed, 25 insertions(+), 22 deletions(-)
---
diff --git a/fuzz/xml.c b/fuzz/xml.c
index 8b4c4efc..87a9fc55 100644
--- a/fuzz/xml.c
+++ b/fuzz/xml.c
@@ -29,6 +29,7 @@ int
LLVMFuzzerTestOneInput(const char *data, size_t size) {
static const size_t maxChunkSize = 128;
xmlDocPtr doc;
+ xmlDocPtr preservedReaderDoc = NULL;
xmlParserCtxtPtr ctxt;
xmlTextReaderPtr reader;
xmlChar *out;
@@ -94,7 +95,13 @@ LLVMFuzzerTestOneInput(const char *data, size_t size) {
}
}
}
+ if (size % 5 == 0)
+ preservedReaderDoc = xmlTextReaderCurrentDoc(reader);
+ if (size % 3 == 0)
+ xmlTextReaderClose(reader);
xmlFreeTextReader(reader);
+ if (size % 5 == 0 && preservedReaderDoc)
+ xmlFreeDoc(preservedReaderDoc);
exit:
xmlFuzzDataCleanup();
diff --git a/xmlreader.c b/xmlreader.c
index ba95813b..b3299ced 100644
--- a/xmlreader.c
+++ b/xmlreader.c
@@ -2181,36 +2181,16 @@ xmlFreeTextReader(xmlTextReaderPtr reader) {
xmlFree(reader->patternTab);
}
#endif
- if (reader->faketext != NULL) {
- xmlFreeNode(reader->faketext);
- }
+ if (reader->mode != XML_TEXTREADER_MODE_CLOSED)
+ xmlTextReaderClose(reader);
if (reader->ctxt != NULL) {
if (reader->dict == reader->ctxt->dict)
reader->dict = NULL;
-#ifdef LIBXML_VALID_ENABLED
- if ((reader->ctxt->vctxt.vstateTab != NULL) &&
- (reader->ctxt->vctxt.vstateMax > 0)){
-#ifdef LIBXML_REGEXP_ENABLED
- while (reader->ctxt->vctxt.vstateNr > 0)
- xmlValidatePopElement(&reader->ctxt->vctxt, NULL, NULL, NULL);
-#endif /* LIBXML_REGEXP_ENABLED */
- xmlFree(reader->ctxt->vctxt.vstateTab);
- reader->ctxt->vctxt.vstateTab = NULL;
- reader->ctxt->vctxt.vstateMax = 0;
- }
-#endif /* LIBXML_VALID_ENABLED */
- if (reader->ctxt->myDoc != NULL) {
- if (reader->preserve == 0)
- xmlTextReaderFreeDoc(reader, reader->ctxt->myDoc);
- reader->ctxt->myDoc = NULL;
- }
if (reader->allocs & XML_TEXTREADER_CTXT)
xmlFreeParserCtxt(reader->ctxt);
}
if (reader->sax != NULL)
xmlFree(reader->sax);
- if ((reader->input != NULL) && (reader->allocs & XML_TEXTREADER_INPUT))
- xmlFreeParserInputBuffer(reader->input);
if (reader->buffer != NULL)
xmlBufFree(reader->buffer);
if (reader->entTab != NULL)
@@ -2241,7 +2221,23 @@ xmlTextReaderClose(xmlTextReaderPtr reader) {
reader->node = NULL;
reader->curnode = NULL;
reader->mode = XML_TEXTREADER_MODE_CLOSED;
+ if (reader->faketext != NULL) {
+ xmlFreeNode(reader->faketext);
+ reader->faketext = NULL;
+ }
if (reader->ctxt != NULL) {
+#ifdef LIBXML_VALID_ENABLED
+ if ((reader->ctxt->vctxt.vstateTab != NULL) &&
+ (reader->ctxt->vctxt.vstateMax > 0)){
+#ifdef LIBXML_REGEXP_ENABLED
+ while (reader->ctxt->vctxt.vstateNr > 0)
+ xmlValidatePopElement(&reader->ctxt->vctxt, NULL, NULL, NULL);
+#endif /* LIBXML_REGEXP_ENABLED */
+ xmlFree(reader->ctxt->vctxt.vstateTab);
+ reader->ctxt->vctxt.vstateTab = NULL;
+ reader->ctxt->vctxt.vstateMax = 0;
+ }
+#endif /* LIBXML_VALID_ENABLED */
xmlStopParser(reader->ctxt);
if (reader->ctxt->myDoc != NULL) {
if (reader->preserve == 0)
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]