After an evaluation, GNOME has moved from Bugzilla to GitLab. Learn more about GitLab.
No new issues can be reported in GNOME Bugzilla anymore.
To report an issue in a GNOME project, go to GNOME GitLab.
Do not go to GNOME Gitlab for: Bluefish, Doxygen, GnuCash, GStreamer, java-gnome, LDTP, NetworkManager, Tomboy.
Bug 775200 - (CVE-2017-8872) global-buffer-overflow in htmlParseTryOrFinish (HTMLparser.c:5403)
(CVE-2017-8872)
global-buffer-overflow in htmlParseTryOrFinish (HTMLparser.c:5403)
Status: RESOLVED FIXED
Product: libxml2
Classification: Platform
Component: htmlparser
git master
Other Linux
: Normal normal
: ---
Assigned To: Daniel Veillard
libxml QA maintainers
Depends on:
Blocks:
 
 
Reported: 2016-11-28 04:12 UTC by deng yi
Modified: 2019-01-30 10:30 UTC
See Also:
GNOME target: ---
GNOME version: ---


Attachments
poc (765 bytes, text/plain)
2016-11-28 04:12 UTC, deng yi
  Details
libxml2-CVE-2017-8872.patch (422 bytes, patch)
2017-05-15 15:53 UTC, Marcus Meissner
none Details | Review
xx.pat (413 bytes, patch)
2017-07-13 15:31 UTC, Marcus Meissner
none Details | Review
Out-of-bounds read in htmlParseTryOrFinish (CVE-2017-8872) (729 bytes, patch)
2018-01-02 12:32 UTC, Salvatore Bonaccorso
none Details | Review

Description deng yi 2016-11-28 04:12:24 UTC
Created attachment 340871 [details]
poc

found with AFL+AddressSanitizer:
./configure --disable-shared
AFL_USE_ASAN=1 make clean all
xmllint --html --push poc

ERROR: AddressSanitizer: global-buffer-overflow on address 0x088052c1 at pc 0x082f55b2 bp 0xbfd06418 sp 0xbfd06408
READ of size 1 at 0x088052c1 thread T0
    #0 0x82f55b1 in htmlParseTryOrFinish /vagrant/libxml2/HTMLparser.c:5403
    #1 0x82f55b1 in htmlParseChunk /vagrant/libxml2/HTMLparser.c:6148
    #2 0x805e1dd in parseAndPrintFile /vagrant/libxml2/xmllint.c:2222
    #3 0x80519d1 in main /vagrant/libxml2/xmllint.c:3767
    #4 0xb6ffa636 in __libc_start_main (/lib/i386-linux-gnu/libc.so.6+0x18636)
    #5 0x805765b  (/vagrant/libxml2/xmllint+0x805765b)

I gdb program and found
int nodePush(xmlParserCtxtPtr ctxt, xmlNodePtr value)
{
    ...

    if ((((unsigned int) ctxt->nodeNr) > xmlParserMaxDepth) &&
        ((ctxt->options & XML_PARSE_HUGE) == 0)) {
	xmlFatalErrMsgInt(ctxt, XML_ERR_INTERNAL_ERROR,
		 "Excessive depth in document: %d use XML_PARSE_HUGE option\n",
			  xmlParserMaxDepth);
	xmlHaltParser(ctxt);
	return(-1);
    }
    ...
}

when xmlParserMaxDepth reached, xmlHaltParser was called, it set ctxt->input->base = ctxt->input->cur, then htmlParseTryOrFinish compute avail = xmlBufUse(in->buf->buffer) - (in->cur - in->base),cause overflow
Comment 1 Marcus Meissner 2017-05-12 12:07:54 UTC
HTMLparser.c:



        if (avail < 1)
            goto done;
        cur = in->cur[0];
        if (cur == 0) {
            SKIP(1);
            continue;
        }

The problem is that xmlHaltParser sets cur/base to a different pointer not associated with in->buf->buffer , so the "avail" calculation is pretty much broken as soon as that happens.

the         switch (ctxt->instate) {
should be done somehow befoer continuing to access in->cur
Comment 2 Marcus Meissner 2017-05-12 12:14:14 UTC
(I can confirm the issue, but the overflow will only work as long as it reads 0 bytes.)
Comment 3 Marcus Meissner 2017-05-15 15:53:46 UTC
Created attachment 351897 [details] [review]
libxml2-CVE-2017-8872.patch

perhaps this patch?
Comment 4 Dominic Cooney 2017-05-21 23:35:08 UTC
Re: comment 3, how is that effectively different to the

if (avail < 1)
  goto done;

statement right after? In Comment 2 it sounds like avail is incorrect before that.
Comment 5 Marcus Meissner 2017-05-22 05:50:12 UTC
No, xmlHaltParser sets cur and base to the same value, so avail will be the full buffer size again and not 0 ... 

(It is this snippet in parser.c::xmlHaltParser)
        ctxt->input->cur = BAD_CAST"";
        ctxt->input->base = ctxt->input->cur;

this kind of 0 skipping needs to take the parser state in account :/
Comment 6 Dominic Cooney 2017-05-25 05:39:38 UTC
I'm sorry but I still don't understand.

I think with more context around your patch, there's this:

	if ((avail == 0) && (terminate)) {
	    htmlAutoCloseOnEnd(ctxt);
	    if ((ctxt->nameNr == 0) && (ctxt->instate != XML_PARSER_EOF)) {
		/*
		 * SAX: end of the document processing.
		 */
		ctxt->instate = XML_PARSER_EOF;
		if ((ctxt->sax) && (ctxt->sax->endDocument != NULL))
		    ctxt->sax->endDocument(ctxt->userData);

>               goto done;
	    }
	}
        if (avail < 1)
	    goto done;

Either avail == 0 or avail != 0.

If avail == 0, then also avail < 1 and that last goto statement will run.

If avail != 0, then the goto statement you are adding won't run because of the if ((avail == 0) && ... statement that surrounds it.
Comment 7 Marcus Meissner 2017-07-13 15:31:07 UTC
Created attachment 355527 [details] [review]
xx.pat

this makes xmlHaltParser "empty" the buffer, as it resets cur and avail too here.

this seems to cure this specific issue, and also passes the testsuite
Comment 8 Salvatore Bonaccorso 2017-12-14 19:31:45 UTC
Any updates on this issue?
Comment 9 Salvatore Bonaccorso 2018-01-02 12:32:21 UTC
Created attachment 366193 [details] [review]
Out-of-bounds read in htmlParseTryOrFinish (CVE-2017-8872)

Hi

The issue is not yet fixed up to master (as per ad88b54f1a28a8565964a370b5d387927b633c0d) and the patch from Marcus in Comment #7 does not apply anymore cleanly as Mattia Rizzolo correctly noted in the Debian Bug https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=862450#22.

Attaching a rebased version of the patch from Marcus.

Regards,
Salvatore
Comment 10 changqing.li 2018-08-01 05:12:17 UTC
with attachment 366193 [details] [review], one regression tests failed. remove this patch, 
all test can pass. Please take a look, thanks.


## Error cases regression tests
file result/errors/759573.xml.err is 1983 bytes, result is 1557 bytes
Error for ./test/errors/759573.xml failed
File ./test/errors/759573.xml generated an error
FAIL: Error cases regression tests


Below is the difference of result/errors/759573.xml.err and actual error
we get(I save it in tmp.err). I guess maybe some error msg is missed since
cut the buffer not properly.


--- result/errors/759573.xml.err
+++ tmp.err
@@ -21,14 +21,11 @@
             ^
 ./test/errors/759573.xml:1: parser error : internal error: xmlParseInternalSubset: error detected in Markup declaration
 
-<?h?><!DOCTYPEt[<!ELEMENT t (A)><!ENTITY % xx '&#37;<![INCLUDE[000&#37;&#3000;00
-     ^
+
+^
 ./test/errors/759573.xml:1: parser error : DOCTYPE improperly terminated
-<?h?><!DOCTYPEt[<!ELEMENT t (A)><!ENTITY % xx '&#37;<![INCLUDE[000&#37;&#3000;00
-     ^
-./test/errors/759573.xml:1: parser error : StartTag: invalid element name
-<?h?><!DOCTYPEt[<!ELEMENT t (A)><!ENTITY % xx '&#37;<![INCLUDE[000&#37;&#3000;00
-      ^
-./test/errors/759573.xml:1: parser error : Extra content at the end of the document
-<?h?><!DOCTYPEt[<!ELEMENT t (A)><!ENTITY % xx '&#37;<![INCLUDE[000&#37;&#3000;00
-      ^
+
+^
+./test/errors/759573.xml:1: parser error : Start tag expected, '<' not found
+
+^
Comment 11 Ross Burton 2018-10-03 16:38:21 UTC
Looks like https://gitlab.gnome.org/GNOME/libxml2/issues/26 is the same as this, and was merged.
Comment 12 Nick Wellnhofer 2019-01-30 10:30:33 UTC
Fixed with:

https://gitlab.gnome.org/GNOME/libxml2/commit/123234f2cfcd9e9b9f83047eee1dc17b4c3f4407

I wasn't aware of this bug report, but the fix is nearly identical.