FYI
-------- Original-Message --------
Subject: [xml] Security fix for libxml2
Date: Wed, 20 Aug 2008 19:00:51 +0200
From: Daniel Veillard <veillard(a)redhat.com>
To: xml(a)gnome.org
Bad news, when checking against recursive entities expansion problem
back when it was made official (c.f. the billion laught attack circa
2004) I had checked for the normal recursion, but when happening in
an attribute value the resource consumption is way faster and the
recursion detection in place is not sufficient to catch the problem.
Basically when this happen within an attribute just checking for
a recursion depth is not sufficient, and the only good method I could
find was to count the number of entities replacement taking place while
parsing a given document, and drop parsing after half a million
substitution. I think it's a fair default process and what the patches
below implements for various libxml2 versions, but i can understand that
in some case that may be problematic. So i intend in the next release
(2.7.0 hopefully available soon) to add a parser flag removing the
hardcoded limits (there is also a maximum document depth in place).
Distributions have been made aware of the problem for a couple of
weeks and updates should be available soon from normal update channels
I'm updating SVN with the fix too,
Daniel
Index: include/libxml/parser.h
===================================================================
--- include/libxml/parser.h (revision 3771)
+++ include/libxml/parser.h (working copy)
@@ -297,6 +297,7 @@ struct _xmlParserCtxt {
*/
xmlError lastError;
xmlParserMode parseMode; /* the parser mode */
+ unsigned long nbentities; /* number of entities references */
};
/**
Index: include/libxml/entities.h
===================================================================
--- include/libxml/entities.h (revision 3771)
+++ include/libxml/entities.h (working copy)
@@ -57,6 +57,7 @@ struct _xmlEntity {
const xmlChar *URI; /* the full URI as computed */
int owner; /* does the entity own the childrens */
int checked; /* was the entity content checked */
+ unsigned long nbentities; /* the number of entities references */
};
/*
Index: entities.c
===================================================================
--- entities.c (revision 3771)
+++ entities.c (working copy)
@@ -31,35 +31,35 @@ static xmlEntity xmlEntityLt = {
NULL, NULL, NULL, NULL, NULL, NULL,
BAD_CAST "<", BAD_CAST "<", 1,
XML_INTERNAL_PREDEFINED_ENTITY,
- NULL, NULL, NULL, NULL, 0, 1
+ NULL, NULL, NULL, NULL, 0, 1, 0
};
static xmlEntity xmlEntityGt = {
NULL, XML_ENTITY_DECL, BAD_CAST "gt",
NULL, NULL, NULL, NULL, NULL, NULL,
BAD_CAST ">", BAD_CAST ">", 1,
XML_INTERNAL_PREDEFINED_ENTITY,
- NULL, NULL, NULL, NULL, 0, 1
+ NULL, NULL, NULL, NULL, 0, 1, 0
};
static xmlEntity xmlEntityAmp = {
NULL, XML_ENTITY_DECL, BAD_CAST "amp",
NULL, NULL, NULL, NULL, NULL, NULL,
BAD_CAST "&", BAD_CAST "&", 1,
XML_INTERNAL_PREDEFINED_ENTITY,
- NULL, NULL, NULL, NULL, 0, 1
+ NULL, NULL, NULL, NULL, 0, 1, 0
};
static xmlEntity xmlEntityQuot = {
NULL, XML_ENTITY_DECL, BAD_CAST "quot",
NULL, NULL, NULL, NULL, NULL, NULL,
BAD_CAST "\"", BAD_CAST "\"", 1,
XML_INTERNAL_PREDEFINED_ENTITY,
- NULL, NULL, NULL, NULL, 0, 1
+ NULL, NULL, NULL, NULL, 0, 1, 0
};
static xmlEntity xmlEntityApos = {
NULL, XML_ENTITY_DECL, BAD_CAST "apos",
NULL, NULL, NULL, NULL, NULL, NULL,
BAD_CAST "'", BAD_CAST "'", 1,
XML_INTERNAL_PREDEFINED_ENTITY,
- NULL, NULL, NULL, NULL, 0, 1
+ NULL, NULL, NULL, NULL, 0, 1, 0
};
/**
Index: parserInternals.c
===================================================================
--- parserInternals.c (revision 3771)
+++ parserInternals.c (working copy)
@@ -1670,6 +1670,7 @@ xmlInitParserCtxt(xmlParserCtxtPtr ctxt)
ctxt->depth = 0;
ctxt->charset = XML_CHAR_ENCODING_UTF8;
ctxt->catalogs = NULL;
+ ctxt->nbentities = 0;
xmlInitNodeInfoSeq(&ctxt->node_seq);
return(0);
}
Index: parser.c
===================================================================
--- parser.c (revision 3771)
+++ parser.c (working copy)
@@ -2379,7 +2379,7 @@ xmlStringLenDecodeEntities(xmlParserCtxt
return(NULL);
last = str + len;
- if (ctxt->depth > 40) {
+ if ((ctxt->depth > 40) || (ctxt->nbentities >= 500000)) {
xmlFatalErr(ctxt, XML_ERR_ENTITY_LOOP, NULL);
return(NULL);
}
@@ -2417,6 +2417,11 @@ xmlStringLenDecodeEntities(xmlParserCtxt
"String decoding Entity Reference: %.30s\n",
str);
ent = xmlParseStringEntityRef(ctxt, &str);
+ if (ctxt->lastError.code == XML_ERR_ENTITY_LOOP)
+ goto int_error;
+ ctxt->nbentities++;
+ if (ent != NULL)
+ ctxt->nbentities += ent->nbentities;
if ((ent != NULL) &&
(ent->etype == XML_INTERNAL_PREDEFINED_ENTITY)) {
if (ent->content != NULL) {
@@ -2462,6 +2467,11 @@ xmlStringLenDecodeEntities(xmlParserCtxt
xmlGenericError(xmlGenericErrorContext,
"String decoding PE Reference: %.30s\n", str);
ent = xmlParseStringPEReference(ctxt, &str);
+ if (ctxt->lastError.code == XML_ERR_ENTITY_LOOP)
+ goto int_error;
+ ctxt->nbentities++;
+ if (ent != NULL)
+ ctxt->nbentities += ent->nbentities;
if (ent != NULL) {
if (ent->content == NULL) {
if (xmlLoadEntityContent(ctxt, ent) < 0) {
@@ -2501,6 +2511,7 @@ xmlStringLenDecodeEntities(xmlParserCtxt
mem_error:
xmlErrMemory(ctxt, NULL);
+int_error:
if (rep != NULL)
xmlFree(rep);
if (buffer != NULL)
@@ -3542,6 +3553,9 @@ xmlParseAttValueComplex(xmlParserCtxtPtr
}
} else {
ent = xmlParseEntityRef(ctxt);
+ ctxt->nbentities++;
+ if (ent != NULL)
+ ctxt->nbentities += ent->nbentities;
if ((ent != NULL) &&
(ent->etype == XML_INTERNAL_PREDEFINED_ENTITY)) {
if (len > buf_size - 10) {
@@ -4844,6 +4858,7 @@ xmlParseEntityDecl(xmlParserCtxtPtr ctxt
int isParameter = 0;
xmlChar *orig = NULL;
int skipped;
+ unsigned long oldnbent = ctxt->nbentities;
/* GROW; done in the caller */
if (CMP8(CUR_PTR, '<', '!', 'E', 'N', 'T', 'I', 'T', 'Y')) {
@@ -5068,6 +5083,7 @@ xmlParseEntityDecl(xmlParserCtxtPtr ctxt
}
}
if (cur != NULL) {
+ cur->nbentities = ctxt->nbentities - oldnbent;
if (cur->orig != NULL)
xmlFree(orig);
else
@@ -6477,6 +6493,11 @@ xmlParseReference(xmlParserCtxtPtr ctxt)
if (ent == NULL) return;
if (!ctxt->wellFormed)
return;
+ ctxt->nbentities++;
+ if (ctxt->nbentities >= 500000) {
+ xmlFatalErr(ctxt, XML_ERR_ENTITY_LOOP, NULL);
+ return;
+ }
was_checked = ent->checked;
if ((ent->name != NULL) &&
(ent->etype != XML_INTERNAL_PREDEFINED_ENTITY)) {
@@ -6537,6 +6558,7 @@ xmlParseReference(xmlParserCtxtPtr ctxt)
xmlFreeNodeList(list);
}
} else {
+ unsigned long oldnbent = ctxt->nbentities;
/*
* 4.3.2: An internal general parsed entity is well-formed
* if its replacement text matches the production labeled
@@ -6559,6 +6581,7 @@ xmlParseReference(xmlParserCtxtPtr ctxt)
ret = xmlParseBalancedChunkMemoryInternal(ctxt,
value, user_data, &list);
ctxt->depth--;
+
} else if (ent->etype ==
XML_EXTERNAL_GENERAL_PARSED_ENTITY) {
ctxt->depth++;
@@ -6571,6 +6594,7 @@ xmlParseReference(xmlParserCtxtPtr ctxt)
xmlErrMsgStr(ctxt, XML_ERR_INTERNAL_ERROR,
"invalid entity type found\n", NULL);
}
+ ent->nbentities = ctxt->nbentities - oldnbent;
if (ret == XML_ERR_ENTITY_LOOP) {
xmlFatalErr(ctxt, XML_ERR_ENTITY_LOOP, NULL);
return;
@@ -6629,6 +6653,7 @@ xmlParseReference(xmlParserCtxtPtr ctxt)
}
ent->checked = 1;
}
+ ctxt->nbentities += ent->nbentities;
if (ent->children == NULL) {
/*
@@ -11800,7 +11825,7 @@ xmlParseCtxtExternalEntity(xmlParserCtxt
if (ctx == NULL) return(-1);
- if (ctx->depth > 40) {
+ if ((ctx->depth > 40) || (ctx->nbentities >= 500000)) {
return(XML_ERR_ENTITY_LOOP);
}
@@ -12010,7 +12035,8 @@ xmlParseExternalEntityPrivate(xmlDocPtr
xmlChar start[4];
xmlCharEncoding enc;
- if (depth > 40) {
+ if ((depth > 40) ||
+ ((oldctxt != NULL) && (oldctxt->nbentities >= 500000))) {
return(XML_ERR_ENTITY_LOOP);
}
@@ -12154,6 +12180,7 @@ xmlParseExternalEntityPrivate(xmlDocPtr
oldctxt->node_seq.maximum = ctxt->node_seq.maximum;
oldctxt->node_seq.length = ctxt->node_seq.length;
oldctxt->node_seq.buffer = ctxt->node_seq.buffer;
+ oldctxt->nbentities += ctxt->nbentities;
ctxt->node_seq.maximum = 0;
ctxt->node_seq.length = 0;
ctxt->node_seq.buffer = NULL;
@@ -12254,7 +12281,7 @@ xmlParseBalancedChunkMemoryInternal(xmlP
int size;
xmlParserErrors ret = XML_ERR_OK;
- if (oldctxt->depth > 40) {
+ if ((oldctxt->depth > 40) || (oldctxt->nbentities >= 500000)) {
return(XML_ERR_ENTITY_LOOP);
}
@@ -12379,6 +12406,7 @@ xmlParseBalancedChunkMemoryInternal(xmlP
ctxt->myDoc->last = last;
}
+ oldctxt->nbentities += ctxt->nbentities;
ctxt->sax = oldsax;
ctxt->dict = NULL;
ctxt->attsDefault = NULL;
@@ -13695,6 +13723,7 @@ xmlCtxtReset(xmlParserCtxtPtr ctxt)
ctxt->depth = 0;
ctxt->charset = XML_CHAR_ENCODING_UTF8;
ctxt->catalogs = NULL;
+ ctxt->nbentities = 0;
xmlInitNodeInfoSeq(&ctxt->node_seq);
if (ctxt->attsDefault != NULL) {
--- include/libxml/parser.h (revision 3771)
+++ include/libxml/parser.h (working copy)
@@ -297,6 +297,7 @@ struct _xmlParserCtxt {
*/
xmlError lastError;
xmlParserMode parseMode; /* the parser mode */
+ unsigned long nbentities; /* number of entities references */
};
/**
--- include/libxml/entities.h.orig 2005-01-04 15:49:49.000000000 +0100
+++ include/libxml/entities.h 2008-08-11 17:56:53.000000000 +0200
@@ -56,6 +56,7 @@ struct _xmlEntity {
struct _xmlEntity *nexte; /* unused */
const xmlChar *URI; /* the full URI as computed */
int owner; /* does the entity own the childrens */
+ unsigned long nbentities; /* the number of entities references */
};
/*
--- entities.c.orig 2006-03-09 17:39:46.000000000 +0100
+++ entities.c 2008-08-11 18:01:06.000000000 +0200
@@ -31,35 +31,35 @@ static xmlEntity xmlEntityLt = {
NULL, NULL, NULL, NULL, NULL, NULL,
BAD_CAST "<", BAD_CAST "<", 1,
XML_INTERNAL_PREDEFINED_ENTITY,
- NULL, NULL, NULL, NULL, 0
+ NULL, NULL, NULL, NULL, 0, 0
};
static xmlEntity xmlEntityGt = {
NULL, XML_ENTITY_DECL, BAD_CAST "gt",
NULL, NULL, NULL, NULL, NULL, NULL,
BAD_CAST ">", BAD_CAST ">", 1,
XML_INTERNAL_PREDEFINED_ENTITY,
- NULL, NULL, NULL, NULL, 0
+ NULL, NULL, NULL, NULL, 0, 0
};
static xmlEntity xmlEntityAmp = {
NULL, XML_ENTITY_DECL, BAD_CAST "amp",
NULL, NULL, NULL, NULL, NULL, NULL,
BAD_CAST "&", BAD_CAST "&", 1,
XML_INTERNAL_PREDEFINED_ENTITY,
- NULL, NULL, NULL, NULL, 0
+ NULL, NULL, NULL, NULL, 0, 0
};
static xmlEntity xmlEntityQuot = {
NULL, XML_ENTITY_DECL, BAD_CAST "quot",
NULL, NULL, NULL, NULL, NULL, NULL,
BAD_CAST "\"", BAD_CAST "\"", 1,
XML_INTERNAL_PREDEFINED_ENTITY,
- NULL, NULL, NULL, NULL, 0
+ NULL, NULL, NULL, NULL, 0, 0
};
static xmlEntity xmlEntityApos = {
NULL, XML_ENTITY_DECL, BAD_CAST "apos",
NULL, NULL, NULL, NULL, NULL, NULL,
BAD_CAST "'", BAD_CAST "'", 1,
XML_INTERNAL_PREDEFINED_ENTITY,
- NULL, NULL, NULL, NULL, 0
+ NULL, NULL, NULL, NULL, 0, 0
};
/**
--- parser.c.orig 2006-04-23 11:39:15.000000000 +0200
+++ parser.c 2008-08-11 18:36:56.000000000 +0200
@@ -2174,7 +2176,7 @@ xmlStringLenDecodeEntities(xmlParserCtxt
return(NULL);
last = str + len;
- if (ctxt->depth > 40) {
+ if ((ctxt->depth > 40) || (ctxt->nbentities >= 500000)) {
xmlFatalErr(ctxt, XML_ERR_ENTITY_LOOP, NULL);
return(NULL);
}
@@ -2212,6 +2214,11 @@ xmlStringLenDecodeEntities(xmlParserCtxt
"String decoding Entity Reference: %.30s\n",
str);
ent = xmlParseStringEntityRef(ctxt, &str);
+ if (ctxt->lastError.code == XML_ERR_ENTITY_LOOP)
+ goto int_error;
+ ctxt->nbentities++;
+ if (ent != NULL)
+ ctxt->nbentities += ent->nbentities;
if ((ent != NULL) &&
(ent->etype == XML_INTERNAL_PREDEFINED_ENTITY)) {
if (ent->content != NULL) {
@@ -2258,6 +2265,11 @@ xmlStringLenDecodeEntities(xmlParserCtxt
xmlGenericError(xmlGenericErrorContext,
"String decoding PE Reference: %.30s\n", str);
ent = xmlParseStringPEReference(ctxt, &str);
+ if (ctxt->lastError.code == XML_ERR_ENTITY_LOOP)
+ goto int_error;
+ ctxt->nbentities++;
+ if (ent != NULL)
+ ctxt->nbentities += ent->nbentities;
if (ent != NULL) {
xmlChar *rep;
@@ -2294,6 +2306,9 @@ xmlStringLenDecodeEntities(xmlParserCtxt
mem_error:
xmlErrMemory(ctxt, NULL);
+int_error:
+ if (buffer != NULL)
+ xmlFree(buffer);
return(NULL);
}
@@ -3100,6 +3115,9 @@ xmlParseAttValueComplex(xmlParserCtxtPtr
}
} else {
ent = xmlParseEntityRef(ctxt);
+ ctxt->nbentities++;
+ if (ent != NULL)
+ ctxt->nbentities += ent->nbentities;
if ((ent != NULL) &&
(ent->etype == XML_INTERNAL_PREDEFINED_ENTITY)) {
if (len > buf_size - 10) {
@@ -4342,6 +4360,7 @@ xmlParseEntityDecl(xmlParserCtxtPtr ctxt
int isParameter = 0;
xmlChar *orig = NULL;
int skipped;
+ unsigned long oldnbent = ctxt->nbentities;
/* GROW; done in the caller */
if (CMP8(CUR_PTR, '<', '!', 'E', 'N', 'T', 'I', 'T', 'Y')) {
@@ -4551,6 +4570,7 @@ xmlParseEntityDecl(xmlParserCtxtPtr ctxt
}
}
if (cur != NULL) {
+ cur->nbentities = ctxt->nbentities - oldnbent;
if (cur->orig != NULL)
xmlFree(orig);
else
@@ -5927,6 +5947,11 @@ xmlParseReference(xmlParserCtxtPtr ctxt)
if (ent == NULL) return;
if (!ctxt->wellFormed)
return;
+ ctxt->nbentities++;
+ if (ctxt->nbentities >= 500000) {
+ xmlFatalErr(ctxt, XML_ERR_ENTITY_LOOP, NULL);
+ return;
+ }
if ((ent->name != NULL) &&
(ent->etype != XML_INTERNAL_PREDEFINED_ENTITY)) {
xmlNodePtr list = NULL;
@@ -5985,6 +6010,7 @@ xmlParseReference(xmlParserCtxtPtr ctxt)
xmlFreeNodeList(list);
}
} else {
+ unsigned long oldnbent = ctxt->nbentities;
/*
* 4.3.2: An internal general parsed entity is well-formed
* if its replacement text matches the production labeled
@@ -6007,6 +6033,7 @@ xmlParseReference(xmlParserCtxtPtr ctxt)
ret = xmlParseBalancedChunkMemoryInternal(ctxt,
value, user_data, &list);
ctxt->depth--;
+
} else if (ent->etype ==
XML_EXTERNAL_GENERAL_PARSED_ENTITY) {
ctxt->depth++;
@@ -6019,6 +6046,7 @@ xmlParseReference(xmlParserCtxtPtr ctxt)
xmlErrMsgStr(ctxt, XML_ERR_INTERNAL_ERROR,
"invalid entity type found\n", NULL);
}
+ ent->nbentities = ctxt->nbentities - oldnbent;
if (ret == XML_ERR_ENTITY_LOOP) {
xmlFatalErr(ctxt, XML_ERR_ENTITY_LOOP, NULL);
return;
@@ -6075,6 +6103,7 @@ xmlParseReference(xmlParserCtxtPtr ctxt)
}
}
}
+ ctxt->nbentities += ent->nbentities;
if ((ctxt->sax != NULL) && (ctxt->sax->reference != NULL) &&
(ctxt->replaceEntities == 0) && (!ctxt->disableSAX)) {
/*
@@ -11035,7 +11064,7 @@ xmlParseCtxtExternalEntity(xmlParserCtxt
if (ctx == NULL) return(-1);
- if (ctx->depth > 40) {
+ if ((ctx->depth > 40) || (ctx->nbentities >= 500000)) {
return(XML_ERR_ENTITY_LOOP);
}
@@ -11220,7 +11249,8 @@ xmlParseExternalEntityPrivate(xmlDocPtr
xmlChar start[4];
xmlCharEncoding enc;
- if (depth > 40) {
+ if ((depth > 40) ||
+ ((oldctxt != NULL) && (oldctxt->nbentities >= 500000))) {
return(XML_ERR_ENTITY_LOOP);
}
@@ -11363,6 +11393,7 @@ xmlParseExternalEntityPrivate(xmlDocPtr
oldctxt->node_seq.maximum = ctxt->node_seq.maximum;
oldctxt->node_seq.length = ctxt->node_seq.length;
oldctxt->node_seq.buffer = ctxt->node_seq.buffer;
+ oldctxt->nbentities += ctxt->nbentities;
ctxt->node_seq.maximum = 0;
ctxt->node_seq.length = 0;
ctxt->node_seq.buffer = NULL;
@@ -11463,7 +11494,7 @@ xmlParseBalancedChunkMemoryInternal(xmlP
int size;
xmlParserErrors ret = XML_ERR_OK;
- if (oldctxt->depth > 40) {
+ if ((oldctxt->depth > 40) || (oldctxt->nbentities >= 500000)) {
return(XML_ERR_ENTITY_LOOP);
}
@@ -11587,6 +11618,7 @@ xmlParseBalancedChunkMemoryInternal(xmlP
ctxt->myDoc->last = last;
}
+ oldctxt->nbentities += ctxt->nbentities;
ctxt->sax = oldsax;
ctxt->dict = NULL;
ctxt->attsDefault = NULL;
@@ -12883,6 +12915,7 @@ xmlCtxtReset(xmlParserCtxtPtr ctxt)
ctxt->depth = 0;
ctxt->charset = XML_CHAR_ENCODING_UTF8;
ctxt->catalogs = NULL;
+ ctxt->nbentities = 0;
xmlInitNodeInfoSeq(&ctxt->node_seq);
if (ctxt->attsDefault != NULL) {
--- include/libxml/parser.h (revision 3771)
+++ include/libxml/parser.h (working copy)
@@ -297,6 +297,7 @@ struct _xmlParserCtxt {
*/
xmlError lastError;
xmlParserMode parseMode; /* the parser mode */
+ unsigned long nbentities; /* number of entities references */
};
/**
--- include/libxml/entities.h.orig 2005-01-04 15:49:49.000000000 +0100
+++ include/libxml/entities.h 2008-08-11 17:56:53.000000000 +0200
@@ -56,6 +56,7 @@ struct _xmlEntity {
struct _xmlEntity *nexte; /* unused */
const xmlChar *URI; /* the full URI as computed */
int owner; /* does the entity own the childrens */
+ unsigned long nbentities; /* the number of entities references */
};
/*
--- entities.c.orig 2006-03-09 17:39:46.000000000 +0100
+++ entities.c 2008-08-11 18:01:06.000000000 +0200
@@ -31,35 +31,35 @@ static xmlEntity xmlEntityLt = {
NULL, NULL, NULL, NULL, NULL, NULL,
BAD_CAST "<", BAD_CAST "<", 1,
XML_INTERNAL_PREDEFINED_ENTITY,
- NULL, NULL, NULL, NULL, 0
+ NULL, NULL, NULL, NULL, 0, 0
};
static xmlEntity xmlEntityGt = {
NULL, XML_ENTITY_DECL, BAD_CAST "gt",
NULL, NULL, NULL, NULL, NULL, NULL,
BAD_CAST ">", BAD_CAST ">", 1,
XML_INTERNAL_PREDEFINED_ENTITY,
- NULL, NULL, NULL, NULL, 0
+ NULL, NULL, NULL, NULL, 0, 0
};
static xmlEntity xmlEntityAmp = {
NULL, XML_ENTITY_DECL, BAD_CAST "amp",
NULL, NULL, NULL, NULL, NULL, NULL,
BAD_CAST "&", BAD_CAST "&", 1,
XML_INTERNAL_PREDEFINED_ENTITY,
- NULL, NULL, NULL, NULL, 0
+ NULL, NULL, NULL, NULL, 0, 0
};
static xmlEntity xmlEntityQuot = {
NULL, XML_ENTITY_DECL, BAD_CAST "quot",
NULL, NULL, NULL, NULL, NULL, NULL,
BAD_CAST "\"", BAD_CAST "\"", 1,
XML_INTERNAL_PREDEFINED_ENTITY,
- NULL, NULL, NULL, NULL, 0
+ NULL, NULL, NULL, NULL, 0, 0
};
static xmlEntity xmlEntityApos = {
NULL, XML_ENTITY_DECL, BAD_CAST "apos",
NULL, NULL, NULL, NULL, NULL, NULL,
BAD_CAST "'", BAD_CAST "'", 1,
XML_INTERNAL_PREDEFINED_ENTITY,
- NULL, NULL, NULL, NULL, 0
+ NULL, NULL, NULL, NULL, 0, 0
};
/**
--- parser.c.orig 2006-04-23 11:39:15.000000000 +0200
+++ parser.c 2008-08-11 18:36:56.000000000 +0200
@@ -2174,7 +2176,7 @@ xmlStringLenDecodeEntities(xmlParserCtxt
return(NULL);
last = str + len;
- if (ctxt->depth > 40) {
+ if ((ctxt->depth > 40) || (ctxt->nbentities >= 500000)) {
xmlFatalErr(ctxt, XML_ERR_ENTITY_LOOP, NULL);
return(NULL);
}
@@ -2212,6 +2214,11 @@ xmlStringLenDecodeEntities(xmlParserCtxt
"String decoding Entity Reference: %.30s\n",
str);
ent = xmlParseStringEntityRef(ctxt, &str);
+ if (ctxt->lastError.code == XML_ERR_ENTITY_LOOP)
+ goto int_error;
+ ctxt->nbentities++;
+ if (ent != NULL)
+ ctxt->nbentities += ent->nbentities;
if ((ent != NULL) &&
(ent->etype == XML_INTERNAL_PREDEFINED_ENTITY)) {
if (ent->content != NULL) {
@@ -2258,6 +2265,11 @@ xmlStringLenDecodeEntities(xmlParserCtxt
xmlGenericError(xmlGenericErrorContext,
"String decoding PE Reference: %.30s\n", str);
ent = xmlParseStringPEReference(ctxt, &str);
+ if (ctxt->lastError.code == XML_ERR_ENTITY_LOOP)
+ goto int_error;
+ ctxt->nbentities++;
+ if (ent != NULL)
+ ctxt->nbentities += ent->nbentities;
if (ent != NULL) {
xmlChar *rep;
@@ -2294,6 +2306,9 @@ xmlStringLenDecodeEntities(xmlParserCtxt
mem_error:
xmlErrMemory(ctxt, NULL);
+int_error:
+ if (buffer != NULL)
+ xmlFree(buffer);
return(NULL);
}
@@ -3100,6 +3115,9 @@ xmlParseAttValueComplex(xmlParserCtxtPtr
}
} else {
ent = xmlParseEntityRef(ctxt);
+ ctxt->nbentities++;
+ if (ent != NULL)
+ ctxt->nbentities += ent->nbentities;
if ((ent != NULL) &&
(ent->etype == XML_INTERNAL_PREDEFINED_ENTITY)) {
if (len > buf_size - 10) {
@@ -4342,6 +4360,7 @@ xmlParseEntityDecl(xmlParserCtxtPtr ctxt
int isParameter = 0;
xmlChar *orig = NULL;
int skipped;
+ unsigned long oldnbent = ctxt->nbentities;
/* GROW; done in the caller */
if (CMP8(CUR_PTR, '<', '!', 'E', 'N', 'T', 'I', 'T', 'Y')) {
@@ -4551,6 +4570,7 @@ xmlParseEntityDecl(xmlParserCtxtPtr ctxt
}
}
if (cur != NULL) {
+ cur->nbentities = ctxt->nbentities - oldnbent;
if (cur->orig != NULL)
xmlFree(orig);
else
@@ -5927,6 +5947,11 @@ xmlParseReference(xmlParserCtxtPtr ctxt)
if (ent == NULL) return;
if (!ctxt->wellFormed)
return;
+ ctxt->nbentities++;
+ if (ctxt->nbentities >= 500000) {
+ xmlFatalErr(ctxt, XML_ERR_ENTITY_LOOP, NULL);
+ return;
+ }
if ((ent->name != NULL) &&
(ent->etype != XML_INTERNAL_PREDEFINED_ENTITY)) {
xmlNodePtr list = NULL;
@@ -5985,6 +6010,7 @@ xmlParseReference(xmlParserCtxtPtr ctxt)
xmlFreeNodeList(list);
}
} else {
+ unsigned long oldnbent = ctxt->nbentities;
/*
* 4.3.2: An internal general parsed entity is well-formed
* if its replacement text matches the production labeled
@@ -6007,6 +6033,7 @@ xmlParseReference(xmlParserCtxtPtr ctxt)
ret = xmlParseBalancedChunkMemoryInternal(ctxt,
value, user_data, &list);
ctxt->depth--;
+
} else if (ent->etype ==
XML_EXTERNAL_GENERAL_PARSED_ENTITY) {
ctxt->depth++;
@@ -6019,6 +6046,7 @@ xmlParseReference(xmlParserCtxtPtr ctxt)
xmlErrMsgStr(ctxt, XML_ERR_INTERNAL_ERROR,
"invalid entity type found\n", NULL);
}
+ ent->nbentities = ctxt->nbentities - oldnbent;
if (ret == XML_ERR_ENTITY_LOOP) {
xmlFatalErr(ctxt, XML_ERR_ENTITY_LOOP, NULL);
return;
@@ -6075,6 +6103,7 @@ xmlParseReference(xmlParserCtxtPtr ctxt)
}
}
}
+ ctxt->nbentities += ent->nbentities;
if ((ctxt->sax != NULL) && (ctxt->sax->reference != NULL) &&
(ctxt->replaceEntities == 0) && (!ctxt->disableSAX)) {
/*
@@ -11035,7 +11064,7 @@ xmlParseCtxtExternalEntity(xmlParserCtxt
if (ctx == NULL) return(-1);
- if (ctx->depth > 40) {
+ if ((ctx->depth > 40) || (ctx->nbentities >= 500000)) {
return(XML_ERR_ENTITY_LOOP);
}
@@ -11220,7 +11249,8 @@ xmlParseExternalEntityPrivate(xmlDocPtr
xmlChar start[4];
xmlCharEncoding enc;
- if (depth > 40) {
+ if ((depth > 40) ||
+ ((oldctxt != NULL) && (oldctxt->nbentities >= 500000))) {
return(XML_ERR_ENTITY_LOOP);
}
@@ -11363,6 +11393,7 @@ xmlParseExternalEntityPrivate(xmlDocPtr
oldctxt->node_seq.maximum = ctxt->node_seq.maximum;
oldctxt->node_seq.length = ctxt->node_seq.length;
oldctxt->node_seq.buffer = ctxt->node_seq.buffer;
+ oldctxt->nbentities += ctxt->nbentities;
ctxt->node_seq.maximum = 0;
ctxt->node_seq.length = 0;
ctxt->node_seq.buffer = NULL;
@@ -11463,7 +11494,7 @@ xmlParseBalancedChunkMemoryInternal(xmlP
int size;
xmlParserErrors ret = XML_ERR_OK;
- if (oldctxt->depth > 40) {
+ if ((oldctxt->depth > 40) || (oldctxt->nbentities >= 500000)) {
return(XML_ERR_ENTITY_LOOP);
}
@@ -11587,6 +11618,7 @@ xmlParseBalancedChunkMemoryInternal(xmlP
ctxt->myDoc->last = last;
}
+ oldctxt->nbentities += ctxt->nbentities;
ctxt->sax = oldsax;
ctxt->dict = NULL;
ctxt->attsDefault = NULL;
@@ -12883,6 +12915,7 @@ xmlCtxtReset(xmlParserCtxtPtr ctxt)
ctxt->depth = 0;
ctxt->charset = XML_CHAR_ENCODING_UTF8;
ctxt->catalogs = NULL;
+ ctxt->nbentities = 0;
xmlInitNodeInfoSeq(&ctxt->node_seq);
if (ctxt->attsDefault != NULL) {