--- test/data/billion-laughs.xml (revision 0)
+++ test/data/billion-laughs.xml (revision 781409)
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+]>
+&laugh30;
+ native
--- test/testxml.c (revision 781402)
+++ test/testxml.c (revision 781409)
@@ -36,8 +36,7 @@
return rv;
rv = apr_file_puts("\n"
- "\n", *fd);
+ "\n", *fd);
ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
for (i = 0; i < 5000; i++) {
@@ -75,7 +74,7 @@
for (i = 0; i < 5000; i++) {
rv = apr_file_puts("yummy\n", *fd);
+ "for=\"dinner <>=\">yummy\n", *fd);
ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
}
@@ -103,7 +102,7 @@
a = e->attr;
ABTS_PTR_NOTNULL(tc, a);
ABTS_STR_EQUAL(tc, "for", a->name);
- ABTS_STR_EQUAL(tc, "dinner", a->value);
+ ABTS_STR_EQUAL(tc, "dinner <>=", a->value);
a = a->next;
ABTS_PTR_NOTNULL(tc, a);
ABTS_STR_EQUAL(tc, "roast", a->name);
@@ -149,11 +148,29 @@
ABTS_TRUE(tc, rv != APR_SUCCESS);
}
+static void test_billion_laughs(abts_case *tc, void *data)
+{
+ apr_file_t *fd;
+ apr_xml_parser *parser;
+ apr_xml_doc *doc;
+ apr_status_t rv;
+
+ rv = apr_file_open(&fd, "data/billion-laughs.xml",
+ APR_FOPEN_READ, 0, p);
+ APR_ASSERT_SUCCESS(tc, "open billion-laughs.xml", rv);
+
+ rv = apr_xml_parse_file(p, &parser, &doc, fd, 2000);
+ ABTS_TRUE(tc, rv != APR_SUCCESS);
+
+ apr_file_close(fd);
+}
+
abts_suite *testxml(abts_suite *suite)
{
suite = ADD_SUITE(suite);
abts_run_test(suite, test_xml_parser, NULL);
+ abts_run_test(suite, test_billion_laughs, NULL);
return suite;
}
--- xml/apr_xml.c (revision 781402)
+++ xml/apr_xml.c (revision 781409)
@@ -347,6 +347,25 @@
return APR_SUCCESS;
}
+#if XML_MAJOR_VERSION > 1
+/* Stop the parser if an entity declaration is hit. */
+static void entity_declaration(void *userData, const XML_Char *entityName,
+ int is_parameter_entity, const XML_Char *value,
+ int value_length, const XML_Char *base,
+ const XML_Char *systemId, const XML_Char *publicId,
+ const XML_Char *notationName)
+{
+ apr_xml_parser *parser = userData;
+
+ XML_StopParser(parser->xp, XML_FALSE);
+}
+#else
+/* A noop default_handler. */
+static void default_handler(void *userData, const XML_Char *s, int len)
+{
+}
+#endif
+
APU_DECLARE(apr_xml_parser *) apr_xml_parser_create(apr_pool_t *pool)
{
apr_xml_parser *parser = apr_pcalloc(pool, sizeof(*parser));
@@ -372,6 +391,19 @@
XML_SetElementHandler(parser->xp, start_handler, end_handler);
XML_SetCharacterDataHandler(parser->xp, cdata_handler);
+ /* Prevent the "billion laughs" attack against expat by disabling
+ * internal entity expansion. With 2.x, forcibly stop the parser
+ * if an entity is declared - this is safer and a more obvious
+ * failure mode. With older versions, installing a noop
+ * DefaultHandler means that internal entities will be expanded as
+ * the empty string, which is also sufficient to prevent the
+ * attack. */
+#if XML_MAJOR_VERSION > 1
+ XML_SetEntityDeclHandler(parser->xp, entity_declaration);
+#else
+ XML_SetDefaultHandler(parser->xp, default_handler);
+#endif
+
return parser;
}