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 avlue 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 processand 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
--
Red Hat Virtualization group http://redhat.com/virtualization/
Daniel Veillard | virtualization library http://libvirt.org/
[EMAIL PROTECTED] | libxml GNOME XML XSLT toolkit http://xmlsoft.org/
http://veillard.com/ | Rpmfind RPM search engine http://rpmfind.net/
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) {
--- 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 */
};
/*
--- include/libxml/parser.h.orig 2003-04-24 14:04:00.000000000 +0200
+++ include/libxml/parser.h 2008-08-11 19:03:50.000000000 +0200
@@ -234,6 +234,7 @@ struct _xmlParserCtxt {
void *catalogs; /* document's own catalog */
int recovery; /* run in recovery mode */
int progressive; /* is this a progressive parsing */
+ unsigned long nbentities; /* number of entities references */
};
/**
--- parser.c.orig 2003-08-15 01:42:29.000000000 +0200
+++ parser.c 2008-08-11 19:28:59.000000000 +0200
@@ -1061,7 +1061,7 @@ xmlStringDecodeEntities(xmlParserCtxtPtr
if (str == NULL)
return(NULL);
- if (ctxt->depth > 40) {
+ if ((ctxt->depth > 40) || (ctxt->nbentities >= 500000)) {
ctxt->errNo = XML_ERR_ENTITY_LOOP;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
@@ -1102,6 +1102,11 @@ xmlStringDecodeEntities(xmlParserCtxtPtr
"String decoding Entity Reference: %.30s\n",
str);
ent = xmlParseStringEntityRef(ctxt, &str);
+ if (ctxt->errNo == 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) {
@@ -1146,6 +1151,11 @@ xmlStringDecodeEntities(xmlParserCtxtPtr
xmlGenericError(xmlGenericErrorContext,
"String decoding PE Reference: %.30s\n", str);
ent = xmlParseStringPEReference(ctxt, &str);
+ if (ctxt->errNo == XML_ERR_ENTITY_LOOP)
+ goto int_error;
+ ctxt->nbentities++;
+ if (ent != NULL)
+ ctxt->nbentities += ent->nbentities;
if (ent != NULL) {
xmlChar *rep;
@@ -1176,6 +1186,10 @@ xmlStringDecodeEntities(xmlParserCtxtPtr
}
buffer[nbchars++] = 0;
return(buffer);
+int_error:
+ if (buffer != NULL)
+ xmlFree(buffer);
+ return(NULL);
}
@@ -2527,6 +2541,9 @@ xmlParseAttValueComplex(xmlParserCtxtPtr
}
} else {
ent = xmlParseEntityRef(ctxt);
+ ctxt->nbentities++;
+ if (ent != NULL)
+ ctxt->nbentities += ent->nbentities;
if ((ent != NULL) &&
(ctxt->replaceEntities != 0)) {
xmlChar *rep;
@@ -3595,6 +3612,7 @@ xmlParseEntityDecl(xmlParserCtxtPtr ctxt
int isParameter = 0;
xmlChar *orig = NULL;
int skipped;
+ unsigned long oldnbent = ctxt->nbentities;
GROW;
if ((RAW == '<') && (NXT(1) == '!') &&
@@ -3870,6 +3888,7 @@ xmlParseEntityDecl(xmlParserCtxtPtr ctxt
}
}
if (cur != NULL) {
+ cur->nbentities = ctxt->nbentities - oldnbent;
if (cur->orig != NULL)
xmlFree(orig);
else
@@ -5480,6 +5499,16 @@ xmlParseReference(xmlParserCtxtPtr ctxt)
if (ent == NULL) return;
if (!ctxt->wellFormed)
return;
+ ctxt->nbentities++;
+ if (ctxt->nbentities >= 500000) {
+ ctxt->errNo = XML_ERR_ENTITY_LOOP;
+ if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
+ ctxt->sax->error(ctxt->userData,
+ "Detected entity reference loop\n");
+ ctxt->wellFormed = 0;
+ if (ctxt->recovery == 0) ctxt->disableSAX = 1;
+ return;
+ }
if ((ent->name != NULL) &&
(ent->etype != XML_INTERNAL_PREDEFINED_ENTITY)) {
xmlNodePtr list = NULL;
@@ -5538,6 +5567,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
@@ -5560,6 +5590,7 @@ xmlParseReference(xmlParserCtxtPtr ctxt)
ret = xmlParseBalancedChunkMemoryInternal(ctxt,
value, user_data, &list);
ctxt->depth--;
+
} else if (ent->etype ==
XML_EXTERNAL_GENERAL_PARSED_ENTITY) {
ctxt->depth++;
@@ -5573,6 +5604,7 @@ xmlParseReference(xmlParserCtxtPtr ctxt)
ctxt->sax->error(ctxt->userData,
"Internal: invalid entity type\n");
}
+ ent->nbentities = ctxt->nbentities - oldnbent;
if (ret == XML_ERR_ENTITY_LOOP) {
ctxt->errNo = XML_ERR_ENTITY_LOOP;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
@@ -5635,6 +5667,7 @@ xmlParseReference(xmlParserCtxtPtr ctxt)
}
}
}
+ ctxt->nbentities += ent->nbentities;
if ((ctxt->sax != NULL) && (ctxt->sax->reference != NULL) &&
(ctxt->replaceEntities == 0) && (!ctxt->disableSAX)) {
/*
@@ -9713,7 +9746,7 @@ xmlParseCtxtExternalEntity(xmlParserCtxt
xmlChar start[4];
xmlCharEncoding enc;
- if (ctx->depth > 40) {
+ if ((ctx->depth > 40) || (ctx->nbentities >= 500000)) {
return(XML_ERR_ENTITY_LOOP);
}
@@ -9894,7 +9927,8 @@ xmlParseExternalEntityPrivate(xmlDocPtr
xmlChar start[4];
xmlCharEncoding enc;
- if (depth > 40) {
+ if ((depth > 40) ||
+ ((oldctxt != NULL) && (oldctxt->nbentities >= 500000))) {
return(XML_ERR_ENTITY_LOOP);
}
@@ -10053,6 +10087,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;
@@ -10149,7 +10184,7 @@ xmlParseBalancedChunkMemoryInternal(xmlP
int size;
int ret = 0;
- if (oldctxt->depth > 40) {
+ if ((oldctxt->depth > 40) || (oldctxt->nbentities >= 500000)) {
return(XML_ERR_ENTITY_LOOP);
}
@@ -10264,6 +10299,7 @@ xmlParseBalancedChunkMemoryInternal(xmlP
ctxt->myDoc->children = content;
}
+ oldctxt->nbentities += ctxt->nbentities;
ctxt->sax = oldsax;
xmlFreeParserCtxt(ctxt);
if (newDoc != NULL)
--- include/libxml/entities.h.orig 2002-03-13 04:35:14.000000000 +0100
+++ include/libxml/entities.h 2008-08-11 19:40:53.000000000 +0200
@@ -52,6 +52,7 @@ struct _xmlEntity {
struct _xmlEntity *nexte; /* unused */
const xmlChar *URI; /* the full URI as computed */
+ unsigned long nbentities; /* the number of entities references */
};
/*
--- include/libxml/parser.h.orig 2002-03-13 04:35:14.000000000 +0100
+++ include/libxml/parser.h 2008-08-11 19:41:53.000000000 +0200
@@ -219,6 +219,7 @@ struct _xmlParserCtxt {
int loadsubset; /* should the external subset be loaded
*/
int linenumbers; /* set line number in element content */
void *catalogs; /* document's own catalog */
+ unsigned long nbentities; /* number of entities references */
};
/**
--- parser.c.orig 2002-03-21 04:35:11.000000000 +0100
+++ parser.c 2008-08-11 19:53:04.000000000 +0200
@@ -919,7 +919,7 @@ xmlStringDecodeEntities(xmlParserCtxtPtr
if (str == NULL)
return(NULL);
- if (ctxt->depth > 40) {
+ if ((ctxt->depth > 40) || (ctxt->nbentities >= 500000)) {
ctxt->errNo = XML_ERR_ENTITY_LOOP;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
@@ -959,6 +959,11 @@ xmlStringDecodeEntities(xmlParserCtxtPtr
"String decoding Entity Reference: %.30s\n",
str);
ent = xmlParseStringEntityRef(ctxt, &str);
+ if (ctxt->errNo == 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) {
@@ -1003,6 +1008,11 @@ xmlStringDecodeEntities(xmlParserCtxtPtr
xmlGenericError(xmlGenericErrorContext,
"String decoding PE Reference: %.30s\n", str);
ent = xmlParseStringPEReference(ctxt, &str);
+ if (ctxt->errNo == XML_ERR_ENTITY_LOOP)
+ goto int_error;
+ ctxt->nbentities++;
+ if (ent != NULL)
+ ctxt->nbentities += ent->nbentities;
if (ent != NULL) {
xmlChar *rep;
@@ -1033,6 +1043,10 @@ xmlStringDecodeEntities(xmlParserCtxtPtr
}
buffer[nbchars++] = 0;
return(buffer);
+int_error:
+ if (buffer != NULL)
+ xmlFree(buffer);
+ return(NULL);
}
@@ -2279,6 +2293,9 @@ xmlParseAttValue(xmlParserCtxtPtr ctxt)
}
} else {
ent = xmlParseEntityRef(ctxt);
+ ctxt->nbentities++;
+ if (ent != NULL)
+ ctxt->nbentities += ent->nbentities;
if ((ent != NULL) &&
(ctxt->replaceEntities != 0)) {
xmlChar *rep;
@@ -3337,6 +3354,7 @@ xmlParseEntityDecl(xmlParserCtxtPtr ctxt
xmlChar *ndata = NULL;
int isParameter = 0;
xmlChar *orig = NULL;
+ unsigned long oldnbent = ctxt->nbentities;
GROW;
if ((RAW == '<') && (NXT(1) == '!') &&
@@ -3612,6 +3630,7 @@ xmlParseEntityDecl(xmlParserCtxtPtr ctxt
}
}
if (cur != NULL) {
+ cur->nbentities = ctxt->nbentities - oldnbent;
if (cur->orig != NULL)
xmlFree(orig);
else
@@ -5228,6 +5247,15 @@ xmlParseReference(xmlParserCtxtPtr ctxt)
if (ent == NULL) return;
if (!ctxt->wellFormed)
return;
+ ctxt->nbentities++;
+ if (ctxt->nbentities >= 500000) {
+ ctxt->errNo = XML_ERR_ENTITY_LOOP;
+ if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
+ ctxt->sax->error(ctxt->userData,
+ "Detected entity reference loop\n");
+ ctxt->wellFormed = 0;
+ return;
+ }
if ((ent->name != NULL) &&
(ent->etype != XML_INTERNAL_PREDEFINED_ENTITY)) {
xmlNodePtr list = NULL;
@@ -5285,6 +5313,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
@@ -5308,6 +5337,7 @@ xmlParseReference(xmlParserCtxtPtr ctxt)
ctxt->sax, user_data, ctxt->depth,
value, &list);
ctxt->depth--;
+
} else if (ent->etype ==
XML_EXTERNAL_GENERAL_PARSED_ENTITY) {
ctxt->depth++;
@@ -5321,6 +5351,7 @@ xmlParseReference(xmlParserCtxtPtr ctxt)
ctxt->sax->error(ctxt->userData,
"Internal: invalid entity type\n");
}
+ ent->nbentities = ctxt->nbentities - oldnbent;
if (ret == XML_ERR_ENTITY_LOOP) {
ctxt->errNo = XML_ERR_ENTITY_LOOP;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
@@ -5379,6 +5410,7 @@ xmlParseReference(xmlParserCtxtPtr ctxt)
}
}
}
+ ctxt->nbentities += ent->nbentities;
if ((ctxt->sax != NULL) && (ctxt->sax->reference != NULL) &&
(ctxt->replaceEntities == 0) && (!ctxt->disableSAX)) {
/*
@@ -9251,7 +9283,7 @@ xmlParseCtxtExternalEntity(xmlParserCtxt
xmlChar start[4];
xmlCharEncoding enc;
- if (ctx->depth > 40) {
+ if ((ctx->depth > 40) || (ctx->nbentities >= 500000)) {
return(XML_ERR_ENTITY_LOOP);
}
@@ -9428,7 +9460,8 @@ xmlParseExternalEntityPrivate(xmlDocPtr
xmlChar start[4];
xmlCharEncoding enc;
- if (depth > 40) {
+ if ((depth > 40) ||
+ ((oldctxt != NULL) && (oldctxt->nbentities >= 500000))) {
return(XML_ERR_ENTITY_LOOP);
}
@@ -9572,6 +9605,7 @@ xmlParseExternalEntityPrivate(xmlDocPtr
}
ret = 0;
}
+ oldctxt->nbentities += ctxt->nbentities;
if (sax != NULL)
ctxt->sax = oldsax;
xmlFreeParserCtxt(ctxt);
_______________________________________________
xml mailing list, project page http://xmlsoft.org/
[email protected]
http://mail.gnome.org/mailman/listinfo/xml