On Wed, Jun 13, 2012 at 11:41:22PM +0530, Ashwin Sinha wrote:
> Hi Daniel, list
> Firstly thanks for this wonderful piece of software which we have been
> using for a number of years now. Its super! :)
>
> I write chiefly to get some information regarding CVE2008-3281. We are
> currently using version 2.6.28, and wanted to merge the patch for
> 3281,however the patch solution and the latest libxml versions seem to have
> some differences. Specifically with the use of ctxt->owner in the patch,
> while the latest version does not use it. I tried to search on the list but
> could gather nothing conclusive :(.
>
> I would be really grateful if someone could point me in the right direction
> or give some background for the same.
>
> Chiefly i wanted to know if the patch merge as is of 3281 is sufficient, or
> does the latest version fix some problems in the patch.
> I am referring to the following links
>
> http://svn.gnome.org/viewvc/libxml2/trunk/parser.c?r1=3762&r2=3772
> https://mail.gnome.org/archives/xml/2008-August/msg00034.html
>
> On top of this following seems to have been added
> http://svn.gnome.org/viewvc/libxml2/trunk/parser.c?r1=3772&r2=3773
>
> Any help would be greatly appreciated
Please note that SVN use is deprecated, we use git as GNOME SCM for a
few years now !
Yes, the patch needs to be backported and it may not be trivial. I did
so for version 2.6.26 and I assume it will apply to 2.6.28 without much
troubles. Give it a try,
Daniel
--
Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/
[email protected] | Rpmfind RPM search engine http://rpmfind.net/
http://veillard.com/ | virtualization library http://libvirt.org/
Index: entities.c
===================================================================
--- entities.c (revision 3771)
+++ entities.c (working copy)
@@ -102,7 +102,7 @@ xmlFreeEntity(xmlEntityPtr entity)
dict = entity->doc->dict;
- if ((entity->children) && (entity->owner == 1) &&
+ if ((entity->children) && (entity->owner != 0) &&
(entity == (xmlEntityPtr) entity->children->parent))
xmlFreeNodeList(entity->children);
if (dict != NULL) {
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);
}
--- include/libxml/parser.h.orig 2008-08-28 21:31:24.000000000 +0200
+++ include/libxml/parser.h 2008-08-28 20:55:09.000000000 +0200
@@ -297,6 +297,8 @@ struct _xmlParserCtxt {
*/
xmlError lastError;
xmlParserMode parseMode; /* the parser mode */
+ unsigned long nbentities; /* number of entities references */
+ unsigned long sizeentities; /* size of parsed entities */
};
/**
--- parser.c.orig 2006-04-23 11:39:15.000000000 +0200
+++ parser.c 2008-08-28 21:36:27.000000000 +0200
@@ -80,6 +80,95 @@
#include <zlib.h>
#endif
+static void
+xmlFatalErr(xmlParserCtxtPtr ctxt, xmlParserErrors error, const char *info);
+
+/************************************************************************
+ * *
+ * Arbitrary limits set in the parser. *
+ * *
+ ************************************************************************/
+
+#define XML_PARSER_BIG_ENTITY 1000
+#define XML_PARSER_LOT_ENTITY 5000
+
+/*
+ * XML_PARSER_NON_LINEAR is the threshold where the ratio of parsed entity
+ * replacement over the size in byte of the input indicates that you have
+ * and eponential behaviour. A value of 10 correspond to at least 3 entity
+ * replacement per byte of input.
+ */
+#define XML_PARSER_NON_LINEAR 10
+
+/*
+ * xmlParserEntityCheck
+ *
+ * Function to check non-linear entity expansion behaviour
+ * This is here to detect and stop exponential linear entity expansion
+ * This is not a limitation of the parser but a safety
+ * boundary feature.
+ */
+static int
+xmlParserEntityCheck(xmlParserCtxtPtr ctxt, unsigned long size,
+ xmlEntityPtr ent)
+{
+ unsigned long consumed = 0;
+
+ if (ctxt == NULL)
+ return (0);
+ if (ctxt->lastError.code == XML_ERR_ENTITY_LOOP)
+ return (1);
+ if (size != 0) {
+ /*
+ * Do the check based on the replacement size of the entity
+ */
+ if (size < XML_PARSER_BIG_ENTITY)
+ return(0);
+
+ /*
+ * A limit on the amount of text data reasonably used
+ */
+ if (ctxt->input != NULL) {
+ consumed = ctxt->input->consumed +
+ (ctxt->input->cur - ctxt->input->base);
+ }
+ consumed += ctxt->sizeentities;
+
+ if ((size < XML_PARSER_NON_LINEAR * consumed) &&
+ (ctxt->nbentities * 3 < XML_PARSER_NON_LINEAR * consumed))
+ return (0);
+ } else if (ent != NULL) {
+ /*
+ * use the number of parsed entities in the replacement
+ */
+ size = ent->owner;
+
+ /*
+ * The amount of data parsed counting entities size only once
+ */
+ if (ctxt->input != NULL) {
+ consumed = ctxt->input->consumed +
+ (ctxt->input->cur - ctxt->input->base);
+ }
+ consumed += ctxt->sizeentities;
+
+ /*
+ * Check the density of entities for the amount of data
+ * knowing an entity reference will take at least 3 bytes
+ */
+ if (size * 3 < consumed * XML_PARSER_NON_LINEAR)
+ return (0);
+ } else {
+ /*
+ * strange we got no data for checking just return
+ */
+ return (0);
+ }
+
+ xmlFatalErr(ctxt, XML_ERR_ENTITY_LOOP, NULL);
+ return (1);
+}
+
/**
* xmlParserMaxDepth:
*
@@ -2212,6 +2301,10 @@ xmlStringLenDecodeEntities(xmlParserCtxt
"String decoding Entity Reference: %.30s\n",
str);
ent = xmlParseStringEntityRef(ctxt, &str);
+ if (ctxt->lastError.code == XML_ERR_ENTITY_LOOP)
+ goto int_error;
+ if (ent != NULL)
+ ctxt->nbentities += ent->owner;
if ((ent != NULL) &&
(ent->etype == XML_INTERNAL_PREDEFINED_ENTITY)) {
if (ent->content != NULL) {
@@ -2236,6 +2329,10 @@ xmlStringLenDecodeEntities(xmlParserCtxt
buffer[nbchars++] = *current++;
if (nbchars >
buffer_size - XML_PARSER_BUFFER_SIZE) {
+ if (xmlParserEntityCheck(ctxt, nbchars, ent)) {
+ xmlFree(rep);
+ goto int_error;
+ }
growBuffer(buffer);
}
}
@@ -2258,6 +2355,10 @@ 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;
+ if (ent != NULL)
+ ctxt->nbentities += ent->owner;
if (ent != NULL) {
xmlChar *rep;
@@ -2271,6 +2372,10 @@ xmlStringLenDecodeEntities(xmlParserCtxt
buffer[nbchars++] = *current++;
if (nbchars >
buffer_size - XML_PARSER_BUFFER_SIZE) {
+ if (xmlParserEntityCheck(ctxt, nbchars, ent)) {
+ xmlFree(rep);
+ goto int_error;
+ }
growBuffer(buffer);
}
}
@@ -2294,6 +2399,9 @@ xmlStringLenDecodeEntities(xmlParserCtxt
mem_error:
xmlErrMemory(ctxt, NULL);
+int_error:
+ if (buffer != NULL)
+ xmlFree(buffer);
return(NULL);
}
@@ -3100,6 +3208,9 @@ xmlParseAttValueComplex(xmlParserCtxtPtr
}
} else {
ent = xmlParseEntityRef(ctxt);
+ ctxt->nbentities++;
+ if (ent != NULL)
+ ctxt->nbentities += ent->owner;
if ((ent != NULL) &&
(ent->etype == XML_INTERNAL_PREDEFINED_ENTITY)) {
if (len > buf_size - 10) {
@@ -4342,6 +4453,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 +4663,11 @@ xmlParseEntityDecl(xmlParserCtxtPtr ctxt
}
}
if (cur != NULL) {
+ if ((cur->owner != 0) || (cur->children == NULL)) {
+ cur->owner = ctxt->nbentities - oldnbent;
+ if (cur->owner == 0)
+ cur->owner = 1;
+ }
if (cur->orig != NULL)
xmlFree(orig);
else
@@ -5976,7 +6093,8 @@ xmlParseReference(xmlParserCtxtPtr ctxt)
(ent->children == NULL)) {
ent->children = list;
ent->last = list;
- ent->owner = 1;
+ if (ent->owner == 0)
+ ent->owner = 1;
list->parent = (xmlNodePtr) ent;
} else {
xmlFreeNodeList(list);
@@ -5985,6 +6103,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 +6126,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 +6139,24 @@ xmlParseReference(xmlParserCtxtPtr ctxt)
xmlErrMsgStr(ctxt, XML_ERR_INTERNAL_ERROR,
"invalid entity type found\n", NULL);
}
+ /*
+ * Store the number of entities needing parsing for entity
+ * content and do checkings
+ */
+ if ((ent->owner != 0) || (ent->children == NULL)) {
+ ent->owner = ctxt->nbentities - oldnbent;
+ if (ent->owner == 0)
+ ent->owner = 1;
+ }
+ if (ret == XML_ERR_ENTITY_LOOP) {
+ xmlFatalErr(ctxt, XML_ERR_ENTITY_LOOP, NULL);
+ xmlFreeNodeList(list);
+ return;
+ }
+ if (xmlParserEntityCheck(ctxt, 0, ent)) {
+ xmlFreeNodeList(list);
+ return;
+ }
if (ret == XML_ERR_ENTITY_LOOP) {
xmlFatalErr(ctxt, XML_ERR_ENTITY_LOOP, NULL);
return;
@@ -6037,7 +6175,8 @@ xmlParseReference(xmlParserCtxtPtr ctxt)
(ctxt->parseMode == XML_PARSE_READER)) {
list->parent = (xmlNodePtr) ent;
list = NULL;
- ent->owner = 1;
+ if (ent->owner == 0)
+ ent->owner = 1;
} else {
ent->owner = 0;
while (list != NULL) {
@@ -6054,7 +6193,8 @@ xmlParseReference(xmlParserCtxtPtr ctxt)
#endif /* LIBXML_LEGACY_ENABLED */
}
} else {
- ent->owner = 1;
+ if (ent->owner == 0)
+ ent->owner = 1;
while (list != NULL) {
list->parent = (xmlNodePtr) ent;
if (list->next == NULL)
@@ -6074,6 +6214,8 @@ xmlParseReference(xmlParserCtxtPtr ctxt)
list = NULL;
}
}
+ } else if (ent->owner != 1) {
+ ctxt->nbentities += ent->owner;
}
if ((ctxt->sax != NULL) && (ctxt->sax->reference != NULL) &&
(ctxt->replaceEntities == 0) && (!ctxt->disableSAX)) {
@@ -6176,7 +6318,8 @@ xmlParseReference(xmlParserCtxtPtr ctxt)
break;
cur = next;
}
- ent->owner = 1;
+ if (ent->owner == 0)
+ ent->owner = 1;
#ifdef LIBXML_LEGACY_ENABLED
if (ent->etype == XML_EXTERNAL_GENERAL_PARSED_ENTITY)
xmlAddEntityReference(ent, firstChild, nw);
@@ -6291,6 +6434,11 @@ xmlParseEntityRef(xmlParserCtxtPtr ctxt)
if (RAW == ';') {
NEXT;
/*
+ * Increase the number of entity references parsed
+ */
+ ctxt->nbentities++;
+
+ /*
* Ask first SAX for entity resolution, otherwise try the
* predefined set.
*/
@@ -6462,6 +6610,10 @@ xmlParseStringEntityRef(xmlParserCtxtPtr
if (*ptr == ';') {
ptr++;
/*
+ * Increase the number of entity references parsed
+ */
+ ctxt->nbentities++;
+ /*
* Ask first SAX for entity resolution, otherwise try the
* predefined set.
*/
@@ -6623,6 +6775,11 @@ xmlParsePEReference(xmlParserCtxtPtr ctx
} else {
if (RAW == ';') {
NEXT;
+ /*
+ * Increase the number of entity references parsed
+ */
+ ctxt->nbentities++;
+
if ((ctxt->sax != NULL) &&
(ctxt->sax->getParameterEntity != NULL))
entity = ctxt->sax->getParameterEntity(ctxt->userData,
@@ -6753,6 +6910,11 @@ xmlParseStringPEReference(xmlParserCtxtP
if (cur == ';') {
ptr++;
cur = *ptr;
+ /*
+ * Increase the number of entity references parsed
+ */
+ ctxt->nbentities++;
+
if ((ctxt->sax != NULL) &&
(ctxt->sax->getParameterEntity != NULL))
entity = ctxt->sax->getParameterEntity(ctxt->userData,
@@ -11358,11 +11520,31 @@ xmlParseExternalEntityPrivate(xmlDocPtr
}
ret = XML_ERR_OK;
}
+
+ /*
+ * Record in the parent context the number of entities replacement
+ * done when parsing that reference.
+ */
+ oldctxt->nbentities += ctxt->nbentities;
+ /*
+ * Also record the size of the entity parsed
+ */
+ if (ctxt->input != NULL) {
+ oldctxt->sizeentities += ctxt->input->consumed;
+ oldctxt->sizeentities += (ctxt->input->cur - ctxt->input->base);
+ }
+ /*
+ * And record the last error if any
+ */
+ if (ctxt->lastError.code != XML_ERR_OK)
+ xmlCopyError(&ctxt->lastError, &oldctxt->lastError);
+
if (sax != NULL)
ctxt->sax = oldsax;
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;
@@ -11587,6 +11769,17 @@ xmlParseBalancedChunkMemoryInternal(xmlP
ctxt->myDoc->last = last;
}
+ /*
+ * Record in the parent context the number of entities replacement
+ * done when parsing that reference.
+ */
+ oldctxt->nbentities += ctxt->nbentities;
+ /*
+ * Also record the last error if any
+ */
+ if (ctxt->lastError.code != XML_ERR_OK)
+ xmlCopyError(&ctxt->lastError, &oldctxt->lastError);
+
ctxt->sax = oldsax;
ctxt->dict = NULL;
ctxt->attsDefault = NULL;
@@ -12883,6 +13076,8 @@ xmlCtxtReset(xmlParserCtxtPtr ctxt)
ctxt->depth = 0;
ctxt->charset = XML_CHAR_ENCODING_UTF8;
ctxt->catalogs = NULL;
+ ctxt->nbentities = 0;
+ ctxt->sizeentities = 0;
xmlInitNodeInfoSeq(&ctxt->node_seq);
if (ctxt->attsDefault != NULL) {
_______________________________________________
xml mailing list, project page http://xmlsoft.org/
[email protected]
https://mail.gnome.org/mailman/listinfo/xml