diff --git a/expat/lib/xmlparse.c b/expat/lib/xmlparse.c index c430541f..fd08b4ec 100644 --- a/expat/lib/xmlparse.c +++ b/expat/lib/xmlparse.c @@ -3117,13 +3117,17 @@ storeAtts(XML_Parser parser, const ENCODING *enc, #endif attsSize = n + nDefaultAtts + INIT_ATTS_SIZE; temp = (ATTRIBUTE *)REALLOC((void *)atts, attsSize * sizeof(ATTRIBUTE)); - if (temp == NULL) + if (temp == NULL) { + attsSize = oldAttsSize; return XML_ERROR_NO_MEMORY; + } atts = temp; #ifdef XML_ATTR_INFO temp2 = (XML_AttrInfo *)REALLOC((void *)attInfo, attsSize * sizeof(XML_AttrInfo)); - if (temp2 == NULL) + if (temp2 == NULL) { + attsSize = oldAttsSize; return XML_ERROR_NO_MEMORY; + } attInfo = temp2; #endif if (n > oldAttsSize) @@ -3260,6 +3264,7 @@ storeAtts(XML_Parser parser, const ENCODING *enc, int j; /* hash table index */ unsigned long version = nsAttsVersion; int nsAttsSize = (int)1 << nsAttsPower; + unsigned char oldNsAttsPower = nsAttsPower; /* size of hash table must be at least 2 * (# of prefixed attributes) */ if ((nPrefixes << 1) >> nsAttsPower) { /* true for nsAttsPower = 0 */ NS_ATT *temp; @@ -3269,8 +3274,11 @@ storeAtts(XML_Parser parser, const ENCODING *enc, nsAttsPower = 3; nsAttsSize = (int)1 << nsAttsPower; temp = (NS_ATT *)REALLOC(nsAtts, nsAttsSize * sizeof(NS_ATT)); - if (!temp) + if (!temp) { + /* Restore actual size of memory in nsAtts */ + nsAttsPower = oldNsAttsPower; return XML_ERROR_NO_MEMORY; + } nsAtts = temp; version = 0; /* force re-initialization of nsAtts hash table */ } diff --git a/expat/tests/chardata.h b/expat/tests/chardata.h index e8dc4ce2..0db4b999 100644 --- a/expat/tests/chardata.h +++ b/expat/tests/chardata.h @@ -18,7 +18,7 @@ extern "C" { typedef struct { int count; /* # of chars, < 0 if not set */ - XML_Char data[1024]; + XML_Char data[2048]; } CharData; diff --git a/expat/tests/runtests.c b/expat/tests/runtests.c index 5c9857c9..edfd8880 100644 --- a/expat/tests/runtests.c +++ b/expat/tests/runtests.c @@ -194,6 +194,10 @@ dummy_start_element(void *UNUSED_P(userData), const XML_Char *UNUSED_P(name), const XML_Char **UNUSED_P(atts)) {} +static void XMLCALL +dummy_end_element(void *UNUSED_P(userData), const XML_Char *UNUSED_P(name)) +{} + static void XMLCALL dummy_start_cdata_handler(void *UNUSED_P(userData)) {} @@ -202,6 +206,12 @@ static void XMLCALL dummy_end_cdata_handler(void *UNUSED_P(userData)) {} +static void XMLCALL +dummy_cdata_handler(void *UNUSED_P(userData), + const XML_Char *UNUSED_P(s), + int UNUSED_P(len)) +{} + static void XMLCALL dummy_start_namespace_decl_handler(void *UNUSED_P(userData), const XML_Char *UNUSED_P(prefix), @@ -225,6 +235,11 @@ dummy_unparsed_entity_decl_handler(void *UNUSED_P(userData), const XML_Char *UNUSED_P(notationName)) {} +static void XMLCALL +dummy_default_handler(void *UNUSED_P(userData), + const XML_Char *UNUSED_P(s), + int UNUSED_P(len)) +{} /* * Character & encoding tests. @@ -580,6 +595,13 @@ START_TEST(test_latin1_umlauts) run_character_check(text, utf8); XML_ParserReset(parser, NULL); run_attribute_check(text, utf8); + /* Repeat with a default handler */ + XML_ParserReset(parser, NULL); + XML_SetDefaultHandler(parser, dummy_default_handler); + run_character_check(text, utf8); + XML_ParserReset(parser, NULL); + XML_SetDefaultHandler(parser, dummy_default_handler); + run_attribute_check(text, utf8); } END_TEST @@ -763,6 +785,50 @@ START_TEST(test_really_long_lines) } END_TEST +/* Test cdata processing across a buffer boundary */ +START_TEST(test_really_long_encoded_lines) +{ + /* As above, except that we want to provoke an output buffer + * overflow with a non-trivial encoding. For this we need to pass + * the whole cdata in one go, not byte-by-byte. + */ + void *buffer; + const char *text = + "" + "" + /* 64 chars */ + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+" + /* until we have at least 1024 characters on the line: */ + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+" + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+" + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+" + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+" + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+" + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+" + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+" + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+" + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+" + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+" + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+" + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+" + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+" + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+" + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+" + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+" + ""; + int parse_len = strlen(text); + + /* Need a cdata handler to provoke the code path we want to test */ + XML_SetCharacterDataHandler(parser, dummy_cdata_handler); + buffer = XML_GetBuffer(parser, parse_len); + if (buffer == NULL) + fail("Could not allocate parse buffer"); + memcpy(buffer, text, parse_len); + if (XML_ParseBuffer(parser, parse_len, XML_TRUE) == XML_STATUS_ERROR) + xml_failure(parser); +} +END_TEST + /* * Element event tests. @@ -1022,6 +1088,20 @@ START_TEST(test_ext_entity_set_encoding) } END_TEST +/* Test external entities with no handler */ +START_TEST(test_ext_entity_no_handler) +{ + const char *text = + "\n" + "]>\n" + "&en;"; + + XML_SetDefaultHandler(parser, dummy_default_handler); + run_character_check(text, ""); +} +END_TEST + /* Test UTF-8 BOM is accepted */ static int XMLCALL external_entity_loader_set_bom(XML_Parser parser, @@ -1327,6 +1407,11 @@ START_TEST(test_ext_entity_invalid_parse) "Incomplete character not faulted", XML_ERROR_PARTIAL_CHAR }, + { + "\xe2\x82", + "Incomplete character in CDATA not faulted", + XML_ERROR_PARTIAL_CHAR + }, { NULL, NULL, XML_ERROR_NONE } }; const ExtFaults *fault = faults; @@ -1558,6 +1643,20 @@ START_TEST(test_good_cdata_ascii) CharData_Init(&storage); XML_SetUserData(parser, &storage); XML_SetCharacterDataHandler(parser, accumulate_characters); + /* Add start and end handlers for coverage */ + XML_SetStartCdataSectionHandler(parser, dummy_start_cdata_handler); + XML_SetEndCdataSectionHandler(parser, dummy_end_cdata_handler); + + if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) + xml_failure(parser); + CharData_CheckXMLChars(&storage, expected); + + /* Try again, this time with a default handler */ + XML_ParserReset(parser, NULL); + CharData_Init(&storage); + XML_SetUserData(parser, &storage); + XML_SetCharacterDataHandler(parser, accumulate_characters); + XML_SetDefaultHandler(parser, dummy_default_handler); if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) xml_failure(parser); @@ -1590,6 +1689,115 @@ START_TEST(test_good_cdata_utf16) } END_TEST +/* Test UTF16 conversion of a long cdata string */ + +/* 16 characters: handy macro to reduce visual clutter */ +#define A_TO_P_IN_UTF16 "\0A\0B\0C\0D\0E\0F\0G\0H\0I\0J\0K\0L\0M\0N\0O\0P" + +START_TEST(test_long_cdata_utf16) +{ + /* Test data is: + * + * + */ + const char text[] = + "\0<\0?\0x\0m\0l\0 " + "\0v\0e\0r\0s\0i\0o\0n\0=\0'\0\x31\0.\0\x30\0'\0 " + "\0e\0n\0c\0o\0d\0i\0n\0g\0=\0'\0u\0t\0f\0-\0\x31\0\x36\0'\0?\0>" + "\0<\0a\0>\0<\0!\0[\0C\0D\0A\0T\0A\0[" + /* 64 characters per line */ + A_TO_P_IN_UTF16 A_TO_P_IN_UTF16 A_TO_P_IN_UTF16 A_TO_P_IN_UTF16 + A_TO_P_IN_UTF16 A_TO_P_IN_UTF16 A_TO_P_IN_UTF16 A_TO_P_IN_UTF16 + A_TO_P_IN_UTF16 A_TO_P_IN_UTF16 A_TO_P_IN_UTF16 A_TO_P_IN_UTF16 + A_TO_P_IN_UTF16 A_TO_P_IN_UTF16 A_TO_P_IN_UTF16 A_TO_P_IN_UTF16 + A_TO_P_IN_UTF16 A_TO_P_IN_UTF16 A_TO_P_IN_UTF16 A_TO_P_IN_UTF16 + A_TO_P_IN_UTF16 A_TO_P_IN_UTF16 A_TO_P_IN_UTF16 A_TO_P_IN_UTF16 + A_TO_P_IN_UTF16 A_TO_P_IN_UTF16 A_TO_P_IN_UTF16 A_TO_P_IN_UTF16 + A_TO_P_IN_UTF16 A_TO_P_IN_UTF16 A_TO_P_IN_UTF16 A_TO_P_IN_UTF16 + A_TO_P_IN_UTF16 A_TO_P_IN_UTF16 A_TO_P_IN_UTF16 A_TO_P_IN_UTF16 + A_TO_P_IN_UTF16 A_TO_P_IN_UTF16 A_TO_P_IN_UTF16 A_TO_P_IN_UTF16 + A_TO_P_IN_UTF16 A_TO_P_IN_UTF16 A_TO_P_IN_UTF16 A_TO_P_IN_UTF16 + A_TO_P_IN_UTF16 A_TO_P_IN_UTF16 A_TO_P_IN_UTF16 A_TO_P_IN_UTF16 + A_TO_P_IN_UTF16 A_TO_P_IN_UTF16 A_TO_P_IN_UTF16 A_TO_P_IN_UTF16 + A_TO_P_IN_UTF16 A_TO_P_IN_UTF16 A_TO_P_IN_UTF16 A_TO_P_IN_UTF16 + A_TO_P_IN_UTF16 A_TO_P_IN_UTF16 A_TO_P_IN_UTF16 A_TO_P_IN_UTF16 + A_TO_P_IN_UTF16 A_TO_P_IN_UTF16 A_TO_P_IN_UTF16 A_TO_P_IN_UTF16 + A_TO_P_IN_UTF16 + "\0]\0]\0>\0<\0/\0a\0>"; + const char *expected = + "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP" + "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP" + "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP" + "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP" + "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP" + "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP" + "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP" + "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP" + "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP" + "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP" + "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP" + "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP" + "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP" + "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP" + "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP" + "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP" + "ABCDEFGHIJKLMNOP"; + CharData storage; + void *buffer; + + CharData_Init(&storage); + XML_SetUserData(parser, &storage); + XML_SetCharacterDataHandler(parser, accumulate_characters); + buffer = XML_GetBuffer(parser, sizeof(text) - 1); + if (buffer == NULL) + fail("Could not allocate parse buffer"); + memcpy(buffer, text, sizeof(text) - 1); + if (XML_ParseBuffer(parser, + sizeof(text) - 1, + XML_TRUE) == XML_STATUS_ERROR) + xml_failure(parser); + CharData_CheckXMLChars(&storage, expected); +} +END_TEST + +/* Test handling of multiple unit UTF-16 characters */ +START_TEST(test_multichar_cdata_utf16) +{ + /* Test data is: + * + * + * + * where {MINIM} is U+1d15e (a minim or half-note) + * UTF-16: 0xd834 0xdd5e + * UTF-8: 0xf0 0x9d 0x85 0x9e + * and {CROTCHET} is U+1d15f (a crotchet or quarter-note) + * UTF-16: 0xd834 0xdd5e + * UTF-8: 0xf0 0x9d 0x85 0x9e + */ + const char text[] = + "\0<\0?\0x\0m\0l\0" + " \0v\0e\0r\0s\0i\0o\0n\0=\0'\0\x31\0.\0\x30\0'\0" + " \0e\0n\0c\0o\0d\0i\0n\0g\0=\0'\0u\0t\0f\0-\0""1\0""6\0'" + "\0?\0>\0\n" + "\0<\0a\0>\0<\0!\0[\0C\0D\0A\0T\0A\0[" + "\xd8\x34\xdd\x5e\xd8\x34\xdd\x5f" + "\0]\0]\0>\0<\0/\0a\0>"; + const char *expected = "\xf0\x9d\x85\x9e\xf0\x9d\x85\x9f"; + CharData storage; + + CharData_Init(&storage); + XML_SetUserData(parser, &storage); + XML_SetCharacterDataHandler(parser, accumulate_characters); + + if (_XML_Parse_SINGLE_BYTES(parser, text, sizeof(text) - 1, XML_TRUE) == XML_STATUS_ERROR) + xml_failure(parser); + CharData_CheckXMLChars(&storage, expected); +} +END_TEST + + START_TEST(test_bad_cdata) { struct CaseData { @@ -1644,6 +1852,144 @@ START_TEST(test_bad_cdata) } END_TEST +/* Test failures in UTF-16 CDATA */ +START_TEST(test_bad_cdata_utf16) +{ + struct CaseData { + size_t text_bytes; + const char *text; + enum XML_Error expected_error; + }; + + const char prolog[] = + "\0<\0?\0x\0m\0l\0" + " \0v\0e\0r\0s\0i\0o\0n\0=\0'\0\x31\0.\0\x30\0'\0" + " \0e\0n\0c\0o\0d\0i\0n\0g\0=\0'\0u\0t\0f\0-\0""1\0""6\0'" + "\0?\0>\0\n" + "\0<\0a\0>"; + struct CaseData cases[] = { + {1, "\0", XML_ERROR_UNCLOSED_TOKEN}, + {2, "\0<", XML_ERROR_UNCLOSED_TOKEN}, + {3, "\0<\0", XML_ERROR_UNCLOSED_TOKEN}, + {4, "\0<\0!", XML_ERROR_UNCLOSED_TOKEN}, + {5, "\0<\0!\0", XML_ERROR_UNCLOSED_TOKEN}, + {6, "\0<\0!\0[", XML_ERROR_UNCLOSED_TOKEN}, + {7, "\0<\0!\0[\0", XML_ERROR_UNCLOSED_TOKEN}, + {8, "\0<\0!\0[\0C", XML_ERROR_UNCLOSED_TOKEN}, + {9, "\0<\0!\0[\0C\0", XML_ERROR_UNCLOSED_TOKEN}, + {10, "\0<\0!\0[\0C\0D", XML_ERROR_UNCLOSED_TOKEN}, + {11, "\0<\0!\0[\0C\0D\0", XML_ERROR_UNCLOSED_TOKEN}, + {12, "\0<\0!\0[\0C\0D\0A", XML_ERROR_UNCLOSED_TOKEN}, + {13, "\0<\0!\0[\0C\0D\0A\0", XML_ERROR_UNCLOSED_TOKEN}, + {14, "\0<\0!\0[\0C\0D\0A\0T", XML_ERROR_UNCLOSED_TOKEN}, + {15, "\0<\0!\0[\0C\0D\0A\0T\0", XML_ERROR_UNCLOSED_TOKEN}, + {16, "\0<\0!\0[\0C\0D\0A\0T\0A", XML_ERROR_UNCLOSED_TOKEN}, + {17, "\0<\0!\0[\0C\0D\0A\0T\0A\0", XML_ERROR_UNCLOSED_TOKEN}, + {18, "\0<\0!\0[\0C\0D\0A\0T\0A\0[", + XML_ERROR_UNCLOSED_CDATA_SECTION}, + {19, "\0<\0!\0[\0C\0D\0A\0T\0A\0[\0", + XML_ERROR_UNCLOSED_CDATA_SECTION}, + {20, "\0<\0!\0[\0C\0D\0A\0T\0A\0[\0Z", + XML_ERROR_UNCLOSED_CDATA_SECTION}, + /* Now add a four-byte UTF-16 character */ + {21, "\0<\0!\0[\0C\0D\0A\0T\0A\0[\0Z\xd8", + XML_ERROR_UNCLOSED_CDATA_SECTION}, + {22, "\0<\0!\0[\0C\0D\0A\0T\0A\0[\0Z\xd8\x34", + XML_ERROR_PARTIAL_CHAR}, + {23, "\0<\0!\0[\0C\0D\0A\0T\0A\0[\0Z\xd8\x34\xdd", + XML_ERROR_PARTIAL_CHAR}, + {24, "\0<\0!\0[\0C\0D\0A\0T\0A\0[\0Z\xd8\x34\xdd\x5e", + XML_ERROR_UNCLOSED_CDATA_SECTION} + }; + size_t i; + + for (i = 0; i < sizeof(cases)/sizeof(struct CaseData); i++) { + enum XML_Status actual_status; + enum XML_Error actual_error; + + if (_XML_Parse_SINGLE_BYTES(parser, prolog, sizeof(prolog)-1, + XML_FALSE) == XML_STATUS_ERROR) + xml_failure(parser); + actual_status = _XML_Parse_SINGLE_BYTES(parser, + cases[i].text, + cases[i].text_bytes, + XML_TRUE); + assert(actual_status == XML_STATUS_ERROR); + actual_error = XML_GetErrorCode(parser); + if (actual_error != cases[i].expected_error) { + char message[1024]; + + sprintf(message, + "Expected error %d (%s), got %d (%s) for case %lu\n", + cases[i].expected_error, + XML_ErrorString(cases[i].expected_error), + actual_error, + XML_ErrorString(actual_error), + i+1); + fail(message); + } + XML_ParserReset(parser, NULL); + } +} +END_TEST + +static const char *long_cdata_text = + ""; + +/* Test stopping the parser in cdata handler */ +START_TEST(test_stop_parser_between_cdata_calls) +{ + const char *text = long_cdata_text; + + XML_SetCharacterDataHandler(parser, + clearing_aborting_character_handler); + resumable = XML_FALSE; + expect_failure(text, XML_ERROR_ABORTED, + "Parse not aborted in CDATA handler"); +} +END_TEST + +/* Test suspending the parser in cdata handler */ +START_TEST(test_suspend_parser_between_cdata_calls) +{ + const char *text = long_cdata_text; + enum XML_Status result; + + XML_SetCharacterDataHandler(parser, + clearing_aborting_character_handler); + resumable = XML_TRUE; + result = _XML_Parse_SINGLE_BYTES(parser, text, strlen(text), XML_TRUE); + if (result != XML_STATUS_SUSPENDED) { + if (result == XML_STATUS_ERROR) + xml_failure(parser); + fail("Parse not suspended in CDATA handler"); + } + if (XML_GetErrorCode(parser) != XML_ERROR_NONE) + xml_failure(parser); +} +END_TEST + /* Test memory allocation functions */ START_TEST(test_memory_allocation) { @@ -1707,7 +2053,7 @@ record_cdata_nodefault_handler(void *userData, /* Test XML_DefaultCurrent() passes handling on correctly */ START_TEST(test_default_current) { - const char *text = "hello"; + const char *text = "hell]"; const char *entity_text = "\n" @@ -1802,6 +2148,8 @@ START_TEST(test_set_foreign_dtd) XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS); XML_SetUserData(parser, dtd_text); XML_SetExternalEntityRefHandler(parser, external_entity_loader); + /* Add a default handler to exercise more code paths */ + XML_SetDefaultHandler(parser, dummy_default_handler); if (XML_UseForeignDTD(parser, XML_TRUE) != XML_ERROR_NONE) fail("Could not set foreign DTD"); if (_XML_Parse_SINGLE_BYTES(parser, text1, strlen(text1), @@ -1811,7 +2159,8 @@ START_TEST(test_set_foreign_dtd) /* Ensure that trying to set the DTD after parsing has started * is faulted, even if it's the same setting. */ - if (XML_UseForeignDTD(parser, XML_TRUE) == XML_ERROR_NONE) + if (XML_UseForeignDTD(parser, XML_TRUE) != + XML_ERROR_CANT_CHANGE_FEATURE_ONCE_PARSING) fail("Failed to reject late foreign DTD setting"); /* Ditto for the hash salt */ if (XML_SetHashSalt(parser, 0x23456789)) @@ -1872,7 +2221,12 @@ counting_start_element_handler(void *userData, } if (info->name == NULL) fail("Element not recognised"); - /* Note attribute count is doubled */ + /* The attribute count is twice what you might expect. It is a + * count of items in atts, an array which contains alternating + * attribute names and attribute values. For the naive user this + * is possibly a little unexpected, but it is what the + * documentation in expat.h tells us to expect. + */ count = XML_GetSpecifiedAttributeCount(parser); if (info->attr_count * 2 != count) { fail("Not got expected attribute count"); @@ -1902,6 +2256,7 @@ counting_start_element_handler(void *userData, fail("Attribute has wrong value"); return; } + /* Remember, two entries in atts per attribute (see above) */ atts += 2; } } @@ -1934,6 +2289,12 @@ START_TEST(test_attributes) info[0].attributes = doc_info; info[1].attributes = tag_info; + /* Silence some warnings: doc_info and tag_info are not computable + * at load time, making the variable initialisation harder. + */ + info[0].attributes = doc_info; + info[1].attributes = tag_info; + XML_SetStartElementHandler(parser, counting_start_element_handler); XML_SetUserData(parser, info); if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) @@ -2006,6 +2367,23 @@ START_TEST(test_resume_resuspended) } END_TEST +/* Test that CDATA shows up correctly through a default handler */ +START_TEST(test_cdata_default) +{ + const char *text = ""; + CharData storage; + + CharData_Init(&storage); + XML_SetUserData(parser, &storage); + XML_SetDefaultHandler(parser, accumulate_characters); + + if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), + XML_TRUE) == XML_STATUS_ERROR) + xml_failure(parser); + CharData_CheckXMLChars(&storage, text); +} +END_TEST + /* Test resetting a subordinate parser does exactly nothing */ static int XMLCALL external_entity_resetter(XML_Parser parser, @@ -2290,7 +2668,10 @@ START_TEST(test_explicit_encoding) const char *text1 = "Hello "; const char *text2 = " World"; - /* First say we are UTF-8 */ + /* Just check that we can set the encoding to NULL before starting */ + if (XML_SetEncoding(parser, NULL) != XML_STATUS_OK) + fail("Failed to initialise encoding to NULL"); + /* Say we are UTF-8 */ if (XML_SetEncoding(parser, "utf-8") != XML_STATUS_OK) fail("Failed to set explicit encoding"); if (_XML_Parse_SINGLE_BYTES(parser, text1, strlen(text1), @@ -2308,6 +2689,267 @@ START_TEST(test_explicit_encoding) } END_TEST +/* Test handling of trailing CR (rather than newline) */ +static void XMLCALL +cr_cdata_handler(void *userData, const XML_Char *s, int len) +{ + int *pfound = (int *)userData; + + /* Internal processing turns the CR into a newline for the + * character data handler, but not for the default handler + */ + if (len == 1 && (*s == '\n' || *s == '\r')) + *pfound = 1; +} + +START_TEST(test_trailing_cr) +{ + const char *text = "\r"; + int found_cr; + + /* Try with a character handler, for code coverage */ + XML_SetCharacterDataHandler(parser, cr_cdata_handler); + XML_SetUserData(parser, &found_cr); + found_cr = 0; + if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), + XML_TRUE) == XML_STATUS_OK) + fail("Failed to fault unclosed doc"); + if (found_cr == 0) + fail("Did not catch the carriage return"); + XML_ParserReset(parser, NULL); + + /* Now with a default handler instead */ + XML_SetDefaultHandler(parser, cr_cdata_handler); + XML_SetUserData(parser, &found_cr); + found_cr = 0; + if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), + XML_TRUE) == XML_STATUS_OK) + fail("Failed to fault unclosed doc"); + if (found_cr == 0) + fail("Did not catch default carriage return"); +} +END_TEST + +/* Test trailing CR in an external entity parse */ +static int XMLCALL +external_entity_cr_catcher(XML_Parser parser, + const XML_Char *context, + const XML_Char *UNUSED_P(base), + const XML_Char *UNUSED_P(systemId), + const XML_Char *UNUSED_P(publicId)) +{ + const char *text = "\r"; + XML_Parser ext_parser; + + ext_parser = XML_ExternalEntityParserCreate(parser, context, NULL); + if (ext_parser == NULL) + fail("Could not create external entity parser"); + XML_SetCharacterDataHandler(ext_parser, cr_cdata_handler); + if (_XML_Parse_SINGLE_BYTES(ext_parser, text, strlen(text), + XML_TRUE) == XML_STATUS_ERROR) + xml_failure(ext_parser); + XML_ParserFree(ext_parser); + return XML_STATUS_OK; +} + +static int XMLCALL +external_entity_bad_cr_catcher(XML_Parser parser, + const XML_Char *context, + const XML_Char *UNUSED_P(base), + const XML_Char *UNUSED_P(systemId), + const XML_Char *UNUSED_P(publicId)) +{ + const char *text = "\r"; + XML_Parser ext_parser; + + ext_parser = XML_ExternalEntityParserCreate(parser, context, NULL); + if (ext_parser == NULL) + fail("Could not create external entity parser"); + XML_SetCharacterDataHandler(ext_parser, cr_cdata_handler); + if (_XML_Parse_SINGLE_BYTES(ext_parser, text, strlen(text), + XML_TRUE) == XML_STATUS_OK) + fail("Async entity error not caught"); + if (XML_GetErrorCode(ext_parser) != XML_ERROR_ASYNC_ENTITY) + xml_failure(ext_parser); + XML_ParserFree(ext_parser); + return XML_STATUS_OK; +} + +START_TEST(test_ext_entity_trailing_cr) +{ + const char *text = + "\n" + "]>\n" + "&en;"; + int found_cr; + + XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS); + XML_SetExternalEntityRefHandler(parser, external_entity_cr_catcher); + XML_SetUserData(parser, &found_cr); + found_cr = 0; + if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), + XML_TRUE) != XML_STATUS_OK) + xml_failure(parser); + if (found_cr == 0) + fail("No carriage return found"); + XML_ParserReset(parser, NULL); + + /* Try again with a different trailing CR */ + XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS); + XML_SetExternalEntityRefHandler(parser, external_entity_bad_cr_catcher); + XML_SetUserData(parser, &found_cr); + found_cr = 0; + if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), + XML_TRUE) != XML_STATUS_OK) + xml_failure(parser); + if (found_cr == 0) + fail("No carriage return found"); +} +END_TEST + +/* Test handling of trailing square bracket */ +static void XMLCALL +rsqb_handler(void *userData, const XML_Char *s, int len) +{ + int *pfound = (int *)userData; + + if (len == 1 && *s == ']') + *pfound = 1; +} + +START_TEST(test_trailing_rsqb) +{ + const char *text8 = "]"; + const char text16[] = "\xFF\xFE<\000d\000o\000c\000>\000]\000"; + int found_rsqb; + int text8_len = strlen(text8); + + XML_SetCharacterDataHandler(parser, rsqb_handler); + XML_SetUserData(parser, &found_rsqb); + found_rsqb = 0; + if (_XML_Parse_SINGLE_BYTES(parser, text8, text8_len, + XML_TRUE) == XML_STATUS_OK) + fail("Failed to fault unclosed doc"); + if (found_rsqb == 0) + fail("Did not catch the right square bracket"); + + /* Try again with a different encoding */ + XML_ParserReset(parser, NULL); + XML_SetCharacterDataHandler(parser, rsqb_handler); + XML_SetUserData(parser, &found_rsqb); + found_rsqb = 0; + if (_XML_Parse_SINGLE_BYTES(parser, text16, sizeof(text16)-1, + XML_TRUE) == XML_STATUS_OK) + fail("Failed to fault unclosed doc"); + if (found_rsqb == 0) + fail("Did not catch the right square bracket"); + + /* And finally with a default handler */ + XML_ParserReset(parser, NULL); + XML_SetDefaultHandler(parser, rsqb_handler); + XML_SetUserData(parser, &found_rsqb); + found_rsqb = 0; + if (_XML_Parse_SINGLE_BYTES(parser, text16, sizeof(text16)-1, + XML_TRUE) == XML_STATUS_OK) + fail("Failed to fault unclosed doc"); + if (found_rsqb == 0) + fail("Did not catch the right square bracket"); +} +END_TEST + +/* Test trailing right square bracket in an external entity parse */ +static int XMLCALL +external_entity_rsqb_catcher(XML_Parser parser, + const XML_Char *context, + const XML_Char *UNUSED_P(base), + const XML_Char *UNUSED_P(systemId), + const XML_Char *UNUSED_P(publicId)) +{ + const char *text = "]"; + XML_Parser ext_parser; + + ext_parser = XML_ExternalEntityParserCreate(parser, context, NULL); + if (ext_parser == NULL) + fail("Could not create external entity parser"); + XML_SetCharacterDataHandler(ext_parser, rsqb_handler); + if (_XML_Parse_SINGLE_BYTES(ext_parser, text, strlen(text), + XML_TRUE) != XML_STATUS_ERROR) + fail("Async entity error not caught"); + if (XML_GetErrorCode(ext_parser) != XML_ERROR_ASYNC_ENTITY) + xml_failure(ext_parser); + XML_ParserFree(ext_parser); + return XML_STATUS_OK; +} + +START_TEST(test_ext_entity_trailing_rsqb) +{ + const char *text = + "\n" + "]>\n" + "&en;"; + int found_rsqb; + + XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS); + XML_SetExternalEntityRefHandler(parser, external_entity_rsqb_catcher); + XML_SetUserData(parser, &found_rsqb); + found_rsqb = 0; + if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), + XML_TRUE) != XML_STATUS_OK) + xml_failure(parser); + if (found_rsqb == 0) + fail("No right square bracket found"); +} +END_TEST + +/* Test CDATA handling in an external entity */ +static int XMLCALL +external_entity_good_cdata_ascii(XML_Parser parser, + const XML_Char *context, + const XML_Char *UNUSED_P(base), + const XML_Char *UNUSED_P(systemId), + const XML_Char *UNUSED_P(publicId)) +{ + const char *text = + "Hello, world!]]>"; + const char *expected = "Hello, world!"; + CharData storage; + XML_Parser ext_parser; + + CharData_Init(&storage); + ext_parser = XML_ExternalEntityParserCreate(parser, context, NULL); + if (ext_parser == NULL) + fail("Could not create external entity parser"); + XML_SetUserData(ext_parser, &storage); + XML_SetCharacterDataHandler(ext_parser, accumulate_characters); + + if (_XML_Parse_SINGLE_BYTES(ext_parser, text, strlen(text), + XML_TRUE) == XML_STATUS_ERROR) + xml_failure(ext_parser); + CharData_CheckXMLChars(&storage, expected); + + XML_ParserFree(ext_parser); + return XML_STATUS_OK; +} + +START_TEST(test_ext_entity_good_cdata) +{ + const char *text = + "\n" + "]>\n" + "&en;"; + + XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS); + XML_SetExternalEntityRefHandler(parser, + external_entity_good_cdata_ascii); + if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), + XML_TRUE) != XML_STATUS_OK) + xml_failure(parser); +} +END_TEST + /* Test user parameter settings */ /* Variable holding the expected handler userData */ static void *handler_data = NULL; @@ -2423,16 +3065,35 @@ END_TEST /* Test that an explicit external entity handler argument replaces * the parser as the first argument. + * + * We do not call the first parameter to the external entity handler + * 'parser' for once, since the first time the handler is called it + * will actually be a text string. We need to be able to access the + * global 'parser' variable to create our external entity parser from, + * since there are code paths we need to ensure get executed. */ static int XMLCALL -external_entity_ref_param_checker(XML_Parser parser, - const XML_Char *UNUSED_P(context), +external_entity_ref_param_checker(XML_Parser parameter, + const XML_Char *context, const XML_Char *UNUSED_P(base), const XML_Char *UNUSED_P(systemId), const XML_Char *UNUSED_P(publicId)) { - if ((void *)parser != handler_data) + const char *text = ""; + XML_Parser ext_parser; + + if ((void *)parameter != handler_data) fail("External entity ref handler parameter not correct"); + + /* Here we use the global 'parser' variable */ + ext_parser = XML_ExternalEntityParserCreate(parser, context, NULL); + if (ext_parser == NULL) + fail("Could not create external entity parser"); + if (_XML_Parse_SINGLE_BYTES(ext_parser, text, strlen(text), + XML_TRUE) == XML_STATUS_ERROR) + xml_failure(ext_parser); + + XML_ParserFree(ext_parser); return XML_STATUS_OK; } @@ -2738,6 +3399,85 @@ START_TEST(test_invalid_tag_in_dtd) } END_TEST +/* Test conditional inclusion (IGNORE) */ +static int XMLCALL +external_entity_load_ignore(XML_Parser parser, + const XML_Char *context, + const XML_Char *UNUSED_P(base), + const XML_Char *UNUSED_P(systemId), + const XML_Char *UNUSED_P(publicId)) +{ + const char *text = "]]>"; + XML_Parser ext_parser; + + ext_parser = XML_ExternalEntityParserCreate(parser, context, NULL); + if (ext_parser == NULL) + fail("Could not create external entity parser"); + if (_XML_Parse_SINGLE_BYTES(ext_parser, text, strlen(text), + XML_TRUE) == XML_STATUS_ERROR) + xml_failure(parser); + + XML_ParserFree(ext_parser); + return XML_STATUS_OK; +} + +START_TEST(test_ignore_section) +{ + const char *text = + "\n" + "&entity;"; + const char *expected = + "]]>\n&entity;"; + CharData storage; + + CharData_Init(&storage); + XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS); + XML_SetUserData(parser, &storage); + XML_SetExternalEntityRefHandler(parser, external_entity_load_ignore); + XML_SetDefaultHandler(parser, accumulate_characters); + XML_SetStartDoctypeDeclHandler(parser, dummy_start_doctype_handler); + XML_SetEndDoctypeDeclHandler(parser, dummy_end_doctype_handler); + XML_SetElementDeclHandler(parser, dummy_element_decl_handler); + XML_SetStartElementHandler(parser, dummy_start_element); + XML_SetEndElementHandler(parser, dummy_end_element); + if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), + XML_TRUE) == XML_STATUS_ERROR) + xml_failure(parser); + CharData_CheckXMLChars(&storage, expected); +} +END_TEST + +/* Test mis-formatted conditional exclusion */ +START_TEST(test_bad_ignore_section) +{ + const char *text = + "\n" + "&entity;"; + ExtFaults faults[] = { + { + "", + "Invalid XML character not faulted", + XML_ERROR_INVALID_TOKEN + }, + { NULL, NULL, XML_ERROR_NONE } + }; + ExtFaults *fault; + + for (fault = &faults[0]; fault->parse_text != NULL; fault++) { + XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS); + XML_SetExternalEntityRefHandler(parser, external_entity_faulter); + XML_SetUserData(parser, fault); + expect_failure(text, XML_ERROR_EXTERNAL_ENTITY_HANDLING, + "Incomplete IGNORE section not failed"); + XML_ParserReset(parser, NULL); + } +} +END_TEST /* * Namespaces tests. @@ -3065,12 +3805,36 @@ START_TEST(test_ns_prefix_with_empty_uri_4) } END_TEST +/* Test with non-xmlns prefix */ +START_TEST(test_ns_unbound_prefix) +{ + const char *text = + "\n" + " \n" + "]>\n" + ""; + + if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), + XML_TRUE) != XML_STATUS_ERROR) + fail("Unbound prefix incorrectly passed"); + if (XML_GetErrorCode(parser) != XML_ERROR_UNBOUND_PREFIX) + xml_failure(parser); +} +END_TEST + START_TEST(test_ns_default_with_empty_uri) { const char *text = "\n" " \n" ""; + /* Add some handlers to exercise extra code paths */ + XML_SetStartNamespaceDeclHandler(parser, + dummy_start_namespace_decl_handler); + XML_SetEndNamespaceDeclHandler(parser, + dummy_end_namespace_decl_handler); if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) xml_failure(parser); } @@ -3089,6 +3853,33 @@ START_TEST(test_ns_duplicate_attrs_diff_prefixes) } END_TEST +START_TEST(test_ns_duplicate_hashes) +{ + /* The hash of an attribute is calculated as the hash of its URI + * concatenated with a space followed by its name (after the + * colon). We wish to generate attributes with the same hash + * value modulo the attribute table size so that we can check that + * the attribute hash table works correctly. The attribute hash + * table size will be the smallest power of two greater than the + * number of attributes, but at least eight. There is + * unfortunately no programmatic way of getting the hash or the + * table size at user level, but the test code coverage percentage + * will drop if the hashes cease to point to the same row. + * + * The cunning plan is to have few enough attributes to have a + * reliable table size of 8, and have the single letter attribute + * names be 8 characters apart, producing a hash which will be the + * same modulo 8. + */ + const char *text = + ""; + if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), + XML_TRUE) == XML_STATUS_ERROR) + xml_failure(parser); +} +END_TEST + /* Regression test for SF bug #695401: unbound prefix. */ START_TEST(test_ns_unbound_prefix_on_attribute) { @@ -3156,6 +3947,76 @@ START_TEST(test_ns_long_element) } END_TEST +/* Test mixed population of prefixed and unprefixed attributes */ +START_TEST(test_ns_mixed_prefix_atts) +{ + const char *text = + "" + ""; + + if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), + XML_TRUE) == XML_STATUS_ERROR) + xml_failure(parser); +} +END_TEST + +/* Test having a long namespaced element name inside a short one. + * This exercises some internal buffer reallocation that is shared + * across elements with the same namespace URI. + */ +START_TEST(test_ns_extend_uri_buffer) +{ + const char *text = + "" + " " + ""; + if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), + XML_TRUE) == XML_STATUS_ERROR) + xml_failure(parser); +} +END_TEST + +/* Test that xmlns is correctly rejected as an attribute in the xmlns + * namespace, but not in other namespaces + */ +START_TEST(test_ns_reserved_attributes) +{ + const char *text1 = + ""; + const char *text2 = + ""; + expect_failure(text1, XML_ERROR_RESERVED_PREFIX_XMLNS, + "xmlns not rejected as an attribute"); + XML_ParserReset(parser, NULL); + if (_XML_Parse_SINGLE_BYTES(parser, text2, strlen(text2), + XML_TRUE) == XML_STATUS_ERROR) + xml_failure(parser); +} +END_TEST + +/* Test more reserved attributes */ +START_TEST(test_ns_reserved_attributes_2) +{ + const char *text1 = + ""; + const char *text2 = + ""; + const char *text3 = + ""; + + expect_failure(text1, XML_ERROR_RESERVED_PREFIX_XML, + "xml not rejected as an attribute"); + XML_ParserReset(parser, NULL); + expect_failure(text2, XML_ERROR_RESERVED_NAMESPACE_URI, + "Use of w3.org URL not faulted"); + XML_ParserReset(parser, NULL); + expect_failure(text3, XML_ERROR_RESERVED_NAMESPACE_URI, + "Use of w3.org xmlns URL not faulted"); +} +END_TEST /* Control variable; the number of times duff_allocator() will successfully allocate */ #define ALLOC_ALWAYS_SUCCEED (-1) @@ -3225,115 +4086,6 @@ START_TEST(test_misc_alloc_create_parser_with_encoding) } END_TEST -/* Test the effects of allocation failure in simple namespace parsing. - * Based on test_ns_default_with_empty_uri() - */ -START_TEST(test_misc_alloc_ns) -{ - XML_Memory_Handling_Suite memsuite = { duff_allocator, realloc, free }; - const char *text = - "\n" - " \n" - ""; - unsigned int i; - int repeated = 0; - XML_Char ns_sep[2] = { ' ', '\0' }; - - allocation_count = ALLOC_ALWAYS_SUCCEED; - parser = XML_ParserCreate_MM(NULL, &memsuite, ns_sep); - if (parser == NULL) { - fail("Parser not created"); - } else { - for (i = 0; i < 10; i++) { - /* Repeat some tests with the same allocation count to - * catch cached allocations not freed by XML_ParserReset() - */ - if (repeated < 2 && i == 3) { - i--; - repeated++; - } - if (repeated == 2 && i == 5) { - i = 3; - repeated++; - } - allocation_count = i; - if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), XML_TRUE) != XML_STATUS_ERROR) - break; - XML_ParserReset(parser, NULL); - } - if (i == 0) - fail("Parsing worked despite failing allocations"); - else if (i == 10) - fail("Parsing failed even at allocation count 10"); - } -} -END_TEST - -/* Test XML_ParseBuffer interface with namespace and a dicky allocator */ -START_TEST(test_misc_alloc_ns_parse_buffer) -{ - XML_Memory_Handling_Suite memsuite = { duff_allocator, realloc, free }; - XML_Char ns_sep[2] = { ' ', '\0' }; - const char *text = "Hello"; - void *buffer; - - /* Make sure the basic parser is allocated */ - allocation_count = ALLOC_ALWAYS_SUCCEED; - parser = XML_ParserCreate_MM(NULL, &memsuite, ns_sep); - if (parser == NULL) - fail("Parser not created"); - - /* Try a parse before the start of the world */ - /* (Exercises new code path) */ - allocation_count = 0; - if (XML_ParseBuffer(parser, 0, XML_FALSE) != XML_STATUS_ERROR) - fail("Pre-init XML_ParseBuffer not faulted"); - if (XML_GetErrorCode(parser) != XML_ERROR_NO_MEMORY) - fail("Pre-init XML_ParseBuffer faulted for wrong reason"); - - /* Now with actual memory allocation */ - allocation_count = ALLOC_ALWAYS_SUCCEED; - if (XML_ParseBuffer(parser, 0, XML_FALSE) != XML_STATUS_OK) - xml_failure(parser); - - /* Check that resuming an unsuspended parser is faulted */ - if (XML_ResumeParser(parser) != XML_STATUS_ERROR) - fail("Resuming unsuspended parser not faulted"); - if (XML_GetErrorCode(parser) != XML_ERROR_NOT_SUSPENDED) - xml_failure(parser); - - /* Get the parser into suspended state */ - XML_SetCharacterDataHandler(parser, clearing_aborting_character_handler); - resumable = XML_TRUE; - buffer = XML_GetBuffer(parser, strlen(text)); - if (buffer == NULL) - fail("Could not acquire parse buffer"); - memcpy(buffer, text, strlen(text)); - if (XML_ParseBuffer(parser, strlen(text), - XML_TRUE) != XML_STATUS_SUSPENDED) - xml_failure(parser); - if (XML_GetErrorCode(parser) != XML_ERROR_NONE) - xml_failure(parser); - if (XML_ParseBuffer(parser, strlen(text), XML_TRUE) != XML_STATUS_ERROR) - fail("Suspended XML_ParseBuffer not faulted"); - if (XML_GetErrorCode(parser) != XML_ERROR_SUSPENDED) - xml_failure(parser); - if (XML_GetBuffer(parser, strlen(text)) != NULL) - fail("Suspended XML_GetBuffer not faulted"); - - /* Get it going again and complete the world */ - XML_SetCharacterDataHandler(parser, NULL); - if (XML_ResumeParser(parser) != XML_STATUS_OK) - xml_failure(parser); - if (XML_ParseBuffer(parser, strlen(text), XML_TRUE) != XML_STATUS_ERROR) - fail("Post-finishing XML_ParseBuffer not faulted"); - if (XML_GetErrorCode(parser) != XML_ERROR_FINISHED) - xml_failure(parser); - if (XML_GetBuffer(parser, strlen(text)) != NULL) - fail("Post-finishing XML_GetBuffer not faulted"); -} -END_TEST - /* Test that freeing a NULL parser doesn't cause an explosion. * (Not actually tested anywhere else) */ @@ -3437,6 +4189,154 @@ alloc_teardown(void) basic_teardown(); } + +/* Test the effects of allocation failures on a straightforward parse */ +START_TEST(test_alloc_parse_pi) +{ + const char *text = + "\n" + "\n" + "" + "Hello, world" + ""; + int i; + int repeat = 0; +#define MAX_ALLOC_COUNT 10 + + for (i = 0; i < MAX_ALLOC_COUNT; i++) { + allocation_count = i; + /* Repeat some counts because of cached memory */ + if (i == 2 && repeat == 2) { + i -= 2; + repeat++; + } else if ((i == 1 && repeat < 2) || + (i == 1 && repeat > 2 && repeat < 5)) { + i--; + repeat++; + } + XML_SetProcessingInstructionHandler(parser, dummy_pi_handler); + if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), + XML_TRUE) != XML_STATUS_ERROR) + break; + XML_ParserReset(parser, NULL); + } + if (i == 0) + fail("Parse succeeded despite failing allocator"); + if (i == MAX_ALLOC_COUNT) + fail("Parse failed with max allocations"); +#undef MAX_ALLOC_COUNT +} +END_TEST + +START_TEST(test_alloc_parse_pi_2) +{ + const char *text = + "\n" + "" + "Hello, world" + "\n" + ""; + int i; + int repeat = 0; +#define MAX_ALLOC_COUNT 10 + + for (i = 0; i < MAX_ALLOC_COUNT; i++) { + allocation_count = i; + /* Repeat some counts because of cached memory */ + if (i == 2 && repeat == 1) { + i -= 2; + repeat++; + } else if ((i == 1 && repeat < 1) || + (i == 1 && repeat > 1 && repeat < 4)) { + i--; + repeat++; + } + XML_SetProcessingInstructionHandler(parser, dummy_pi_handler); + if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), + XML_TRUE) != XML_STATUS_ERROR) + break; + XML_ParserReset(parser, NULL); + } + if (i == 0) + fail("Parse succeeded despite failing allocator"); + if (i == MAX_ALLOC_COUNT) + fail("Parse failed with max allocations"); +#undef MAX_ALLOC_COUNT +} +END_TEST + +START_TEST(test_alloc_parse_comment) +{ + const char *text = + "\n" + "" + "Hi"; + int i; + int repeat = 0; +#define MAX_ALLOC_COUNT 10 + + for (i = 0; i < MAX_ALLOC_COUNT; i++) { + /* Repeat some counts because of cached memory */ + if (i == 2 && repeat == 2) { + i -= 2; + repeat++; + } else if ((i == 1 && repeat < 2) || + (i == 1 && repeat > 2 && repeat < 5)) { + i--; + repeat++; + } + allocation_count = i; + XML_SetCommentHandler(parser, dummy_comment_handler); + if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), + XML_TRUE) != XML_STATUS_ERROR) + break; + XML_ParserReset(parser, NULL); + } + if (i == 0) + fail("Parse succeeded despite failing allocator"); + if (i == MAX_ALLOC_COUNT) + fail("Parse failed with max allocations"); +#undef MAX_ALLOC_COUNT +} +END_TEST + +START_TEST(test_alloc_parse_comment_2) +{ + const char *text = + "\n" + "" + "Hello, world" + "" + ""; + int i; + int repeat = 0; +#define MAX_ALLOC_COUNT 10 + + for (i = 0; i < MAX_ALLOC_COUNT; i++) { + allocation_count = i; + /* Repeat some counts because of cached memory */ + if (i == 2 && repeat == 1) { + i -= 2; + repeat++; + } else if ((i == 1 && repeat < 1) || + (i == 1 && repeat > 1 && repeat < 4)) { + i--; + repeat++; + } + XML_SetCommentHandler(parser, dummy_comment_handler); + if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), + XML_TRUE) != XML_STATUS_ERROR) + break; + XML_ParserReset(parser, NULL); + } + if (i == 0) + fail("Parse succeeded despite failing allocator"); + if (i == MAX_ALLOC_COUNT) + fail("Parse failed with max allocations"); +#undef MAX_ALLOC_COUNT +} +END_TEST + static int XMLCALL external_entity_duff_loader(XML_Parser parser, const XML_Char *context, @@ -3699,6 +4599,71 @@ START_TEST(test_alloc_external_entity) } END_TEST +/* Test more allocation failure paths */ +static int XMLCALL +external_entity_alloc_set_encoding(XML_Parser parser, + const XML_Char *context, + const XML_Char *UNUSED_P(base), + const XML_Char *UNUSED_P(systemId), + const XML_Char *UNUSED_P(publicId)) +{ + /* As for external_entity_loader_set_encoding() */ + const char *text = + "" + "\xC3\xA9"; + XML_Parser ext_parser; + enum XML_Status status; + + ext_parser = XML_ExternalEntityParserCreate(parser, context, NULL); + if (ext_parser == NULL) + return 0; + if (!XML_SetEncoding(ext_parser, "utf-8")) { + XML_ParserFree(ext_parser); + return 0; + } + status = _XML_Parse_SINGLE_BYTES(ext_parser, text, strlen(text), + XML_TRUE); + XML_ParserFree(ext_parser); + if (status == XML_STATUS_ERROR) + return 0; + return 1; +} + +START_TEST(test_alloc_ext_entity_set_encoding) +{ + const char *text = + "\n" + "]>\n" + "&en;"; + int i; + int repeat = 0; +#define MAX_ALLOCATION_COUNT 20 + + for (i = 0; i < MAX_ALLOCATION_COUNT; i++) { + /* Repeat some counts to get round caching */ + if ((i == 2 && repeat < 3) || + (i == 3 && repeat < 6) || + (i == 4 && repeat == 6)) { + i--; + repeat++; + } + XML_SetExternalEntityRefHandler(parser, + external_entity_alloc_set_encoding); + allocation_count = i; + if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), + XML_TRUE) == XML_STATUS_OK) + break; + allocation_count = -1; + XML_ParserReset(parser, NULL); + } + if (i == 0) + fail("Encoding check succeeded despite failing allocator"); + if (i == MAX_ALLOCATION_COUNT) + fail("Encoding failed at max allocation count"); +#undef MAX_ALLOCATION_COUNT +} +END_TEST static int XMLCALL unknown_released_encoding_handler(void *UNUSED_P(data), @@ -3849,6 +4814,588 @@ START_TEST(test_alloc_set_base) END_TEST +static void +nsalloc_setup(void) +{ + XML_Memory_Handling_Suite memsuite = { + duff_allocator, + duff_reallocator, + free + }; + XML_Char ns_sep[2] = { ' ', '\0' }; + + /* Ensure the parser creation will go through */ + allocation_count = ALLOC_ALWAYS_SUCCEED; + reallocation_count = REALLOC_ALWAYS_SUCCEED; + parser = XML_ParserCreate_MM(NULL, &memsuite, ns_sep); + if (parser == NULL) + fail("Parser not created"); +} + +static void +nsalloc_teardown(void) +{ + basic_teardown(); +} + + +/* Test the effects of allocation failure in simple namespace parsing. + * Based on test_ns_default_with_empty_uri() + */ +START_TEST(test_nsalloc_xmlns) +{ + const char *text = + "\n" + " \n" + ""; + unsigned int i; + int repeated = 0; + + for (i = 0; i < 10; i++) { + /* Repeat some tests with the same allocation count to + * catch cached allocations not freed by XML_ParserReset() + */ + if ((i == 4 && repeated == 3) || + (i == 7 && repeated == 8)) { + i -= 2; + repeated++; + } + else if ((i == 2 && repeated < 2) || + (i == 3 && repeated < 3) || + (i == 3 && repeated > 3 && repeated < 7) || + (i == 5 && repeated < 8)) { + i--; + repeated++; + } + allocation_count = i; + /* Exercise more code paths with a default handler */ + XML_SetDefaultHandler(parser, dummy_default_handler); + if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), + XML_TRUE) != XML_STATUS_ERROR) + break; + XML_ParserReset(parser, NULL); + } + if (i == 0) + fail("Parsing worked despite failing allocations"); + else if (i == 10) + fail("Parsing failed even at allocation count 10"); +} +END_TEST + +/* Test XML_ParseBuffer interface with namespace and a dicky allocator */ +START_TEST(test_nsalloc_parse_buffer) +{ + const char *text = "Hello"; + void *buffer; + + /* Try a parse before the start of the world */ + /* (Exercises new code path) */ + allocation_count = 0; + if (XML_ParseBuffer(parser, 0, XML_FALSE) != XML_STATUS_ERROR) + fail("Pre-init XML_ParseBuffer not faulted"); + if (XML_GetErrorCode(parser) != XML_ERROR_NO_MEMORY) + fail("Pre-init XML_ParseBuffer faulted for wrong reason"); + + /* Now with actual memory allocation */ + allocation_count = ALLOC_ALWAYS_SUCCEED; + if (XML_ParseBuffer(parser, 0, XML_FALSE) != XML_STATUS_OK) + xml_failure(parser); + + /* Check that resuming an unsuspended parser is faulted */ + if (XML_ResumeParser(parser) != XML_STATUS_ERROR) + fail("Resuming unsuspended parser not faulted"); + if (XML_GetErrorCode(parser) != XML_ERROR_NOT_SUSPENDED) + xml_failure(parser); + + /* Get the parser into suspended state */ + XML_SetCharacterDataHandler(parser, clearing_aborting_character_handler); + resumable = XML_TRUE; + buffer = XML_GetBuffer(parser, strlen(text)); + if (buffer == NULL) + fail("Could not acquire parse buffer"); + memcpy(buffer, text, strlen(text)); + if (XML_ParseBuffer(parser, strlen(text), + XML_TRUE) != XML_STATUS_SUSPENDED) + xml_failure(parser); + if (XML_GetErrorCode(parser) != XML_ERROR_NONE) + xml_failure(parser); + if (XML_ParseBuffer(parser, strlen(text), XML_TRUE) != XML_STATUS_ERROR) + fail("Suspended XML_ParseBuffer not faulted"); + if (XML_GetErrorCode(parser) != XML_ERROR_SUSPENDED) + xml_failure(parser); + if (XML_GetBuffer(parser, strlen(text)) != NULL) + fail("Suspended XML_GetBuffer not faulted"); + + /* Get it going again and complete the world */ + XML_SetCharacterDataHandler(parser, NULL); + if (XML_ResumeParser(parser) != XML_STATUS_OK) + xml_failure(parser); + if (XML_ParseBuffer(parser, strlen(text), XML_TRUE) != XML_STATUS_ERROR) + fail("Post-finishing XML_ParseBuffer not faulted"); + if (XML_GetErrorCode(parser) != XML_ERROR_FINISHED) + xml_failure(parser); + if (XML_GetBuffer(parser, strlen(text)) != NULL) + fail("Post-finishing XML_GetBuffer not faulted"); +} +END_TEST + +/* Check handling of long prefix names (pool growth) */ +START_TEST(test_nsalloc_long_prefix) +{ + const char *text = + "<" + /* 64 characters per line */ + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" + ":foo xmlns:" + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" + "='http://example.com/'>" + ""; + int i; +#define MAX_ALLOC_COUNT 10 + int repeated = 0; + + for (i = 0; i < MAX_ALLOC_COUNT; i++) { + /* Repeat some tests with the same allocation count to + * catch cached allocations not freed by XML_ParserReset() + */ + if ((i == 4 && repeated == 3) || + (i == 4 && repeated == 6)) { + i -= 2; + repeated++; + } + else if ((i == 2 && repeated < 2) || + (i == 3 && repeated < 3) || + (i == 3 && repeated > 3 && repeated < 6) || + (i == 3 && repeated > 6 && repeated < 9)) { + i--; + repeated++; + } + allocation_count = i; + if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), + XML_TRUE) != XML_STATUS_ERROR) + break; + XML_ParserReset(parser, NULL); + } + if (i == 0) + fail("Parsing worked despite failing allocations"); + else if (i == MAX_ALLOC_COUNT) + fail("Parsing failed even at max allocation count"); +} +#undef MAX_ALLOC_COUNT +END_TEST + +/* Check handling of long uri names (pool growth) */ +START_TEST(test_nsalloc_long_uri) +{ + const char *text = + "" + ""; + int i; +#define MAX_ALLOC_COUNT 15 + int repeated = 0; + + for (i = 0; i < MAX_ALLOC_COUNT; i++) { + /* Repeat some tests with the same allocation count to + * catch cached allocations not freed by XML_ParserReset() + */ + if ((i == 3 && repeated == 1) || + (i == 6 && repeated == 4)) { + i -= 2; + repeated++; + } + else if ((i == 2 && (repeated == 0 || repeated == 2)) || + (i == 4 && repeated == 3) || + (i == 8 && repeated == 5)) { + i--; + repeated++; + } + allocation_count = i; + if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), + XML_TRUE) != XML_STATUS_ERROR) + break; + XML_ParserReset(parser, NULL); + } + if (i == 0) + fail("Parsing worked despite failing allocations"); + else if (i == MAX_ALLOC_COUNT) + fail("Parsing failed even at max allocation count"); +} +#undef MAX_ALLOC_COUNT +END_TEST + +/* Test handling of long attribute names with prefixes */ +START_TEST(test_nsalloc_long_attr) +{ + const char *text = + "" + ""; + int i; +#define MAX_ALLOC_COUNT 20 + int repeated = 0; + + for (i = 0; i < MAX_ALLOC_COUNT; i++) { + /* Repeat some tests with the same allocation count to + * catch cached allocation not freed by XML_ParserReset() + */ + if ((i == 4 && repeated < 6) || + (i == 7 && repeated == 8) || + (i == 10 && repeated == 10)) { + i -= 2; + repeated++; + } + else if ((i == 2 && repeated < 2) || + (i == 3 && + (repeated == 2 || repeated == 4 || repeated == 6)) || + (i == 5 && repeated == 7) || + (i == 6 && repeated == 9)) { + i--; + repeated++; + } + allocation_count = i; + if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), + XML_TRUE) != XML_STATUS_ERROR) + break; + XML_ParserReset(parser, NULL); + } + if (i == 0) + fail("Parsing worked despite failing allocations"); + else if (i == MAX_ALLOC_COUNT) + fail("Parsing failed even at max allocation count"); +} +#undef MAX_ALLOC_COUNT +END_TEST + +/* Test handling of an attribute name with a long namespace prefix */ +START_TEST(test_nsalloc_long_attr_prefix) +{ + const char *text = + "" + ""; + const char *elemstr[] = { + "http://example.org/ e foo", + "http://example.org/ a " + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" + }; + int i; +#define MAX_ALLOC_COUNT 15 + int repeated = 0; + + for (i = 0; i < MAX_ALLOC_COUNT; i++) { + /* Repeat some counts to flush out cached allocations */ + if ((i == 4 && (repeated == 3 || repeated == 6)) || + (i == 7 && repeated == 9) || + (i == 10 && repeated == 12)) { + i -= 2; + repeated++; + } + else if ((i == 2 && repeated < 2) || + (i == 3 && + (repeated == 2 || + repeated == 4 || + repeated == 5 || + repeated == 7)) || + (i == 5 && repeated == 8) || + (i == 6 && repeated == 10) || + (i == 8 && repeated == 11)) { + i--; + repeated++; + } + allocation_count = i; + XML_SetReturnNSTriplet(parser, XML_TRUE); + XML_SetUserData(parser, elemstr); + XML_SetElementHandler(parser, + triplet_start_checker, + triplet_end_checker); + if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), + XML_TRUE) != XML_STATUS_ERROR) + break; + XML_ParserReset(parser, NULL); + } + if (i == 0) + fail("Parsing worked despite failing allocations"); + else if (i == MAX_ALLOC_COUNT) + fail("Parsing failed even at max allocation count"); +} +#undef MAX_ALLOC_COUNT +END_TEST + +/* Test attribute handling in the face of a dodgy reallocator */ +START_TEST(test_nsalloc_realloc_attributes) +{ + const char *text = + "" + ""; + int i; +#define MAX_REALLOC_COUNT 10 + + for (i = 0; i < MAX_REALLOC_COUNT; i++) { + reallocation_count = i; + if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), + XML_TRUE) != XML_STATUS_ERROR) + break; + XML_ParserReset(parser, NULL); + } + if (i == 0) + fail("Parsing worked despite failing reallocations"); + else if (i == MAX_REALLOC_COUNT) + fail("Parsing failed at max reallocation count"); +} +#undef MAX_REALLOC_COUNT +END_TEST + +/* Test long element names with namespaces under a failing allocator */ +START_TEST(test_nsalloc_long_element) +{ + const char *text = + "" + ""; + const char *elemstr[] = { + "http://example.org/" + " thisisalongenoughelementnametotriggerareallocation foo", + "http://example.org/ a bar" + }; + int i; +#define MAX_ALLOC_COUNT 15 + int repeated = 0; + + for (i = 0; i < MAX_ALLOC_COUNT; i++) { + /* Repeat some allocation counts because some allocations + * get cached across XML_ParserReset() called. + */ + if ((i == 4 && (repeated == 3 || repeated == 5)) || + (i == 7 && repeated == 8) || + (i == 10 && repeated == 9)) { + i -= 2; + repeated++; + } + else if ((i == 2 && repeated < 2) || + (i == 3 && + (repeated == 2 || repeated == 4 || repeated == 6)) || + (i == 5 && repeated == 7)) { + i--; + repeated++; + } + allocation_count = i; + XML_SetReturnNSTriplet(parser, XML_TRUE); + XML_SetUserData(parser, elemstr); + XML_SetElementHandler(parser, + triplet_start_checker, + triplet_end_checker); + if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), + XML_TRUE) != XML_STATUS_ERROR) + break; + XML_ParserReset(parser, NULL); + } + if (i == 0) + fail("Parsing worked despite failing reallocations"); + else if (i == MAX_ALLOC_COUNT) + fail("Parsing failed at max reallocation count"); +} +#undef MAX_ALLOC_COUNT +END_TEST + +/* Test the effects of reallocation failure when reassigning a + * binding. + * + * XML_ParserReset does not free the BINDING structures used by a + * parser, but instead adds them to an internal free list to be reused + * as necessary. Likewise the URI buffers allocated for the binding + * aren't freed, but kept attached to their existing binding. If the + * new binding has a longer URI, it will need reallocation. This test + * provokes that reallocation, and tests the control path if it fails. + */ +START_TEST(test_nsalloc_realloc_binding_uri) +{ + const char *first = + "\n" + " \n" + ""; + const char *second = + "\n" + " \n" + ""; + unsigned i; +#define MAX_REALLOC_COUNT 10 + + /* First, do a full parse that will leave bindings around */ + if (_XML_Parse_SINGLE_BYTES(parser, first, strlen(first), + XML_TRUE) == XML_STATUS_ERROR) + xml_failure(parser); + + /* Now repeat with a longer URI and a duff reallocator */ + for (i = 0; i < MAX_REALLOC_COUNT; i++) { + XML_ParserReset(parser, NULL); + reallocation_count = i; + if (_XML_Parse_SINGLE_BYTES(parser, second, strlen(second), + XML_TRUE) != XML_STATUS_ERROR) + break; + } + if (i == 0) + fail("Parsing worked despite failing reallocation"); + else if (i == MAX_REALLOC_COUNT) + fail("Parsing failed at max reallocation count"); +} +#undef MAX_REALLOC_COUNT +END_TEST + + static Suite * make_suite(void) { @@ -3857,6 +5404,7 @@ make_suite(void) TCase *tc_namespace = tcase_create("XML namespaces"); TCase *tc_misc = tcase_create("miscellaneous tests"); TCase *tc_alloc = tcase_create("allocation tests"); + TCase *tc_nsalloc = tcase_create("namespace allocattion tests"); suite_add_tcase(s, tc_basic); tcase_add_checked_fixture(tc_basic, basic_setup, basic_teardown); @@ -3886,6 +5434,7 @@ make_suite(void) tcase_add_test(tc_basic, test_line_number_after_error); tcase_add_test(tc_basic, test_column_number_after_error); tcase_add_test(tc_basic, test_really_long_lines); + tcase_add_test(tc_basic, test_really_long_encoded_lines); tcase_add_test(tc_basic, test_end_element_events); tcase_add_test(tc_basic, test_attr_whitespace_normalization); tcase_add_test(tc_basic, test_xmldecl_misplaced); @@ -3902,6 +5451,7 @@ make_suite(void) test_wfc_undeclared_entity_with_external_subset_standalone); tcase_add_test(tc_basic, test_wfc_no_recursive_entity_refs); tcase_add_test(tc_basic, test_ext_entity_set_encoding); + tcase_add_test(tc_basic, test_ext_entity_no_handler); tcase_add_test(tc_basic, test_ext_entity_set_bom); tcase_add_test(tc_basic, test_ext_entity_bad_encoding); tcase_add_test(tc_basic, test_ext_entity_invalid_parse); @@ -3914,7 +5464,12 @@ make_suite(void) tcase_add_test(tc_basic, test_repeated_stop_parser_between_char_data_calls); tcase_add_test(tc_basic, test_good_cdata_ascii); tcase_add_test(tc_basic, test_good_cdata_utf16); + tcase_add_test(tc_basic, test_long_cdata_utf16); + tcase_add_test(tc_basic, test_multichar_cdata_utf16); tcase_add_test(tc_basic, test_bad_cdata); + tcase_add_test(tc_basic, test_bad_cdata_utf16); + tcase_add_test(tc_basic, test_stop_parser_between_cdata_calls); + tcase_add_test(tc_basic, test_suspend_parser_between_cdata_calls); tcase_add_test(tc_basic, test_memory_allocation); tcase_add_test(tc_basic, test_default_current); tcase_add_test(tc_basic, test_dtd_elements); @@ -3924,11 +5479,17 @@ make_suite(void) tcase_add_test(tc_basic, test_reset_in_entity); tcase_add_test(tc_basic, test_resume_invalid_parse); tcase_add_test(tc_basic, test_resume_resuspended); + tcase_add_test(tc_basic, test_cdata_default); tcase_add_test(tc_basic, test_subordinate_reset); tcase_add_test(tc_basic, test_subordinate_suspend); tcase_add_test(tc_basic, test_subordinate_xdecl_suspend); tcase_add_test(tc_basic, test_subordinate_xdecl_abort); tcase_add_test(tc_basic, test_explicit_encoding); + tcase_add_test(tc_basic, test_trailing_cr); + tcase_add_test(tc_basic, test_ext_entity_trailing_cr); + tcase_add_test(tc_basic, test_trailing_rsqb); + tcase_add_test(tc_basic, test_ext_entity_trailing_rsqb); + tcase_add_test(tc_basic, test_ext_entity_good_cdata); tcase_add_test(tc_basic, test_user_parameters); tcase_add_test(tc_basic, test_ext_entity_ref_parameter); tcase_add_test(tc_basic, test_empty_parse); @@ -3938,6 +5499,8 @@ make_suite(void) tcase_add_test(tc_basic, test_byte_info_at_error); tcase_add_test(tc_basic, test_byte_info_at_cdata); tcase_add_test(tc_basic, test_invalid_tag_in_dtd); + tcase_add_test(tc_basic, test_ignore_section); + tcase_add_test(tc_basic, test_bad_ignore_section); suite_add_tcase(s, tc_namespace); tcase_add_checked_fixture(tc_namespace, @@ -3951,35 +5514,56 @@ make_suite(void) tcase_add_test(tc_namespace, test_ns_prefix_with_empty_uri_2); tcase_add_test(tc_namespace, test_ns_prefix_with_empty_uri_3); tcase_add_test(tc_namespace, test_ns_prefix_with_empty_uri_4); + tcase_add_test(tc_namespace, test_ns_unbound_prefix); tcase_add_test(tc_namespace, test_ns_default_with_empty_uri); tcase_add_test(tc_namespace, test_ns_duplicate_attrs_diff_prefixes); + tcase_add_test(tc_namespace, test_ns_duplicate_hashes); tcase_add_test(tc_namespace, test_ns_unbound_prefix_on_attribute); tcase_add_test(tc_namespace, test_ns_unbound_prefix_on_element); tcase_add_test(tc_namespace, test_ns_parser_reset); tcase_add_test(tc_namespace, test_ns_long_element); + tcase_add_test(tc_namespace, test_ns_mixed_prefix_atts); + tcase_add_test(tc_namespace, test_ns_extend_uri_buffer); + tcase_add_test(tc_namespace, test_ns_reserved_attributes); + tcase_add_test(tc_namespace, test_ns_reserved_attributes_2); suite_add_tcase(s, tc_misc); tcase_add_checked_fixture(tc_misc, NULL, basic_teardown); tcase_add_test(tc_misc, test_misc_alloc_create_parser); tcase_add_test(tc_misc, test_misc_alloc_create_parser_with_encoding); - tcase_add_test(tc_misc, test_misc_alloc_ns); tcase_add_test(tc_misc, test_misc_null_parser); - tcase_add_test(tc_misc, test_misc_alloc_ns_parse_buffer); tcase_add_test(tc_misc, test_misc_error_string); tcase_add_test(tc_misc, test_misc_version); tcase_add_test(tc_misc, test_misc_attribute_leak); suite_add_tcase(s, tc_alloc); tcase_add_checked_fixture(tc_alloc, alloc_setup, alloc_teardown); + tcase_add_test(tc_alloc, test_alloc_parse_pi); + tcase_add_test(tc_alloc, test_alloc_parse_pi_2); + tcase_add_test(tc_alloc, test_alloc_parse_comment); + tcase_add_test(tc_alloc, test_alloc_parse_comment_2); tcase_add_test(tc_alloc, test_alloc_create_external_parser); tcase_add_test(tc_alloc, test_alloc_run_external_parser); tcase_add_test(tc_alloc, test_alloc_dtd_copy_default_atts); tcase_add_test(tc_alloc, test_alloc_external_entity); + tcase_add_test(tc_alloc, test_alloc_ext_entity_set_encoding); tcase_add_test(tc_alloc, test_alloc_internal_entity); tcase_add_test(tc_alloc, test_alloc_dtd_default_handling); tcase_add_test(tc_alloc, test_alloc_explicit_encoding); tcase_add_test(tc_alloc, test_alloc_set_base); + suite_add_tcase(s, tc_nsalloc); + tcase_add_checked_fixture(tc_nsalloc, nsalloc_setup, nsalloc_teardown); + tcase_add_test(tc_nsalloc, test_nsalloc_xmlns); + tcase_add_test(tc_nsalloc, test_nsalloc_parse_buffer); + tcase_add_test(tc_nsalloc, test_nsalloc_long_prefix); + tcase_add_test(tc_nsalloc, test_nsalloc_long_uri); + tcase_add_test(tc_nsalloc, test_nsalloc_long_attr); + tcase_add_test(tc_nsalloc, test_nsalloc_long_attr_prefix); + tcase_add_test(tc_nsalloc, test_nsalloc_realloc_attributes); + tcase_add_test(tc_nsalloc, test_nsalloc_long_element); + tcase_add_test(tc_nsalloc, test_nsalloc_realloc_binding_uri); + return s; }