From be2a7edaf289c5da74a4f9ed3a0b6c733e775230 Mon Sep 17 00:00:00 2001 From: Daniel Veillard Date: Thu, 16 Oct 2014 13:59:47 +0800 Subject: Fix for CVE-2014-3660 Issues related to the billion laugh entity expansion which happened to escape the initial set of fixes Upstream-status: Backport Reference: https://git.gnome.org/browse/libxml2/commit/?id=be2a7edaf289c5da74a4f9ed3a0b6c733e775230&context=3&ignorews=0&ss=0 Signed-off-by: Joe MacDonald diff --git a/parser.c b/parser.c index f51e8d2..1d93967 100644 --- a/parser.c +++ b/parser.c @@ -130,6 +130,29 @@ xmlParserEntityCheck(xmlParserCtxtPtr ctxt, size_t size, return (0); if (ctxt->lastError.code == XML_ERR_ENTITY_LOOP) return (1); + + /* + * This may look absurd but is needed to detect + * entities problems + */ + if ((ent != NULL) && (ent->etype != XML_INTERNAL_PREDEFINED_ENTITY) && + (ent->content != NULL) && (ent->checked == 0)) { + unsigned long oldnbent = ctxt->nbentities; + xmlChar *rep; + + ent->checked = 1; + + rep = xmlStringDecodeEntities(ctxt, ent->content, + XML_SUBSTITUTE_REF, 0, 0, 0); + + ent->checked = (ctxt->nbentities - oldnbent + 1) * 2; + if (rep != NULL) { + if (xmlStrchr(rep, '<')) + ent->checked |= 1; + xmlFree(rep); + rep = NULL; + } + } if (replacement != 0) { if (replacement < XML_MAX_TEXT_LENGTH) return(0); @@ -189,9 +212,12 @@ xmlParserEntityCheck(xmlParserCtxtPtr ctxt, size_t size, return (0); } else { /* - * strange we got no data for checking just return + * strange we got no data for checking */ - return (0); + if (((ctxt->lastError.code != XML_ERR_UNDECLARED_ENTITY) && + (ctxt->lastError.code != XML_WAR_UNDECLARED_ENTITY)) || + (ctxt->nbentities <= 10000)) + return (0); } xmlFatalErr(ctxt, XML_ERR_ENTITY_LOOP, NULL); return (1); @@ -2589,6 +2615,7 @@ xmlParserHandlePEReference(xmlParserCtxtPtr ctxt) { name, NULL); ctxt->valid = 0; } + xmlParserEntityCheck(ctxt, 0, NULL, 0); } else if (ctxt->input->free != deallocblankswrapper) { input = xmlNewBlanksWrapperInputStream(ctxt, entity); if (xmlPushInput(ctxt, input) < 0) @@ -2759,6 +2786,7 @@ xmlStringLenDecodeEntities(xmlParserCtxtPtr ctxt, const xmlChar *str, int len, if ((ctxt->lastError.code == XML_ERR_ENTITY_LOOP) || (ctxt->lastError.code == XML_ERR_INTERNAL_ERROR)) goto int_error; + xmlParserEntityCheck(ctxt, 0, ent, 0); if (ent != NULL) ctxt->nbentities += ent->checked / 2; if ((ent != NULL) && @@ -2810,6 +2838,7 @@ xmlStringLenDecodeEntities(xmlParserCtxtPtr ctxt, const xmlChar *str, int len, ent = xmlParseStringPEReference(ctxt, &str); if (ctxt->lastError.code == XML_ERR_ENTITY_LOOP) goto int_error; + xmlParserEntityCheck(ctxt, 0, ent, 0); if (ent != NULL) ctxt->nbentities += ent->checked / 2; if (ent != NULL) { @@ -7312,6 +7341,7 @@ xmlParseReference(xmlParserCtxtPtr ctxt) { (ret != XML_WAR_UNDECLARED_ENTITY)) { xmlFatalErrMsgStr(ctxt, XML_ERR_UNDECLARED_ENTITY, "Entity '%s' failed to parse\n", ent->name); + xmlParserEntityCheck(ctxt, 0, ent, 0); } else if (list != NULL) { xmlFreeNodeList(list); list = NULL; @@ -7418,7 +7448,7 @@ xmlParseReference(xmlParserCtxtPtr ctxt) { /* * We are copying here, make sure there is no abuse */ - ctxt->sizeentcopy += ent->length; + ctxt->sizeentcopy += ent->length + 5; if (xmlParserEntityCheck(ctxt, 0, ent, ctxt->sizeentcopy)) return; @@ -7466,7 +7496,7 @@ xmlParseReference(xmlParserCtxtPtr ctxt) { /* * We are copying here, make sure there is no abuse */ - ctxt->sizeentcopy += ent->length; + ctxt->sizeentcopy += ent->length + 5; if (xmlParserEntityCheck(ctxt, 0, ent, ctxt->sizeentcopy)) return; @@ -7652,6 +7682,7 @@ xmlParseEntityRef(xmlParserCtxtPtr ctxt) { ctxt->sax->reference(ctxt->userData, name); } } + xmlParserEntityCheck(ctxt, 0, ent, 0); ctxt->valid = 0; } @@ -7845,6 +7876,7 @@ xmlParseStringEntityRef(xmlParserCtxtPtr ctxt, const xmlChar ** str) { "Entity '%s' not defined\n", name); } + xmlParserEntityCheck(ctxt, 0, ent, 0); /* TODO ? check regressions ctxt->valid = 0; */ } @@ -8004,6 +8036,7 @@ xmlParsePEReference(xmlParserCtxtPtr ctxt) name, NULL); ctxt->valid = 0; } + xmlParserEntityCheck(ctxt, 0, NULL, 0); } else { /* * Internal checking in case the entity quest barfed @@ -8243,6 +8276,7 @@ xmlParseStringPEReference(xmlParserCtxtPtr ctxt, const xmlChar **str) { name, NULL); ctxt->valid = 0; } + xmlParserEntityCheck(ctxt, 0, NULL, 0); } else { /* * Internal checking in case the entity quest barfed -- cgit v0.10.1