Fix CVE-2017-{9049,9050}: https://bugzilla.gnome.org/show_bug.cgi?id=781205 (not yet public) https://bugzilla.gnome.org/show_bug.cgi?id=781361 (not yet public) https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-9049 https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-9050 http://www.openwall.com/lists/oss-security/2017/05/15/1 https://security-tracker.debian.org/tracker/CVE-2017-9049 https://security-tracker.debian.org/tracker/CVE-2017-9050 Patch copied from upstream source repository: https://git.gnome.org/browse/libxml2/commit/?id=e26630548e7d138d2c560844c43820b6767251e3 Changes to 'runtest.c' are removed since they introduce test failure when applying to libxml2 2.9.4 release tarball. From e26630548e7d138d2c560844c43820b6767251e3 Mon Sep 17 00:00:00 2001 From: Nick Wellnhofer Date: Mon, 5 Jun 2017 15:37:17 +0200 Subject: [PATCH] Fix handling of parameter-entity references MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There were two bugs where parameter-entity references could lead to an unexpected change of the input buffer in xmlParseNameComplex and xmlDictLookup being called with an invalid pointer. Percent sign in DTD Names ========================= The NEXTL macro used to call xmlParserHandlePEReference. When parsing "complex" names inside the DTD, this could result in entity expansion which created a new input buffer. The fix is to simply remove the call to xmlParserHandlePEReference from the NEXTL macro. This is safe because no users of the macro require expansion of parameter entities. - xmlParseNameComplex - xmlParseNCNameComplex - xmlParseNmtoken The percent sign is not allowed in names, which are grammatical tokens. - xmlParseEntityValue Parameter-entity references in entity values are expanded but this happens in a separate step in this function. - xmlParseSystemLiteral Parameter-entity references are ignored in the system literal. - xmlParseAttValueComplex - xmlParseCharDataComplex - xmlParseCommentComplex - xmlParsePI - xmlParseCDSect Parameter-entity references are ignored outside the DTD. - xmlLoadEntityContent This function is only called from xmlStringLenDecodeEntities and entities are replaced in a separate step immediately after the function call. This bug could also be triggered with an internal subset and double entity expansion. This fixes bug 766956 initially reported by Wei Lei and independently by Chromium's ClusterFuzz, Hanno Böck, and Marco Grassi. Thanks to everyone involved. xmlParseNameComplex with XML_PARSE_OLD10 ======================================== When parsing Names inside an expanded parameter entity with the XML_PARSE_OLD10 option, xmlParseNameComplex would call xmlGROW via the GROW macro if the input buffer was exhausted. At the end of the parameter entity's replacement text, this function would then call xmlPopInput which invalidated the input buffer. There should be no need to invoke GROW in this situation because the buffer is grown periodically every XML_PARSER_CHUNK_SIZE characters and, at least for UTF-8, in xmlCurrentChar. This also matches the code path executed when XML_PARSE_OLD10 is not set. This fixes bugs 781205 (CVE-2017-9049) and 781361 (CVE-2017-9050). Thanks to Marcel Böhme and Thuan Pham for the report. Additional hardening ==================== A separate check was added in xmlParseNameComplex to validate the buffer size. --- Makefile.am | 18 ++++++++++++++++++ parser.c | 18 ++++++++++-------- result/errors10/781205.xml | 0 result/errors10/781205.xml.err | 21 +++++++++++++++++++++ result/errors10/781361.xml | 0 result/errors10/781361.xml.err | 13 +++++++++++++ result/valid/766956.xml | 0 result/valid/766956.xml.err | 9 +++++++++ result/valid/766956.xml.err.rdr | 10 ++++++++++ runtest.c | 3 +++ test/errors10/781205.xml | 3 +++ test/errors10/781361.xml | 3 +++ test/valid/766956.xml | 2 ++ test/valid/dtds/766956.dtd | 2 ++ 14 files changed, 94 insertions(+), 8 deletions(-) create mode 100644 result/errors10/781205.xml create mode 100644 result/errors10/781205.xml.err create mode 100644 result/errors10/781361.xml create mode 100644 result/errors10/781361.xml.err create mode 100644 result/valid/766956.xml create mode 100644 result/valid/766956.xml.err create mode 100644 result/valid/766956.xml.err.rdr create mode 100644 test/errors10/781205.xml create mode 100644 test/errors10/781361.xml create mode 100644 test/valid/766956.xml create mode 100644 test/valid/dtds/766956.dtd diff --git a/Makefile.am b/Makefile.am index 6fc8ffa9..10e716a5 100644 --- a/Makefile.am +++ b/Makefile.am @@ -427,6 +427,24 @@ Errtests : xmllint$(EXEEXT) if [ -n "$$log" ] ; then echo $$name result ; echo "$$log" ; fi ; \ rm result.$$name error.$$name ; \ fi ; fi ; done) + @echo "## Error cases regression tests (old 1.0)" + -@(for i in $(srcdir)/test/errors10/*.xml ; do \ + name=`basename $$i`; \ + if [ ! -d $$i ] ; then \ + if [ ! -f $(srcdir)/result/errors10/$$name ] ; then \ + echo New test file $$name ; \ + $(CHECKER) $(top_builddir)/xmllint --oldxml10 $$i \ + 2> $(srcdir)/result/errors10/$$name.err \ + > $(srcdir)/result/errors10/$$name ; \ + grep "MORY ALLO" .memdump | grep -v "MEMORY ALLOCATED : 0"; \ + else \ + log=`$(CHECKER) $(top_builddir)/xmllint --oldxml10 $$i 2> error.$$name > result.$$name ; \ + grep "MORY ALLO" .memdump | grep -v "MEMORY ALLOCATED : 0"; \ + diff $(srcdir)/result/errors10/$$name result.$$name ; \ + diff $(srcdir)/result/errors10/$$name.err error.$$name` ; \ + if [ -n "$$log" ] ; then echo $$name result ; echo "$$log" ; fi ; \ + rm result.$$name error.$$name ; \ + fi ; fi ; done) @echo "## Error cases stream regression tests" -@(for i in $(srcdir)/test/errors/*.xml ; do \ name=`basename $$i`; \ diff --git a/parser.c b/parser.c index df2efa55..a175ac4e 100644 --- a/parser.c +++ b/parser.c @@ -2121,7 +2121,6 @@ static void xmlGROW (xmlParserCtxtPtr ctxt) { ctxt->input->line++; ctxt->input->col = 1; \ } else ctxt->input->col++; \ ctxt->input->cur += l; \ - if (*ctxt->input->cur == '%') xmlParserHandlePEReference(ctxt); \ } while (0) #define CUR_CHAR(l) xmlCurrentChar(ctxt, &l) @@ -3412,13 +3411,6 @@ xmlParseNameComplex(xmlParserCtxtPtr ctxt) { len += l; NEXTL(l); c = CUR_CHAR(l); - if (c == 0) { - count = 0; - GROW; - if (ctxt->instate == XML_PARSER_EOF) - return(NULL); - c = CUR_CHAR(l); - } } } if ((len > XML_MAX_NAME_LENGTH) && @@ -3426,6 +3418,16 @@ xmlParseNameComplex(xmlParserCtxtPtr ctxt) { xmlFatalErr(ctxt, XML_ERR_NAME_TOO_LONG, "Name"); return(NULL); } + if (ctxt->input->cur - ctxt->input->base < len) { + /* + * There were a couple of bugs where PERefs lead to to a change + * of the buffer. Check the buffer size to avoid passing an invalid + * pointer to xmlDictLookup. + */ + xmlFatalErr(ctxt, XML_ERR_INTERNAL_ERROR, + "unexpected change of input buffer"); + return (NULL); + } if ((*ctxt->input->cur == '\n') && (ctxt->input->cur[-1] == '\r')) return(xmlDictLookup(ctxt->dict, ctxt->input->cur - (len + 1), len)); return(xmlDictLookup(ctxt->dict, ctxt->input->cur - len, len)); diff --git a/result/errors10/781205.xml b/result/errors10/781205.xml new file mode 100644 index 00000000..e69de29b diff --git a/result/errors10/781205.xml.err b/result/errors10/781205.xml.err new file mode 100644 index 00000000..da15c3f7 --- /dev/null +++ b/result/errors10/781205.xml.err @@ -0,0 +1,21 @@ +Entity: line 1: parser error : internal error: xmlParseInternalSubset: error detected in Markup declaration + + %a; + ^ +Entity: line 1: +<:0000 +^ +Entity: line 1: parser error : DOCTYPE improperly terminated + %a; + ^ +Entity: line 1: +<:0000 +^ +namespace error : Failed to parse QName ':0000' + %a; + ^ +<:0000 + ^ +./test/errors10/781205.xml:4: parser error : Couldn't find end of Start Tag :0000 line 1 + +^ diff --git a/result/errors10/781361.xml b/result/errors10/781361.xml new file mode 100644 index 00000000..e69de29b diff --git a/result/errors10/781361.xml.err b/result/errors10/781361.xml.err new file mode 100644 index 00000000..655f41a2 --- /dev/null +++ b/result/errors10/781361.xml.err @@ -0,0 +1,13 @@ +./test/errors10/781361.xml:4: parser error : xmlParseElementDecl: 'EMPTY', 'ANY' or '(' expected + +^ +./test/errors10/781361.xml:4: parser error : internal error: xmlParseInternalSubset: error detected in Markup declaration + + +^ +./test/errors10/781361.xml:4: parser error : DOCTYPE improperly terminated + +^ +./test/errors10/781361.xml:4: parser error : Start tag expected, '<' not found + +^ diff --git a/result/valid/766956.xml b/result/valid/766956.xml new file mode 100644 index 00000000..e69de29b diff --git a/result/valid/766956.xml.err b/result/valid/766956.xml.err new file mode 100644 index 00000000..34b1dae6 --- /dev/null +++ b/result/valid/766956.xml.err @@ -0,0 +1,9 @@ +test/valid/dtds/766956.dtd:2: parser error : PEReference: expecting ';' +%ä%ent; + ^ +Entity: line 1: parser error : Content error in the external subset + %ent; + ^ +Entity: line 1: +value +^ diff --git a/result/valid/766956.xml.err.rdr b/result/valid/766956.xml.err.rdr new file mode 100644 index 00000000..77603462 --- /dev/null +++ b/result/valid/766956.xml.err.rdr @@ -0,0 +1,10 @@ +test/valid/dtds/766956.dtd:2: parser error : PEReference: expecting ';' +%ä%ent; + ^ +Entity: line 1: parser error : Content error in the external subset + %ent; + ^ +Entity: line 1: +value +^ +./test/valid/766956.xml : failed to parse diff --git a/test/errors10/781205.xml b/test/errors10/781205.xml new file mode 100644 index 00000000..d9e9e839 --- /dev/null +++ b/test/errors10/781205.xml @@ -0,0 +1,3 @@ + + %a; diff --git a/test/errors10/781361.xml b/test/errors10/781361.xml new file mode 100644 index 00000000..67476bcb --- /dev/null +++ b/test/errors10/781361.xml @@ -0,0 +1,3 @@ + + %elem; diff --git a/test/valid/766956.xml b/test/valid/766956.xml new file mode 100644 index 00000000..19a95a0e --- /dev/null +++ b/test/valid/766956.xml @@ -0,0 +1,2 @@ + + diff --git a/test/valid/dtds/766956.dtd b/test/valid/dtds/766956.dtd new file mode 100644 index 00000000..dddde68b --- /dev/null +++ b/test/valid/dtds/766956.dtd @@ -0,0 +1,2 @@ + +%ä%ent; -- 2.14.1