/* Copyright (c) 1998, 1999, 2000 Thai Open Source Software Center Ltd See the file COPYING for copying permission. runtest.c : run the Expat test suite */ #ifdef HAVE_EXPAT_CONFIG_H #include #endif #include #include #include #include #include #include /* ptrdiff_t */ #include #ifndef __cplusplus # include #endif #include #include "expat.h" #include "chardata.h" #include "internal.h" /* for UNUSED_P only */ #include "minicheck.h" #include "memcheck.h" #include "siphash.h" #include "ascii.h" /* for ASCII_xxx */ #ifdef XML_LARGE_SIZE #define XML_FMT_INT_MOD "ll" #else #define XML_FMT_INT_MOD "l" #endif #if defined(NDEBUG) # error \ The test suite relies on assert(...) at the moment. \ You have NDEBUG defined which removes that code so that failures in the \ test suite can go unnoticed. \ \ While we rely on assert(...), compiling the test suite with NDEBUG \ defined is not supported. #endif static XML_Parser parser = NULL; static void basic_setup(void) { parser = XML_ParserCreate(NULL); if (parser == NULL) fail("Parser not created."); } static void basic_teardown(void) { if (parser != NULL) { XML_ParserFree(parser); parser = NULL; } } /* Generate a failure using the parser state to create an error message; this should be used when the parser reports an error we weren't expecting. */ static void _xml_failure(XML_Parser parser, const char *file, int line) { char buffer[1024]; enum XML_Error err = XML_GetErrorCode(parser); sprintf(buffer, " %d: %s (line %" XML_FMT_INT_MOD "u, offset %"\ XML_FMT_INT_MOD "u)\n reported from %s, line %d\n", err, XML_ErrorString(err), XML_GetCurrentLineNumber(parser), XML_GetCurrentColumnNumber(parser), file, line); _fail_unless(0, file, line, buffer); } static enum XML_Status _XML_Parse_SINGLE_BYTES(XML_Parser parser, const char *s, int len, int isFinal) { enum XML_Status res = XML_STATUS_ERROR; int offset = 0; if (len == 0) { return XML_Parse(parser, s, len, isFinal); } for (; offset < len; offset++) { const int innerIsFinal = (offset == len - 1) && isFinal; const char c = s[offset]; /* to help out-of-bounds detection */ res = XML_Parse(parser, &c, sizeof(char), innerIsFinal); if (res != XML_STATUS_OK) { return res; } } return res; } #define xml_failure(parser) _xml_failure((parser), __FILE__, __LINE__) static void _expect_failure(const char *text, enum XML_Error errorCode, const char *errorMessage, const char *file, int lineno) { if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), XML_TRUE) == XML_STATUS_OK) /* Hackish use of _fail_unless() macro, but let's us report the right filename and line number. */ _fail_unless(0, file, lineno, errorMessage); if (XML_GetErrorCode(parser) != errorCode) _xml_failure(parser, file, lineno); } #define expect_failure(text, errorCode, errorMessage) \ _expect_failure((text), (errorCode), (errorMessage), \ __FILE__, __LINE__) /* Dummy handlers for when we need to set a handler to tickle a bug, but it doesn't need to do anything. */ static unsigned long dummy_handler_flags = 0; #define DUMMY_START_DOCTYPE_HANDLER_FLAG (1UL << 0) #define DUMMY_END_DOCTYPE_HANDLER_FLAG (1UL << 1) #define DUMMY_ENTITY_DECL_HANDLER_FLAG (1UL << 2) #define DUMMY_NOTATION_DECL_HANDLER_FLAG (1UL << 3) #define DUMMY_ELEMENT_DECL_HANDLER_FLAG (1UL << 4) #define DUMMY_ATTLIST_DECL_HANDLER_FLAG (1UL << 5) #define DUMMY_COMMENT_HANDLER_FLAG (1UL << 6) #define DUMMY_PI_HANDLER_FLAG (1UL << 7) #define DUMMY_START_ELEMENT_HANDLER_FLAG (1UL << 8) #define DUMMY_START_CDATA_HANDLER_FLAG (1UL << 9) #define DUMMY_END_CDATA_HANDLER_FLAG (1UL << 10) #define DUMMY_UNPARSED_ENTITY_DECL_HANDLER_FLAG (1UL << 11) #define DUMMY_START_NS_DECL_HANDLER_FLAG (1UL << 12) #define DUMMY_END_NS_DECL_HANDLER_FLAG (1UL << 13) #define DUMMY_START_DOCTYPE_DECL_HANDLER_FLAG (1UL << 14) #define DUMMY_END_DOCTYPE_DECL_HANDLER_FLAG (1UL << 15) #define DUMMY_SKIP_HANDLER_FLAG (1UL << 16) #define DUMMY_DEFAULT_HANDLER_FLAG (1UL << 17) static void XMLCALL dummy_xdecl_handler(void *UNUSED_P(userData), const XML_Char *UNUSED_P(version), const XML_Char *UNUSED_P(encoding), int UNUSED_P(standalone)) {} static void XMLCALL dummy_start_doctype_handler(void *UNUSED_P(userData), const XML_Char *UNUSED_P(doctypeName), const XML_Char *UNUSED_P(sysid), const XML_Char *UNUSED_P(pubid), int UNUSED_P(has_internal_subset)) { dummy_handler_flags |= DUMMY_START_DOCTYPE_HANDLER_FLAG; } static void XMLCALL dummy_end_doctype_handler(void *UNUSED_P(userData)) { dummy_handler_flags |= DUMMY_END_DOCTYPE_HANDLER_FLAG; } static void XMLCALL dummy_entity_decl_handler(void *UNUSED_P(userData), const XML_Char *UNUSED_P(entityName), int UNUSED_P(is_parameter_entity), const XML_Char *UNUSED_P(value), int UNUSED_P(value_length), const XML_Char *UNUSED_P(base), const XML_Char *UNUSED_P(systemId), const XML_Char *UNUSED_P(publicId), const XML_Char *UNUSED_P(notationName)) { dummy_handler_flags |= DUMMY_ENTITY_DECL_HANDLER_FLAG; } static void XMLCALL dummy_notation_decl_handler(void *UNUSED_P(userData), const XML_Char *UNUSED_P(notationName), const XML_Char *UNUSED_P(base), const XML_Char *UNUSED_P(systemId), const XML_Char *UNUSED_P(publicId)) { dummy_handler_flags |= DUMMY_NOTATION_DECL_HANDLER_FLAG; } static void XMLCALL dummy_element_decl_handler(void *UNUSED_P(userData), const XML_Char *UNUSED_P(name), XML_Content *model) { /* The content model must be freed by the handler. Unfortunately * we cannot pass the parser as the userData because this is used * with other handlers that require other userData. */ XML_FreeContentModel(parser, model); dummy_handler_flags |= DUMMY_ELEMENT_DECL_HANDLER_FLAG; } static void XMLCALL dummy_attlist_decl_handler(void *UNUSED_P(userData), const XML_Char *UNUSED_P(elname), const XML_Char *UNUSED_P(attname), const XML_Char *UNUSED_P(att_type), const XML_Char *UNUSED_P(dflt), int UNUSED_P(isrequired)) { dummy_handler_flags |= DUMMY_ATTLIST_DECL_HANDLER_FLAG; } static void XMLCALL dummy_comment_handler(void *UNUSED_P(userData), const XML_Char *UNUSED_P(data)) { dummy_handler_flags |= DUMMY_COMMENT_HANDLER_FLAG; } static void XMLCALL dummy_pi_handler(void *UNUSED_P(userData), const XML_Char *UNUSED_P(target), const XML_Char *UNUSED_P(data)) { dummy_handler_flags |= DUMMY_PI_HANDLER_FLAG; } static void XMLCALL dummy_start_element(void *UNUSED_P(userData), const XML_Char *UNUSED_P(name), const XML_Char **UNUSED_P(atts)) { dummy_handler_flags |= DUMMY_START_ELEMENT_HANDLER_FLAG; } 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)) { dummy_handler_flags |= DUMMY_START_CDATA_HANDLER_FLAG; } static void XMLCALL dummy_end_cdata_handler(void *UNUSED_P(userData)) { dummy_handler_flags |= DUMMY_END_CDATA_HANDLER_FLAG; } 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), const XML_Char *UNUSED_P(uri)) { dummy_handler_flags |= DUMMY_START_NS_DECL_HANDLER_FLAG; } static void XMLCALL dummy_end_namespace_decl_handler(void *UNUSED_P(userData), const XML_Char *UNUSED_P(prefix)) { dummy_handler_flags |= DUMMY_END_NS_DECL_HANDLER_FLAG; } /* This handler is obsolete, but while the code exists we should * ensure that dealing with the handler is covered by tests. */ static void XMLCALL dummy_unparsed_entity_decl_handler(void *UNUSED_P(userData), const XML_Char *UNUSED_P(entityName), const XML_Char *UNUSED_P(base), const XML_Char *UNUSED_P(systemId), const XML_Char *UNUSED_P(publicId), const XML_Char *UNUSED_P(notationName)) { dummy_handler_flags |= DUMMY_UNPARSED_ENTITY_DECL_HANDLER_FLAG; } static void XMLCALL dummy_default_handler(void *UNUSED_P(userData), const XML_Char *UNUSED_P(s), int UNUSED_P(len)) {} static void XMLCALL dummy_start_doctype_decl_handler(void *UNUSED_P(userData), const XML_Char *UNUSED_P(doctypeName), const XML_Char *UNUSED_P(sysid), const XML_Char *UNUSED_P(pubid), int UNUSED_P(has_internal_subset)) { dummy_handler_flags |= DUMMY_START_DOCTYPE_DECL_HANDLER_FLAG; } static void XMLCALL dummy_end_doctype_decl_handler(void *UNUSED_P(userData)) { dummy_handler_flags |= DUMMY_END_DOCTYPE_DECL_HANDLER_FLAG; } static void XMLCALL dummy_skip_handler(void *UNUSED_P(userData), const XML_Char *UNUSED_P(entityName), int UNUSED_P(is_parameter_entity)) { dummy_handler_flags |= DUMMY_SKIP_HANDLER_FLAG; } /* * Parameter entity evaluation support. */ #define ENTITY_MATCH_FAIL (-1) #define ENTITY_MATCH_NOT_FOUND (0) #define ENTITY_MATCH_SUCCESS (1) static const XML_Char *entity_name_to_match = NULL; static const XML_Char *entity_value_to_match = NULL; static int entity_match_flag = ENTITY_MATCH_NOT_FOUND; static void XMLCALL param_entity_match_handler(void *UNUSED_P(userData), const XML_Char *entityName, int is_parameter_entity, const XML_Char *value, int value_length, const XML_Char *UNUSED_P(base), const XML_Char *UNUSED_P(systemId), const XML_Char *UNUSED_P(publicId), const XML_Char *UNUSED_P(notationName)) { if (!is_parameter_entity || entity_name_to_match == NULL || entity_value_to_match == NULL) { return; } if (!strcmp(entityName, entity_name_to_match)) { /* The cast here is safe because we control the horizontal and * the vertical, and we therefore know our strings are never * going to overflow an int. */ if (value_length != (int)strlen(entity_value_to_match) || strncmp(value, entity_value_to_match, value_length)) { entity_match_flag = ENTITY_MATCH_FAIL; } else { entity_match_flag = ENTITY_MATCH_SUCCESS; } } /* Else leave the match flag alone */ } /* * Character & encoding tests. */ START_TEST(test_nul_byte) { char text[] = "\0"; /* test that a NUL byte (in US-ASCII data) is an error */ if (_XML_Parse_SINGLE_BYTES(parser, text, sizeof(text) - 1, XML_TRUE) == XML_STATUS_OK) fail("Parser did not report error on NUL-byte."); if (XML_GetErrorCode(parser) != XML_ERROR_INVALID_TOKEN) xml_failure(parser); } END_TEST START_TEST(test_u0000_char) { /* test that a NUL byte (in US-ASCII data) is an error */ expect_failure("", XML_ERROR_BAD_CHAR_REF, "Parser did not report error on NUL-byte."); } END_TEST START_TEST(test_siphash_self) { if (! sip24_valid()) fail("SipHash self-test failed"); } END_TEST START_TEST(test_siphash_spec) { /* https://131002.net/siphash/siphash.pdf (page 19, "Test values") */ const char message[] = "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09" "\x0a\x0b\x0c\x0d\x0e"; const size_t len = sizeof(message) - 1; const uint64_t expected = _SIP_ULL(0xa129ca61U, 0x49be45e5U); struct siphash state; struct sipkey key; (void)sip_tobin; sip_tokey(&key, "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09" "\x0a\x0b\x0c\x0d\x0e\x0f"); sip24_init(&state, &key); /* Cover spread across calls */ sip24_update(&state, message, 4); sip24_update(&state, message + 4, len - 4); /* Cover null length */ sip24_update(&state, message, 0); if (sip24_final(&state) != expected) fail("sip24_final failed spec test\n"); /* Cover wrapper */ if (siphash24(message, len, &key) != expected) fail("siphash24 failed spec test\n"); } END_TEST START_TEST(test_bom_utf8) { /* This test is really just making sure we don't core on a UTF-8 BOM. */ const char *text = "\357\273\277"; if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) xml_failure(parser); } END_TEST START_TEST(test_bom_utf16_be) { char text[] = "\376\377\0<\0e\0/\0>"; if (_XML_Parse_SINGLE_BYTES(parser, text, sizeof(text)-1, XML_TRUE) == XML_STATUS_ERROR) xml_failure(parser); } END_TEST START_TEST(test_bom_utf16_le) { char text[] = "\377\376<\0e\0/\0>\0"; if (_XML_Parse_SINGLE_BYTES(parser, text, sizeof(text)-1, XML_TRUE) == XML_STATUS_ERROR) xml_failure(parser); } END_TEST static void XMLCALL accumulate_characters(void *userData, const XML_Char *s, int len) { CharData_AppendXMLChars((CharData *)userData, s, len); } static void XMLCALL accumulate_attribute(void *userData, const XML_Char *UNUSED_P(name), const XML_Char **atts) { CharData *storage = (CharData *)userData; if (storage->count < 0 && atts != NULL && atts[0] != NULL) { /* "accumulate" the value of the first attribute we see */ CharData_AppendXMLChars(storage, atts[1], -1); } } static void _run_character_check(const XML_Char *text, const XML_Char *expected, const char *file, int line) { CharData storage; CharData_Init(&storage); XML_SetUserData(parser, &storage); XML_SetCharacterDataHandler(parser, accumulate_characters); if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) _xml_failure(parser, file, line); CharData_CheckXMLChars(&storage, expected); } #define run_character_check(text, expected) \ _run_character_check(text, expected, __FILE__, __LINE__) static void _run_attribute_check(const XML_Char *text, const XML_Char *expected, const char *file, int line) { CharData storage; CharData_Init(&storage); XML_SetUserData(parser, &storage); XML_SetStartElementHandler(parser, accumulate_attribute); if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) _xml_failure(parser, file, line); CharData_CheckXMLChars(&storage, expected); } #define run_attribute_check(text, expected) \ _run_attribute_check(text, expected, __FILE__, __LINE__) typedef struct ExtTest { const char *parse_text; const char *encoding; CharData *storage; } ExtTest; static void XMLCALL ext_accumulate_characters(void *userData, const XML_Char *s, int len) { ExtTest *test_data = (ExtTest *)userData; accumulate_characters(test_data->storage, s, len); } static void _run_ext_character_check(const XML_Char *text, ExtTest *test_data, const XML_Char *expected, const char *file, int line) { CharData storage; CharData_Init(&storage); test_data->storage = &storage; XML_SetUserData(parser, test_data); XML_SetCharacterDataHandler(parser, ext_accumulate_characters); if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) _xml_failure(parser, file, line); CharData_CheckXMLChars(&storage, expected); } #define run_ext_character_check(text, test_data, expected) \ _run_ext_character_check(text, test_data, expected, __FILE__, __LINE__) /* Regression test for SF bug #491986. */ START_TEST(test_danish_latin1) { const char *text = "\n" "J\xF8rgen \xE6\xF8\xE5\xC6\xD8\xC5"; run_character_check(text, "J\xC3\xB8rgen \xC3\xA6\xC3\xB8\xC3\xA5\xC3\x86\xC3\x98\xC3\x85"); } END_TEST /* Regression test for SF bug #514281. */ START_TEST(test_french_charref_hexidecimal) { const char *text = "\n" "éèàçêÈ"; run_character_check(text, "\xC3\xA9\xC3\xA8\xC3\xA0\xC3\xA7\xC3\xAA\xC3\x88"); } END_TEST START_TEST(test_french_charref_decimal) { const char *text = "\n" "éèàçêÈ"; run_character_check(text, "\xC3\xA9\xC3\xA8\xC3\xA0\xC3\xA7\xC3\xAA\xC3\x88"); } END_TEST START_TEST(test_french_latin1) { const char *text = "\n" "\xE9\xE8\xE0\xE7\xEa\xC8"; run_character_check(text, "\xC3\xA9\xC3\xA8\xC3\xA0\xC3\xA7\xC3\xAA\xC3\x88"); } END_TEST START_TEST(test_french_utf8) { const char *text = "\n" "\xC3\xA9"; run_character_check(text, "\xC3\xA9"); } END_TEST /* Regression test for SF bug #600479. XXX There should be a test that exercises all legal XML Unicode characters as PCDATA and attribute value content, and XML Name characters as part of element and attribute names. */ START_TEST(test_utf8_false_rejection) { const char *text = "\xEF\xBA\xBF"; run_character_check(text, "\xEF\xBA\xBF"); } END_TEST /* Regression test for SF bug #477667. This test assures that any 8-bit character followed by a 7-bit character will not be mistakenly interpreted as a valid UTF-8 sequence. */ START_TEST(test_illegal_utf8) { char text[100]; int i; for (i = 128; i <= 255; ++i) { sprintf(text, "%ccd", i); if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), XML_TRUE) == XML_STATUS_OK) { sprintf(text, "expected token error for '%c' (ordinal %d) in UTF-8 text", i, i); fail(text); } else if (XML_GetErrorCode(parser) != XML_ERROR_INVALID_TOKEN) xml_failure(parser); /* Reset the parser since we use the same parser repeatedly. */ XML_ParserReset(parser, NULL); } } END_TEST /* Examples, not masks: */ #define UTF8_LEAD_1 "\x7f" /* 0b01111111 */ #define UTF8_LEAD_2 "\xdf" /* 0b11011111 */ #define UTF8_LEAD_3 "\xef" /* 0b11101111 */ #define UTF8_LEAD_4 "\xf7" /* 0b11110111 */ #define UTF8_FOLLOW "\xbf" /* 0b10111111 */ START_TEST(test_utf8_auto_align) { struct TestCase { ptrdiff_t expectedMovementInChars; const char * input; }; struct TestCase cases[] = { {00, ""}, {00, UTF8_LEAD_1}, {-1, UTF8_LEAD_2}, {00, UTF8_LEAD_2 UTF8_FOLLOW}, {-1, UTF8_LEAD_3}, {-2, UTF8_LEAD_3 UTF8_FOLLOW}, {00, UTF8_LEAD_3 UTF8_FOLLOW UTF8_FOLLOW}, {-1, UTF8_LEAD_4}, {-2, UTF8_LEAD_4 UTF8_FOLLOW}, {-3, UTF8_LEAD_4 UTF8_FOLLOW UTF8_FOLLOW}, {00, UTF8_LEAD_4 UTF8_FOLLOW UTF8_FOLLOW UTF8_FOLLOW}, }; size_t i = 0; bool success = true; for (; i < sizeof(cases) / sizeof(*cases); i++) { const char * fromLim = cases[i].input + strlen(cases[i].input); const char * const fromLimInitially = fromLim; ptrdiff_t actualMovementInChars; align_limit_to_full_utf8_characters(cases[i].input, &fromLim); actualMovementInChars = (fromLim - fromLimInitially); if (actualMovementInChars != cases[i].expectedMovementInChars) { size_t j = 0; success = false; printf("[-] UTF-8 case %2u: Expected movement by %2d chars" ", actually moved by %2d chars: \"", (unsigned)(i + 1), (int)cases[i].expectedMovementInChars, (int)actualMovementInChars); for (; j < strlen(cases[i].input); j++) { printf("\\x%02x", (unsigned char)cases[i].input[j]); } printf("\"\n"); } } if (! success) { fail("UTF-8 auto-alignment is not bullet-proof\n"); } } END_TEST START_TEST(test_utf16) { /* some text */ char text[] = "\000<\000?\000x\000m\000\154\000 \000v\000e\000r\000s\000i\000o" "\000n\000=\000'\0001\000.\000\060\000'\000 \000e\000n\000c\000o" "\000d\000i\000n\000g\000=\000'\000U\000T\000F\000-\0001\000\066" "\000'\000?\000>\000\n" "\000<\000d\000o\000c\000 \000a\000=\000'\0001\0002\0003\000'" "\000>\000s\000o\000m\000e\000 \000t\000e\000x\000t\000<\000/" "\000d\000o\000c\000>"; if (_XML_Parse_SINGLE_BYTES(parser, text, sizeof(text)-1, XML_TRUE) == XML_STATUS_ERROR) xml_failure(parser); } END_TEST START_TEST(test_utf16_le_epilog_newline) { unsigned int first_chunk_bytes = 17; char text[] = "\xFF\xFE" /* BOM */ "<\000e\000/\000>\000" /* document element */ "\r\000\n\000\r\000\n\000"; /* epilog */ if (first_chunk_bytes >= sizeof(text) - 1) fail("bad value of first_chunk_bytes"); if ( _XML_Parse_SINGLE_BYTES(parser, text, first_chunk_bytes, XML_FALSE) == XML_STATUS_ERROR) xml_failure(parser); else { enum XML_Status rc; rc = _XML_Parse_SINGLE_BYTES(parser, text + first_chunk_bytes, sizeof(text) - first_chunk_bytes - 1, XML_TRUE); if (rc == XML_STATUS_ERROR) xml_failure(parser); } } END_TEST /* Test that an outright lie in the encoding is faulted */ START_TEST(test_not_utf16) { const char *text = "" "Hi"; /* Use a handler to provoke the appropriate code paths */ XML_SetXmlDeclHandler(parser, dummy_xdecl_handler); expect_failure(text, XML_ERROR_INCORRECT_ENCODING, "UTF-16 declared in UTF-8 not faulted"); } END_TEST /* Test that an unknown encoding is rejected */ START_TEST(test_bad_encoding) { const char *text = "Hi"; if (!XML_SetEncoding(parser, "unknown-encoding")) fail("XML_SetEncoding failed"); expect_failure(text, XML_ERROR_UNKNOWN_ENCODING, "Unknown encoding not faulted"); } END_TEST /* Regression test for SF bug #481609, #774028. */ START_TEST(test_latin1_umlauts) { const char *text = "\n" "\xE4 \xF6 \xFC ä ö ü ä ö ü >"; const char *utf8 = "\xC3\xA4 \xC3\xB6 \xC3\xBC " "\xC3\xA4 \xC3\xB6 \xC3\xBC " "\xC3\xA4 \xC3\xB6 \xC3\xBC >"; 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 /* Regression test #1 for SF bug #653180. */ START_TEST(test_line_number_after_parse) { const char *text = "\n" "\n" "\n"; XML_Size lineno; if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), XML_FALSE) == XML_STATUS_ERROR) xml_failure(parser); lineno = XML_GetCurrentLineNumber(parser); if (lineno != 4) { char buffer[100]; sprintf(buffer, "expected 4 lines, saw %" XML_FMT_INT_MOD "u", lineno); fail(buffer); } } END_TEST /* Regression test #2 for SF bug #653180. */ START_TEST(test_column_number_after_parse) { const char *text = ""; XML_Size colno; if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), XML_FALSE) == XML_STATUS_ERROR) xml_failure(parser); colno = XML_GetCurrentColumnNumber(parser); if (colno != 11) { char buffer[100]; sprintf(buffer, "expected 11 columns, saw %" XML_FMT_INT_MOD "u", colno); fail(buffer); } } END_TEST static void XMLCALL start_element_event_handler2(void *userData, const XML_Char *name, const XML_Char **UNUSED_P(attr)) { CharData *storage = (CharData *) userData; char buffer[100]; sprintf(buffer, "<%s> at col:%" XML_FMT_INT_MOD "u line:%"\ XML_FMT_INT_MOD "u\n", name, XML_GetCurrentColumnNumber(parser), XML_GetCurrentLineNumber(parser)); CharData_AppendString(storage, buffer); } static void XMLCALL end_element_event_handler2(void *userData, const XML_Char *name) { CharData *storage = (CharData *) userData; char buffer[100]; sprintf(buffer, " at col:%" XML_FMT_INT_MOD "u line:%"\ XML_FMT_INT_MOD "u\n", name, XML_GetCurrentColumnNumber(parser), XML_GetCurrentLineNumber(parser)); CharData_AppendString(storage, buffer); } /* Regression test #3 for SF bug #653180. */ START_TEST(test_line_and_column_numbers_inside_handlers) { const char *text = "\n" /* Unix end-of-line */ " \r\n" /* Windows end-of-line */ " \r" /* Mac OS end-of-line */ " \n" " \n" " \n" " \n" ""; const char *expected = " at col:0 line:1\n" " at col:2 line:2\n" " at col:4 line:3\n" " at col:8 line:3\n" " at col:2 line:4\n" " at col:2 line:5\n" " at col:4 line:6\n" " at col:8 line:6\n" " at col:2 line:7\n" " at col:0 line:8\n"; CharData storage; CharData_Init(&storage); XML_SetUserData(parser, &storage); XML_SetStartElementHandler(parser, start_element_event_handler2); XML_SetEndElementHandler(parser, end_element_event_handler2); if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) xml_failure(parser); CharData_CheckString(&storage, expected); } END_TEST /* Regression test #4 for SF bug #653180. */ START_TEST(test_line_number_after_error) { const char *text = "\n" " \n" " "; /* missing */ XML_Size lineno; if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), XML_FALSE) != XML_STATUS_ERROR) fail("Expected a parse error"); lineno = XML_GetCurrentLineNumber(parser); if (lineno != 3) { char buffer[100]; sprintf(buffer, "expected 3 lines, saw %" XML_FMT_INT_MOD "u", lineno); fail(buffer); } } END_TEST /* Regression test #5 for SF bug #653180. */ START_TEST(test_column_number_after_error) { const char *text = "\n" " \n" " "; /* missing */ XML_Size colno; if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), XML_FALSE) != XML_STATUS_ERROR) fail("Expected a parse error"); colno = XML_GetCurrentColumnNumber(parser); if (colno != 4) { char buffer[100]; sprintf(buffer, "expected 4 columns, saw %" XML_FMT_INT_MOD "u", colno); fail(buffer); } } END_TEST /* Regression test for SF bug #478332. */ START_TEST(test_really_long_lines) { /* This parses an input line longer than INIT_DATA_BUF_SIZE characters long (defined to be 1024 in xmlparse.c). We take a really cheesy approach to building the input buffer, because this avoids writing bugs in buffer-filling code. */ 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-+" ""; if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) xml_failure(parser); } 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. */ static void XMLCALL end_element_event_handler(void *userData, const XML_Char *name) { CharData *storage = (CharData *) userData; CharData_AppendString(storage, "/"); CharData_AppendXMLChars(storage, name, -1); } START_TEST(test_end_element_events) { const char *text = ""; const char *expected = "/c/b/f/d/a"; CharData storage; CharData_Init(&storage); XML_SetUserData(parser, &storage); XML_SetEndElementHandler(parser, end_element_event_handler); if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) xml_failure(parser); CharData_CheckString(&storage, expected); } END_TEST /* * Attribute tests. */ /* Helpers used by the following test; this checks any "attr" and "refs" attributes to make sure whitespace has been normalized. Return true if whitespace has been normalized in a string, using the rules for attribute value normalization. The 'is_cdata' flag is needed since CDATA attributes don't need to have multiple whitespace characters collapsed to a single space, while other attribute data types do. (Section 3.3.3 of the recommendation.) */ static int is_whitespace_normalized(const XML_Char *s, int is_cdata) { int blanks = 0; int at_start = 1; while (*s) { if (*s == ' ') ++blanks; else if (*s == '\t' || *s == '\n' || *s == '\r') return 0; else { if (at_start) { at_start = 0; if (blanks && !is_cdata) /* illegal leading blanks */ return 0; } else if (blanks > 1 && !is_cdata) return 0; blanks = 0; } ++s; } if (blanks && !is_cdata) return 0; return 1; } /* Check the attribute whitespace checker: */ static void testhelper_is_whitespace_normalized(void) { assert(is_whitespace_normalized("abc", 0)); assert(is_whitespace_normalized("abc", 1)); assert(is_whitespace_normalized("abc def ghi", 0)); assert(is_whitespace_normalized("abc def ghi", 1)); assert(!is_whitespace_normalized(" abc def ghi", 0)); assert(is_whitespace_normalized(" abc def ghi", 1)); assert(!is_whitespace_normalized("abc def ghi", 0)); assert(is_whitespace_normalized("abc def ghi", 1)); assert(!is_whitespace_normalized("abc def ghi ", 0)); assert(is_whitespace_normalized("abc def ghi ", 1)); assert(!is_whitespace_normalized(" ", 0)); assert(is_whitespace_normalized(" ", 1)); assert(!is_whitespace_normalized("\t", 0)); assert(!is_whitespace_normalized("\t", 1)); assert(!is_whitespace_normalized("\n", 0)); assert(!is_whitespace_normalized("\n", 1)); assert(!is_whitespace_normalized("\r", 0)); assert(!is_whitespace_normalized("\r", 1)); assert(!is_whitespace_normalized("abc\t def", 1)); } static void XMLCALL check_attr_contains_normalized_whitespace(void *UNUSED_P(userData), const XML_Char *UNUSED_P(name), const XML_Char **atts) { int i; for (i = 0; atts[i] != NULL; i += 2) { const XML_Char *attrname = atts[i]; const XML_Char *value = atts[i + 1]; if (strcmp("attr", attrname) == 0 || strcmp("ents", attrname) == 0 || strcmp("refs", attrname) == 0) { if (!is_whitespace_normalized(value, 0)) { char buffer[256]; sprintf(buffer, "attribute value not normalized: %s='%s'", attrname, value); fail(buffer); } } } } START_TEST(test_attr_whitespace_normalization) { const char *text = "\n" "]>\n" "\n" " \n" " \n" ""; XML_SetStartElementHandler(parser, check_attr_contains_normalized_whitespace); if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) xml_failure(parser); } END_TEST /* * XML declaration tests. */ START_TEST(test_xmldecl_misplaced) { expect_failure("\n" "\n" "", XML_ERROR_MISPLACED_XML_PI, "failed to report misplaced XML declaration"); } END_TEST /* Regression test for SF bug #584832. */ static int XMLCALL UnknownEncodingHandler(void *UNUSED_P(data),const XML_Char *encoding,XML_Encoding *info) { if (strcmp(encoding,"unsupported-encoding") == 0) { int i; for (i = 0; i < 256; ++i) info->map[i] = i; info->data = NULL; info->convert = NULL; info->release = NULL; return XML_STATUS_OK; } return XML_STATUS_ERROR; } START_TEST(test_unknown_encoding_internal_entity) { const char *text = "\n" "]>\n" ""; XML_SetUnknownEncodingHandler(parser, UnknownEncodingHandler, NULL); if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) xml_failure(parser); } END_TEST /* Test unrecognised encoding handler */ static void dummy_release(void *UNUSED_P(data)) { } static int XMLCALL UnrecognisedEncodingHandler(void *UNUSED_P(data), const XML_Char *UNUSED_P(encoding), XML_Encoding *info) { info->data = NULL; info->convert = NULL; info->release = dummy_release; return XML_STATUS_ERROR; } START_TEST(test_unrecognised_encoding_internal_entity) { const char *text = "\n" "]>\n" ""; XML_SetUnknownEncodingHandler(parser, UnrecognisedEncodingHandler, NULL); if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), XML_TRUE) != XML_STATUS_ERROR) fail("Unrecognised encoding not rejected"); } END_TEST /* Regression test for SF bug #620106. */ static int XMLCALL external_entity_loader(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)) { ExtTest *test_data = (ExtTest *)XML_GetUserData(parser); XML_Parser extparser; extparser = XML_ExternalEntityParserCreate(parser, context, NULL); if (extparser == NULL) fail("Could not create external entity parser."); if (test_data->encoding != NULL) { if (!XML_SetEncoding(extparser, test_data->encoding)) fail("XML_SetEncoding() ignored for external entity"); } if ( _XML_Parse_SINGLE_BYTES(extparser, test_data->parse_text, strlen(test_data->parse_text), XML_TRUE) == XML_STATUS_ERROR) { xml_failure(extparser); return XML_STATUS_ERROR; } XML_ParserFree(extparser); return XML_STATUS_OK; } START_TEST(test_ext_entity_set_encoding) { const char *text = "\n" "]>\n" "&en;"; ExtTest test_data = { /* This text says it's an unsupported encoding, but it's really UTF-8, which we tell Expat using XML_SetEncoding(). */ "\xC3\xA9", "utf-8", NULL }; XML_SetExternalEntityRefHandler(parser, external_entity_loader); run_ext_character_check(text, &test_data, "\xC3\xA9"); } 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 */ START_TEST(test_ext_entity_set_bom) { const char *text = "\n" "]>\n" "&en;"; ExtTest test_data = { "\xEF\xBB\xBF" /* BOM */ "" "\xC3\xA9", "utf-8", NULL }; XML_SetExternalEntityRefHandler(parser, external_entity_loader); run_ext_character_check(text, &test_data, "\xC3\xA9"); } END_TEST /* Test that bad encodings are faulted */ typedef struct ext_faults { const char *parse_text; const char *fail_text; const char *encoding; enum XML_Error error; } ExtFaults; static int XMLCALL external_entity_faulter(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)) { XML_Parser ext_parser; ExtFaults *fault = (ExtFaults *)XML_GetUserData(parser); ext_parser = XML_ExternalEntityParserCreate(parser, context, NULL); if (ext_parser == NULL) fail("Could not create external entity parser"); if (fault->encoding != NULL) { if (!XML_SetEncoding(ext_parser, fault->encoding)) fail("XML_SetEncoding failed"); } if (_XML_Parse_SINGLE_BYTES(ext_parser, fault->parse_text, strlen(fault->parse_text), XML_TRUE) != XML_STATUS_ERROR) fail(fault->fail_text); if (XML_GetErrorCode(ext_parser) != fault->error) xml_failure(ext_parser); XML_ParserFree(ext_parser); return XML_STATUS_ERROR; } START_TEST(test_ext_entity_bad_encoding) { const char *text = "\n" "]>\n" "&en;"; ExtFaults fault = { "u", "Unsupported encoding not faulted", "unknown", XML_ERROR_UNKNOWN_ENCODING }; XML_SetExternalEntityRefHandler(parser, external_entity_faulter); XML_SetUserData(parser, &fault); expect_failure(text, XML_ERROR_EXTERNAL_ENTITY_HANDLING, "Bad encoding should not have been accepted"); } END_TEST /* Try handing an invalid encoding to an external entity parser */ START_TEST(test_ext_entity_bad_encoding_2) { const char *text = "\n" "\n" "&entity;"; ExtFaults fault = { "", "Unknown encoding not faulted", "unknown-encoding", XML_ERROR_UNKNOWN_ENCODING }; 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, "Bad encoding not faulted in external entity handler"); } END_TEST /* Test that no error is reported for unknown entities if we don't read an external subset. This was fixed in Expat 1.95.5. */ START_TEST(test_wfc_undeclared_entity_unread_external_subset) { const char *text = "\n" "&entity;"; if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) xml_failure(parser); } END_TEST /* Test that an error is reported for unknown entities if we don't have an external subset. */ START_TEST(test_wfc_undeclared_entity_no_external_subset) { expect_failure("&entity;", XML_ERROR_UNDEFINED_ENTITY, "Parser did not report undefined entity w/out a DTD."); } END_TEST /* Test that an error is reported for unknown entities if we don't read an external subset, but have been declared standalone. */ START_TEST(test_wfc_undeclared_entity_standalone) { const char *text = "\n" "\n" "&entity;"; expect_failure(text, XML_ERROR_UNDEFINED_ENTITY, "Parser did not report undefined entity (standalone)."); } END_TEST /* Test that an error is reported for unknown entities if we have read an external subset, and standalone is true. */ START_TEST(test_wfc_undeclared_entity_with_external_subset_standalone) { const char *text = "\n" "\n" "&entity;"; ExtTest test_data = { "", NULL, NULL }; XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS); XML_SetUserData(parser, &test_data); XML_SetExternalEntityRefHandler(parser, external_entity_loader); expect_failure(text, XML_ERROR_UNDEFINED_ENTITY, "Parser did not report undefined entity (external DTD)."); } END_TEST /* Test that external entity handling is not done if the parsing flag * is set to UNLESS_STANDALONE */ START_TEST(test_entity_with_external_subset_unless_standalone) { const char *text = "\n" "\n" "&entity;"; ExtTest test_data = { "", NULL, NULL }; XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_UNLESS_STANDALONE); XML_SetUserData(parser, &test_data); XML_SetExternalEntityRefHandler(parser, external_entity_loader); expect_failure(text, XML_ERROR_UNDEFINED_ENTITY, "Parser did not report undefined entity"); } END_TEST /* Test that no error is reported for unknown entities if we have read an external subset, and standalone is false. */ START_TEST(test_wfc_undeclared_entity_with_external_subset) { const char *text = "\n" "\n" "&entity;"; ExtTest test_data = { "", NULL, NULL }; XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS); XML_SetExternalEntityRefHandler(parser, external_entity_loader); run_ext_character_check(text, &test_data, ""); } END_TEST /* Test that an error is reported if our NotStandalone handler fails */ static int XMLCALL reject_not_standalone_handler(void *UNUSED_P(userData)) { return XML_STATUS_ERROR; } START_TEST(test_not_standalone_handler_reject) { const char *text = "\n" "\n" "&entity;"; ExtTest test_data = { "", NULL, NULL }; XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS); XML_SetUserData(parser, &test_data); XML_SetExternalEntityRefHandler(parser, external_entity_loader); XML_SetNotStandaloneHandler(parser, reject_not_standalone_handler); expect_failure(text, XML_ERROR_NOT_STANDALONE, "NotStandalone handler failed to reject"); /* Try again but without external entity handling */ XML_ParserReset(parser, NULL); XML_SetNotStandaloneHandler(parser, reject_not_standalone_handler); expect_failure(text, XML_ERROR_NOT_STANDALONE, "NotStandalone handler failed to reject"); } END_TEST /* Test that no error is reported if our NotStandalone handler succeeds */ static int XMLCALL accept_not_standalone_handler(void *UNUSED_P(userData)) { return XML_STATUS_OK; } START_TEST(test_not_standalone_handler_accept) { const char *text = "\n" "\n" "&entity;"; ExtTest test_data = { "", NULL, NULL }; XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS); XML_SetExternalEntityRefHandler(parser, external_entity_loader); XML_SetNotStandaloneHandler(parser, accept_not_standalone_handler); run_ext_character_check(text, &test_data, ""); /* Repeat wtihout the external entity handler */ XML_ParserReset(parser, NULL); XML_SetNotStandaloneHandler(parser, accept_not_standalone_handler); run_character_check(text, ""); } END_TEST START_TEST(test_wfc_no_recursive_entity_refs) { const char *text = "\n" "]>\n" "&entity;"; expect_failure(text, XML_ERROR_RECURSIVE_ENTITY_REF, "Parser did not report recursive entity reference."); } END_TEST /* Test incomplete external entities are faulted */ START_TEST(test_ext_entity_invalid_parse) { const char *text = "\n" "]>\n" "&en;"; const ExtFaults faults[] = { { "<", "Incomplete element declaration not faulted", NULL, XML_ERROR_UNCLOSED_TOKEN }, { "<\xe2\x82", /* First two bytes of a three-byte char */ "Incomplete character not faulted", NULL, XML_ERROR_PARTIAL_CHAR }, { "\xe2\x82", "Incomplete character in CDATA not faulted", NULL, XML_ERROR_PARTIAL_CHAR }, { NULL, NULL, NULL, XML_ERROR_NONE } }; const ExtFaults *fault = faults; for (; fault->parse_text != NULL; fault++) { XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS); XML_SetExternalEntityRefHandler(parser, external_entity_faulter); XML_SetUserData(parser, (void *)fault); expect_failure(text, XML_ERROR_EXTERNAL_ENTITY_HANDLING, "Parser did not report external entity error"); XML_ParserReset(parser, NULL); } } END_TEST /* Regression test for SF bug #483514. */ START_TEST(test_dtd_default_handling) { const char *text = "\n" "\n" "\n" "\n" "\n" "\n" "]>"; XML_SetDefaultHandler(parser, accumulate_characters); XML_SetStartDoctypeDeclHandler(parser, dummy_start_doctype_handler); XML_SetEndDoctypeDeclHandler(parser, dummy_end_doctype_handler); XML_SetEntityDeclHandler(parser, dummy_entity_decl_handler); XML_SetNotationDeclHandler(parser, dummy_notation_decl_handler); XML_SetElementDeclHandler(parser, dummy_element_decl_handler); XML_SetAttlistDeclHandler(parser, dummy_attlist_decl_handler); XML_SetProcessingInstructionHandler(parser, dummy_pi_handler); XML_SetCommentHandler(parser, dummy_comment_handler); XML_SetStartCdataSectionHandler(parser, dummy_start_cdata_handler); XML_SetEndCdataSectionHandler(parser, dummy_end_cdata_handler); run_character_check(text, "\n\n\n\n\n\n\n"); } END_TEST /* Test handling of attribute declarations */ typedef struct AttTest { const XML_Char *definition; const XML_Char *element_name; const XML_Char *attr_name; const XML_Char *attr_type; const XML_Char *default_value; int is_required; } AttTest; static void XMLCALL verify_attlist_decl_handler(void *userData, const XML_Char *element_name, const XML_Char *attr_name, const XML_Char *attr_type, const XML_Char *default_value, int is_required) { AttTest *at = (AttTest *)userData; if (strcmp(element_name, at->element_name)) fail("Unexpected element name in attribute declaration"); if (strcmp(attr_name, at->attr_name)) fail("Unexpected attribute name in attribute declaration"); if (strcmp(attr_type, at->attr_type)) fail("Unexpected attribute type in attribute declaration"); if ((default_value == NULL && at->default_value != NULL) || (default_value != NULL && at->default_value == NULL) || (default_value != NULL && strcmp(default_value, at->default_value))) fail("Unexpected default value in attribute declaration"); if (is_required != at->is_required) fail("Requirement mismatch in attribute declaration"); } START_TEST(test_dtd_attr_handling) { const char *prolog = "\n"; AttTest attr_data[] = { { "\n" "]>" "", "doc", "a", "(one|two|three)", /* Extraneous spaces will be removed */ NULL, XML_TRUE }, { "\n" "\n" "]>" "", "doc", "a", "NOTATION(foo)", NULL, XML_FALSE }, { "\n" "]>" "", "doc", "a", "NOTATION(foo)", "bar", XML_FALSE }, { NULL, NULL, NULL, NULL, NULL, XML_FALSE } }; AttTest *test; for (test = attr_data; test->definition != NULL; test++) { XML_SetAttlistDeclHandler(parser, verify_attlist_decl_handler); XML_SetUserData(parser, test); if (_XML_Parse_SINGLE_BYTES(parser, prolog, strlen(prolog), XML_FALSE) == XML_STATUS_ERROR) xml_failure(parser); if (_XML_Parse_SINGLE_BYTES(parser, test->definition, strlen(test->definition), XML_TRUE) == XML_STATUS_ERROR) xml_failure(parser); XML_ParserReset(parser, NULL); } } END_TEST /* See related SF bug #673791. When namespace processing is enabled, setting the namespace URI for a prefix is not allowed; this test ensures that it *is* allowed when namespace processing is not enabled. (See Namespaces in XML, section 2.) */ START_TEST(test_empty_ns_without_namespaces) { const char *text = "\n" " \n" ""; if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) xml_failure(parser); } END_TEST /* Regression test for SF bug #824420. Checks that an xmlns:prefix attribute set in an attribute's default value isn't misinterpreted. */ START_TEST(test_ns_in_attribute_default_without_namespaces) { const char *text = "\n" " ]>\n" ""; if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) xml_failure(parser); } END_TEST static const char *long_character_data_text = "" "012345678901234567890123456789012345678901234567890123456789" "012345678901234567890123456789012345678901234567890123456789" "012345678901234567890123456789012345678901234567890123456789" "012345678901234567890123456789012345678901234567890123456789" "012345678901234567890123456789012345678901234567890123456789" "012345678901234567890123456789012345678901234567890123456789" "012345678901234567890123456789012345678901234567890123456789" "012345678901234567890123456789012345678901234567890123456789" "012345678901234567890123456789012345678901234567890123456789" "012345678901234567890123456789012345678901234567890123456789" "012345678901234567890123456789012345678901234567890123456789" "012345678901234567890123456789012345678901234567890123456789" "012345678901234567890123456789012345678901234567890123456789" "012345678901234567890123456789012345678901234567890123456789" "012345678901234567890123456789012345678901234567890123456789" "012345678901234567890123456789012345678901234567890123456789" "012345678901234567890123456789012345678901234567890123456789" "012345678901234567890123456789012345678901234567890123456789" "012345678901234567890123456789012345678901234567890123456789" "012345678901234567890123456789012345678901234567890123456789" ""; static XML_Bool resumable = XML_FALSE; static void clearing_aborting_character_handler(void *UNUSED_P(userData), const XML_Char *UNUSED_P(s), int UNUSED_P(len)) { XML_StopParser(parser, resumable); XML_SetCharacterDataHandler(parser, NULL); } /* Regression test for SF bug #1515266: missing check of stopped parser in doContext() 'for' loop. */ START_TEST(test_stop_parser_between_char_data_calls) { /* The sample data must be big enough that there are two calls to the character data handler from within the inner "for" loop of the XML_TOK_DATA_CHARS case in doContent(), and the character handler must stop the parser and clear the character data handler. */ const char *text = long_character_data_text; XML_SetCharacterDataHandler(parser, clearing_aborting_character_handler); resumable = XML_FALSE; if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), XML_TRUE) != XML_STATUS_ERROR) xml_failure(parser); if (XML_GetErrorCode(parser) != XML_ERROR_ABORTED) xml_failure(parser); } END_TEST /* Regression test for SF bug #1515266: missing check of stopped parser in doContext() 'for' loop. */ START_TEST(test_suspend_parser_between_char_data_calls) { /* The sample data must be big enough that there are two calls to the character data handler from within the inner "for" loop of the XML_TOK_DATA_CHARS case in doContent(), and the character handler must stop the parser and clear the character data handler. */ const char *text = long_character_data_text; XML_SetCharacterDataHandler(parser, clearing_aborting_character_handler); resumable = XML_TRUE; if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), XML_TRUE) != XML_STATUS_SUSPENDED) xml_failure(parser); if (XML_GetErrorCode(parser) != XML_ERROR_NONE) xml_failure(parser); /* Try parsing directly */ if (XML_Parse(parser, text, strlen(text), XML_TRUE) != XML_STATUS_ERROR) fail("Attempt to continue parse while suspended not faulted"); if (XML_GetErrorCode(parser) != XML_ERROR_SUSPENDED) fail("Suspended parse not faulted with correct error"); } END_TEST static XML_Bool abortable = XML_FALSE; static void parser_stop_character_handler(void *UNUSED_P(userData), const XML_Char *UNUSED_P(s), int UNUSED_P(len)) { XML_StopParser(parser, resumable); XML_SetCharacterDataHandler(parser, NULL); if (!resumable) { /* Check that aborting an aborted parser is faulted */ if (XML_StopParser(parser, XML_FALSE) != XML_STATUS_ERROR) fail("Aborting aborted parser not faulted"); if (XML_GetErrorCode(parser) != XML_ERROR_FINISHED) xml_failure(parser); } else if (abortable) { /* Check that aborting a suspended parser works */ if (XML_StopParser(parser, XML_FALSE) == XML_STATUS_ERROR) xml_failure(parser); } else { /* Check that suspending a suspended parser works */ if (XML_StopParser(parser, XML_TRUE) != XML_STATUS_ERROR) fail("Suspending suspended parser not faulted"); if (XML_GetErrorCode(parser) != XML_ERROR_SUSPENDED) xml_failure(parser); } } /* Test repeated calls to XML_StopParser are handled correctly */ START_TEST(test_repeated_stop_parser_between_char_data_calls) { const char *text = long_character_data_text; XML_SetCharacterDataHandler(parser, parser_stop_character_handler); resumable = XML_FALSE; abortable = XML_FALSE; if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), XML_TRUE) != XML_STATUS_ERROR) fail("Failed to double-stop parser"); XML_ParserReset(parser, NULL); XML_SetCharacterDataHandler(parser, parser_stop_character_handler); resumable = XML_TRUE; abortable = XML_FALSE; if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), XML_TRUE) != XML_STATUS_SUSPENDED) fail("Failed to double-suspend parser"); XML_ParserReset(parser, NULL); XML_SetCharacterDataHandler(parser, parser_stop_character_handler); resumable = XML_TRUE; abortable = XML_TRUE; if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), XML_TRUE) != XML_STATUS_ERROR) fail("Failed to suspend-abort parser"); } END_TEST START_TEST(test_good_cdata_ascii) { const char *text = "Hello, world!]]>"; const char *expected = "Hello, world!"; CharData storage; 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); CharData_CheckXMLChars(&storage, expected); } END_TEST START_TEST(test_good_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""1\0""6\0'" "\0?\0>\0\n" "\0<\0a\0>\0<\0!\0[\0C\0D\0A\0T\0A\0[\0h\0e\0l\0l\0o\0]\0]\0>\0<\0/\0a\0>"; const char *expected = "hello"; 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 /* 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 { const char *text; enum XML_Error expectedError; }; struct CaseData cases[] = { {"<", XML_ERROR_UNCLOSED_TOKEN}, {"", XML_ERROR_INVALID_TOKEN}, {"", XML_ERROR_UNCLOSED_TOKEN}, /* ?! */ {"", XML_ERROR_UNCLOSED_TOKEN}, /* ?! */ {"", XML_ERROR_INVALID_TOKEN}, {"", XML_ERROR_INVALID_TOKEN}, {"", XML_ERROR_INVALID_TOKEN}, {"", XML_ERROR_INVALID_TOKEN}, {"", XML_ERROR_UNCLOSED_CDATA_SECTION}, {"", XML_ERROR_UNCLOSED_CDATA_SECTION}, {"", XML_ERROR_UNCLOSED_CDATA_SECTION} }; size_t i = 0; for (; i < sizeof(cases) / sizeof(struct CaseData); i++) { const enum XML_Status actualStatus = _XML_Parse_SINGLE_BYTES( parser, cases[i].text, strlen(cases[i].text), XML_TRUE); const enum XML_Error actualError = XML_GetErrorCode(parser); assert(actualStatus == XML_STATUS_ERROR); if (actualError != cases[i].expectedError) { char message[100]; sprintf(message, "Expected error %d but got error %d for case %u: \"%s\"\n", cases[i].expectedError, actualError, (unsigned int)i + 1, cases[i].text); fail(message); } XML_ParserReset(parser, NULL); } } 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) { char *buffer = (char *)XML_MemMalloc(parser, 256); char *p; if (buffer == NULL) { fail("Allocation failed"); } else { /* Try writing to memory; some OSes try to cheat! */ buffer[0] = 'T'; buffer[1] = 'E'; buffer[2] = 'S'; buffer[3] = 'T'; buffer[4] = '\0'; if (strcmp(buffer, "TEST") != 0) { fail("Memory not writable"); } else { p = (char *)XML_MemRealloc(parser, buffer, 512); if (p == NULL) { fail("Reallocation failed"); } else { /* Write again, just to be sure */ buffer = p; buffer[0] = 'V'; if (strcmp(buffer, "VEST") != 0) { fail("Reallocated memory not writable"); } } } XML_MemFree(parser, buffer); } } END_TEST static void XMLCALL record_default_handler(void *userData, const XML_Char *UNUSED_P(s), int UNUSED_P(len)) { CharData_AppendString((CharData *)userData, "D"); } static void XMLCALL record_cdata_handler(void *userData, const XML_Char *UNUSED_P(s), int UNUSED_P(len)) { CharData_AppendString((CharData *)userData, "C"); XML_DefaultCurrent(parser); } static void XMLCALL record_cdata_nodefault_handler(void *userData, const XML_Char *UNUSED_P(s), int UNUSED_P(len)) { CharData_AppendString((CharData *)userData, "c"); } static void XMLCALL record_skip_handler(void *userData, const XML_Char *UNUSED_P(entityName), int is_parameter_entity) { CharData_AppendString((CharData *)userData, is_parameter_entity ? "E" : "e"); } /* Test XML_DefaultCurrent() passes handling on correctly */ START_TEST(test_default_current) { const char *text = "hell]"; const char *entity_text = "\n" "]>\n" "&entity;"; CharData storage; XML_SetDefaultHandler(parser, record_default_handler); XML_SetCharacterDataHandler(parser, record_cdata_handler); CharData_Init(&storage); XML_SetUserData(parser, &storage); if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) xml_failure(parser); CharData_CheckString(&storage, "DCDCDCDCDCDD"); /* Again, without the defaulting */ XML_ParserReset(parser, NULL); XML_SetDefaultHandler(parser, record_default_handler); XML_SetCharacterDataHandler(parser, record_cdata_nodefault_handler); CharData_Init(&storage); XML_SetUserData(parser, &storage); if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) xml_failure(parser); CharData_CheckString(&storage, "DcccccD"); /* Now with an internal entity to complicate matters */ XML_ParserReset(parser, NULL); XML_SetDefaultHandler(parser, record_default_handler); XML_SetCharacterDataHandler(parser, record_cdata_handler); CharData_Init(&storage); XML_SetUserData(parser, &storage); if (_XML_Parse_SINGLE_BYTES(parser, entity_text, strlen(entity_text), XML_TRUE) == XML_STATUS_ERROR) xml_failure(parser); /* The default handler suppresses the entity */ CharData_CheckString(&storage, "DDDDDDDDDDDDDDDDDDD"); /* Again, with a skip handler */ XML_ParserReset(parser, NULL); XML_SetDefaultHandler(parser, record_default_handler); XML_SetCharacterDataHandler(parser, record_cdata_handler); XML_SetSkippedEntityHandler(parser, record_skip_handler); CharData_Init(&storage); XML_SetUserData(parser, &storage); if (_XML_Parse_SINGLE_BYTES(parser, entity_text, strlen(entity_text), XML_TRUE) == XML_STATUS_ERROR) xml_failure(parser); /* The default handler suppresses the entity */ CharData_CheckString(&storage, "DDDDDDDDDDDDDDDDDeD"); /* This time, allow the entity through */ XML_ParserReset(parser, NULL); XML_SetDefaultHandlerExpand(parser, record_default_handler); XML_SetCharacterDataHandler(parser, record_cdata_handler); CharData_Init(&storage); XML_SetUserData(parser, &storage); if (_XML_Parse_SINGLE_BYTES(parser, entity_text, strlen(entity_text), XML_TRUE) == XML_STATUS_ERROR) xml_failure(parser); CharData_CheckString(&storage, "DDDDDDDDDDDDDDDDDCDD"); /* Finally, without passing the cdata to the default handler */ XML_ParserReset(parser, NULL); XML_SetDefaultHandlerExpand(parser, record_default_handler); XML_SetCharacterDataHandler(parser, record_cdata_nodefault_handler); CharData_Init(&storage); XML_SetUserData(parser, &storage); if (_XML_Parse_SINGLE_BYTES(parser, entity_text, strlen(entity_text), XML_TRUE) == XML_STATUS_ERROR) xml_failure(parser); CharData_CheckString(&storage, "DDDDDDDDDDDDDDDDDcD"); } END_TEST /* Test DTD element parsing code paths */ START_TEST(test_dtd_elements) { const char *text = "\n" "\n" "]>\n" "Wombats are go"; XML_SetElementDeclHandler(parser, dummy_element_decl_handler); if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) xml_failure(parser); } END_TEST /* Test foreign DTD handling */ START_TEST(test_set_foreign_dtd) { const char *text1 = "\n"; const char *text2 = "&entity;"; ExtTest test_data = { "", NULL, NULL }; /* Check hash salt is passed through too */ XML_SetHashSalt(parser, 0x12345678); XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS); XML_SetUserData(parser, &test_data); 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), XML_FALSE) == XML_STATUS_ERROR) xml_failure(parser); /* 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_CANT_CHANGE_FEATURE_ONCE_PARSING) fail("Failed to reject late foreign DTD setting"); /* Ditto for the hash salt */ if (XML_SetHashSalt(parser, 0x23456789)) fail("Failed to reject late hash salt change"); /* Now finish the parse */ if (_XML_Parse_SINGLE_BYTES(parser, text2, strlen(text2), XML_TRUE) == XML_STATUS_ERROR) xml_failure(parser); } END_TEST /* Test foreign DTD handling with a failing NotStandalone handler */ START_TEST(test_foreign_dtd_not_standalone) { const char *text = "\n" "&entity;"; ExtTest test_data = { "", NULL, NULL }; XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS); XML_SetUserData(parser, &test_data); XML_SetExternalEntityRefHandler(parser, external_entity_loader); XML_SetNotStandaloneHandler(parser, reject_not_standalone_handler); if (XML_UseForeignDTD(parser, XML_TRUE) != XML_ERROR_NONE) fail("Could not set foreign DTD"); expect_failure(text, XML_ERROR_NOT_STANDALONE, "NotStandalonehandler failed to reject"); } END_TEST /* Test invalid character in a foreign DTD is faulted */ START_TEST(test_invalid_foreign_dtd) { const char *text = "\n" "&entity;"; ExtFaults test_data = { "$", "Dollar not faulted", NULL, XML_ERROR_INVALID_TOKEN }; XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS); XML_SetUserData(parser, &test_data); XML_SetExternalEntityRefHandler(parser, external_entity_faulter); XML_UseForeignDTD(parser, XML_TRUE); expect_failure(text, XML_ERROR_EXTERNAL_ENTITY_HANDLING, "Bad DTD should not have been accepted"); } END_TEST /* Test foreign DTD use with a doctype */ START_TEST(test_foreign_dtd_with_doctype) { const char *text1 = "\n" "]>\n"; const char *text2 = "&entity;"; ExtTest test_data = { "", NULL, NULL }; /* Check hash salt is passed through too */ XML_SetHashSalt(parser, 0x12345678); XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS); XML_SetUserData(parser, &test_data); 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), XML_FALSE) == XML_STATUS_ERROR) xml_failure(parser); /* 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_CANT_CHANGE_FEATURE_ONCE_PARSING) fail("Failed to reject late foreign DTD setting"); /* Ditto for the hash salt */ if (XML_SetHashSalt(parser, 0x23456789)) fail("Failed to reject late hash salt change"); /* Now finish the parse */ if (_XML_Parse_SINGLE_BYTES(parser, text2, strlen(text2), XML_TRUE) == XML_STATUS_ERROR) xml_failure(parser); } END_TEST /* Test XML_UseForeignDTD with no external subset present */ static int XMLCALL external_entity_null_loader(XML_Parser UNUSED_P(parser), const XML_Char *UNUSED_P(context), const XML_Char *UNUSED_P(base), const XML_Char *UNUSED_P(systemId), const XML_Char *UNUSED_P(publicId)) { return XML_STATUS_OK; } START_TEST(test_foreign_dtd_without_external_subset) { const char *text = "]>\n" "&foo;"; XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS); XML_SetUserData(parser, NULL); XML_SetExternalEntityRefHandler(parser, external_entity_null_loader); XML_UseForeignDTD(parser, XML_TRUE); if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) xml_failure(parser); } END_TEST START_TEST(test_empty_foreign_dtd) { const char *text = "\n" "&entity;"; XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS); XML_SetExternalEntityRefHandler(parser, external_entity_null_loader); XML_UseForeignDTD(parser, XML_TRUE); expect_failure(text, XML_ERROR_UNDEFINED_ENTITY, "Undefined entity not faulted"); } END_TEST /* Test XML Base is set and unset appropriately */ START_TEST(test_set_base) { const XML_Char *old_base; const XML_Char *new_base = "/local/file/name.xml"; old_base = XML_GetBase(parser); if (XML_SetBase(parser, new_base) != XML_STATUS_OK) fail("Unable to set base"); if (strcmp(XML_GetBase(parser), new_base) != 0) fail("Base setting not correct"); if (XML_SetBase(parser, NULL) != XML_STATUS_OK) fail("Unable to NULL base"); if (XML_GetBase(parser) != NULL) fail("Base setting not nulled"); XML_SetBase(parser, old_base); } END_TEST /* Test attribute counts, indexing, etc */ typedef struct attrInfo { const char *name; const char *value; } AttrInfo; typedef struct elementInfo { const char *name; int attr_count; const char *id_name; AttrInfo *attributes; } ElementInfo; static void XMLCALL counting_start_element_handler(void *userData, const XML_Char *name, const XML_Char **atts) { ElementInfo *info = (ElementInfo *)userData; AttrInfo *attr; int count, id, i; while (info->name != NULL) { if (!strcmp(name, info->name)) break; info++; } if (info->name == NULL) fail("Element not recognised"); /* 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"); return; } id = XML_GetIdAttributeIndex(parser); if (id == -1 && info->id_name != NULL) { fail("ID not present"); return; } if (id != -1 && strcmp(atts[id], info->id_name)) { fail("ID does not have the correct name"); return; } for (i = 0; i < info->attr_count; i++) { attr = info->attributes; while (attr->name != NULL) { if (!strcmp(atts[0], attr->name)) break; attr++; } if (attr->name == NULL) { fail("Attribute not recognised"); return; } if (strcmp(atts[1], attr->value)) { fail("Attribute has wrong value"); return; } /* Remember, two entries in atts per attribute (see above) */ atts += 2; } } START_TEST(test_attributes) { const char *text = "\n" "\n" "]>" "" "" ""; AttrInfo doc_info[] = { { "a", "1" }, { "b", "2" }, { "id", "one" }, { NULL, NULL } }; AttrInfo tag_info[] = { { "c", "3" }, { NULL, NULL } }; ElementInfo info[] = { { "doc", 3, "id", NULL }, { "tag", 1, NULL, NULL }, { NULL, 0, NULL, NULL } }; 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) xml_failure(parser); } END_TEST /* Test reset works correctly in the middle of processing an internal * entity. Exercises some obscure code in XML_ParserReset(). */ START_TEST(test_reset_in_entity) { const char *text = "\n" "\n" "]>\n" "&entity;"; XML_ParsingStatus status; resumable = XML_TRUE; XML_SetCharacterDataHandler(parser, clearing_aborting_character_handler); if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), XML_FALSE) == XML_STATUS_ERROR) xml_failure(parser); XML_GetParsingStatus(parser, &status); if (status.parsing != XML_SUSPENDED) fail("Parsing status not SUSPENDED"); XML_ParserReset(parser, NULL); XML_GetParsingStatus(parser, &status); if (status.parsing != XML_INITIALIZED) fail("Parsing status doesn't reset to INITIALIZED"); } END_TEST /* Test that resume correctly passes through parse errors */ START_TEST(test_resume_invalid_parse) { const char *text = "Hello\n" "&entity;"; XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS); XML_SetExternalEntityRefHandler(parser, external_entity_resetter); if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) xml_failure(parser); } END_TEST /* Test suspending a subordinate parser */ static void XMLCALL entity_suspending_decl_handler(void *userData, const XML_Char *UNUSED_P(name), XML_Content *model) { XML_Parser ext_parser = (XML_Parser)userData; if (XML_StopParser(ext_parser, XML_TRUE) != XML_STATUS_ERROR) fail("Attempting to suspend a subordinate parser not faulted"); if (XML_GetErrorCode(ext_parser) != XML_ERROR_SUSPEND_PE) fail("Suspending subordinate parser get wrong code"); XML_SetElementDeclHandler(ext_parser, NULL); XML_FreeContentModel(parser, model); } static int XMLCALL external_entity_suspender(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_SetElementDeclHandler(ext_parser, entity_suspending_decl_handler); XML_SetUserData(ext_parser, ext_parser); if (_XML_Parse_SINGLE_BYTES(ext_parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) { xml_failure(ext_parser); return XML_STATUS_ERROR; } XML_ParserFree(ext_parser); return XML_STATUS_OK; } START_TEST(test_subordinate_suspend) { const char *text = "\n" "\n" "&entity;"; XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS); XML_SetExternalEntityRefHandler(parser, external_entity_suspender); if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) xml_failure(parser); } END_TEST /* Test suspending a subordinate parser from an XML declaration */ /* Increases code coverage of the tests */ static void XMLCALL entity_suspending_xdecl_handler(void *userData, const XML_Char *UNUSED_P(version), const XML_Char *UNUSED_P(encoding), int UNUSED_P(standalone)) { XML_Parser ext_parser = (XML_Parser)userData; XML_StopParser(ext_parser, resumable); XML_SetXmlDeclHandler(ext_parser, NULL); } static int XMLCALL external_entity_suspend_xmldecl(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; XML_ParsingStatus status; enum XML_Status rc; ext_parser = XML_ExternalEntityParserCreate(parser, context, NULL); if (ext_parser == NULL) fail("Could not create external entity parser"); XML_SetXmlDeclHandler(ext_parser, entity_suspending_xdecl_handler); XML_SetUserData(ext_parser, ext_parser); rc = _XML_Parse_SINGLE_BYTES(ext_parser, text, strlen(text), XML_TRUE); XML_GetParsingStatus(ext_parser, &status); if (resumable) { if (rc == XML_STATUS_ERROR) xml_failure(ext_parser); if (status.parsing != XML_SUSPENDED) fail("Ext Parsing status not SUSPENDED"); } else { if (rc != XML_STATUS_ERROR) fail("Ext parsing not aborted"); if (XML_GetErrorCode(ext_parser) != XML_ERROR_ABORTED) xml_failure(ext_parser); if (status.parsing != XML_FINISHED) fail("Ext Parsing status not FINISHED"); } XML_ParserFree(ext_parser); return XML_STATUS_OK; } START_TEST(test_subordinate_xdecl_suspend) { const char *text = "\n" "]>\n" "&entity;"; XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS); XML_SetExternalEntityRefHandler(parser, external_entity_suspend_xmldecl); resumable = XML_TRUE; if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) xml_failure(parser); } END_TEST START_TEST(test_subordinate_xdecl_abort) { const char *text = "\n" "]>\n" "&entity;"; XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS); XML_SetExternalEntityRefHandler(parser, external_entity_suspend_xmldecl); resumable = XML_FALSE; if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) xml_failure(parser); } END_TEST /* Test external entity fault handling with suspension */ static int XMLCALL external_entity_suspending_faulter(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)) { XML_Parser ext_parser; ExtFaults *fault = (ExtFaults *)XML_GetUserData(parser); void *buffer; int parse_len = strlen(fault->parse_text); ext_parser = XML_ExternalEntityParserCreate(parser, context, NULL); if (ext_parser == NULL) fail("Could not create external entity parser"); XML_SetXmlDeclHandler(ext_parser, entity_suspending_xdecl_handler); XML_SetUserData(ext_parser, ext_parser); resumable = XML_TRUE; buffer = XML_GetBuffer(ext_parser, parse_len); if (buffer == NULL) fail("Could not allocate parse buffer"); memcpy(buffer, fault->parse_text, parse_len); if (XML_ParseBuffer(ext_parser, parse_len, XML_FALSE) != XML_STATUS_SUSPENDED) fail("XML declaration did not suspend"); if (XML_ResumeParser(ext_parser) != XML_STATUS_OK) xml_failure(ext_parser); if (XML_ParseBuffer(ext_parser, 0, XML_TRUE) != XML_STATUS_ERROR) fail(fault->fail_text); if (XML_GetErrorCode(ext_parser) != fault->error) xml_failure(ext_parser); XML_ParserFree(ext_parser); return XML_STATUS_ERROR; } START_TEST(test_ext_entity_invalid_suspended_parse) { const char *text = "\n" "]>\n" "&en;"; ExtFaults faults[] = { { "<", "Incomplete element declaration not faulted", NULL, XML_ERROR_UNCLOSED_TOKEN }, { /* First two bytes of a three-byte char */ "\xe2\x82", "Incomplete character not faulted", NULL, XML_ERROR_PARTIAL_CHAR }, { NULL, 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_suspending_faulter); XML_SetUserData(parser, fault); expect_failure(text, XML_ERROR_EXTERNAL_ENTITY_HANDLING, "Parser did not report external entity error"); XML_ParserReset(parser, NULL); } } END_TEST /* Test setting an explicit encoding */ START_TEST(test_explicit_encoding) { const char *text1 = "Hello "; const char *text2 = " World"; /* 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), XML_FALSE) == XML_STATUS_ERROR) xml_failure(parser); /* Try to switch encodings mid-parse */ if (XML_SetEncoding(parser, "us-ascii") != XML_STATUS_ERROR) fail("Allowed encoding change"); if (_XML_Parse_SINGLE_BYTES(parser, text2, strlen(text2), XML_TRUE) == XML_STATUS_ERROR) xml_failure(parser); /* Try now the parse is over */ if (XML_SetEncoding(parser, NULL) != XML_STATUS_OK) fail("Failed to unset 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; /* Count of the number of times the comment handler has been invoked */ static int comment_count = 0; /* Count of the number of skipped entities */ static int skip_count = 0; /* Count of the number of times the XML declaration handler is invoked */ static int xdecl_count = 0; static void XMLCALL xml_decl_handler(void *userData, const XML_Char *UNUSED_P(version), const XML_Char *UNUSED_P(encoding), int standalone) { if (userData != handler_data) fail("User data (xml decl) not correctly set"); if (standalone != -1) fail("Standalone not flagged as not present in XML decl"); xdecl_count++; } static void XMLCALL param_check_skip_handler(void *userData, const XML_Char *UNUSED_P(entityName), int UNUSED_P(is_parameter_entity)) { if (userData != handler_data) fail("User data (skip) not correctly set"); skip_count++; } static void XMLCALL data_check_comment_handler(void *userData, const XML_Char *UNUSED_P(data)) { /* Check that the userData passed through is what we expect */ if (userData != handler_data) fail("User data (parser) not correctly set"); /* Check that the user data in the parser is appropriate */ if (XML_GetUserData(userData) != (void *)1) fail("User data in parser not correctly set"); comment_count++; } static int XMLCALL external_entity_param_checker(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 = "\n" ""; XML_Parser ext_parser; ext_parser = XML_ExternalEntityParserCreate(parser, context, NULL); if (ext_parser == NULL) fail("Could not create external entity parser"); handler_data = ext_parser; if (_XML_Parse_SINGLE_BYTES(ext_parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) { xml_failure(parser); return XML_STATUS_ERROR; } handler_data = parser; XML_ParserFree(ext_parser); return XML_STATUS_OK; } START_TEST(test_user_parameters) { const char *text = "\n" "\n" "\n" "&entity;"; const char *epilog = "\n" ""; comment_count = 0; skip_count = 0; xdecl_count = 0; XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS); XML_SetXmlDeclHandler(parser, xml_decl_handler); XML_SetExternalEntityRefHandler(parser, external_entity_param_checker); XML_SetCommentHandler(parser, data_check_comment_handler); XML_SetSkippedEntityHandler(parser, param_check_skip_handler); XML_UseParserAsHandlerArg(parser); XML_SetUserData(parser, (void *)1); handler_data = parser; if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), XML_FALSE) == XML_STATUS_ERROR) xml_failure(parser); if (comment_count != 2) fail("Comment handler not invoked enough times"); /* Ensure we can't change policy mid-parse */ if (XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_NEVER)) fail("Changed param entity parsing policy while parsing"); if (_XML_Parse_SINGLE_BYTES(parser, epilog, strlen(epilog), XML_TRUE) == XML_STATUS_ERROR) xml_failure(parser); if (comment_count != 3) fail("Comment handler not invoked enough times"); if (skip_count != 1) fail("Skip handler not invoked enough times"); if (xdecl_count != 1) fail("XML declaration handler not invoked"); } 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 parameter, 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; 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; } START_TEST(test_ext_entity_ref_parameter) { const char *text = "\n" "\n" "&entity;"; XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS); XML_SetExternalEntityRefHandler(parser, external_entity_ref_param_checker); /* Set a handler arg that is not NULL and not parser (which is * what NULL would cause to be passed. */ XML_SetExternalEntityRefHandlerArg(parser, (void *)text); handler_data = (void *)text; if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) xml_failure(parser); /* Now try again with unset args */ XML_ParserReset(parser, NULL); XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS); XML_SetExternalEntityRefHandler(parser, external_entity_ref_param_checker); XML_SetExternalEntityRefHandlerArg(parser, NULL); handler_data = (void *)parser; if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) xml_failure(parser); } END_TEST /* Test the parsing of an empty string */ START_TEST(test_empty_parse) { const char *text = ""; const char *partial = ""; if (XML_Parse(parser, NULL, 0, XML_FALSE) == XML_STATUS_ERROR) fail("Parsing empty string faulted"); if (XML_Parse(parser, NULL, 0, XML_TRUE) != XML_STATUS_ERROR) fail("Parsing final empty string not faulted"); if (XML_GetErrorCode(parser) != XML_ERROR_NO_ELEMENTS) fail("Parsing final empty string faulted for wrong reason"); /* Now try with valid text before the empty end */ XML_ParserReset(parser, NULL); if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), XML_FALSE) == XML_STATUS_ERROR) xml_failure(parser); if (XML_Parse(parser, NULL, 0, XML_TRUE) == XML_STATUS_ERROR) fail("Parsing final empty string faulted"); /* Now try with invalid text before the empty end */ XML_ParserReset(parser, NULL); if (_XML_Parse_SINGLE_BYTES(parser, partial, strlen(partial), XML_FALSE) == XML_STATUS_ERROR) xml_failure(parser); if (XML_Parse(parser, NULL, 0, XML_TRUE) != XML_STATUS_ERROR) fail("Parsing final incomplete empty string not faulted"); } END_TEST /* Test odd corners of the XML_GetBuffer interface */ static enum XML_Status get_feature(enum XML_FeatureEnum feature_id, long *presult) { const XML_Feature *feature = XML_GetFeatureList(); if (feature == NULL) return XML_STATUS_ERROR; for (; feature->feature != XML_FEATURE_END; feature++) { if (feature->feature == feature_id) { *presult = feature->value; return XML_STATUS_OK; } } return XML_STATUS_ERROR; } /* Having an element name longer than 1024 characters exercises some * of the pool allocation code in the parser that otherwise does not * get executed. The count at the end of the line is the number of * characters (bytes) in the element name by that point.x */ static const char *get_buffer_test_text = "\n" START_TEST(test_byte_info_at_error) { const char *text = PRE_ERROR_STR POST_ERROR_STR; if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), XML_TRUE) == XML_STATUS_OK) fail("Syntax error not faulted"); if (XML_GetCurrentByteCount(parser) != 0) fail("Error byte count incorrect"); if (XML_GetCurrentByteIndex(parser) != strlen(PRE_ERROR_STR)) fail("Error byte index incorrect"); } END_TEST #undef PRE_ERROR_STR #undef POST_ERROR_STR /* Test position information in handler */ typedef struct ByteTestData { int start_element_len; int cdata_len; int total_string_len; } ByteTestData; static void byte_character_handler(void *userData, const XML_Char *s, int len) { #ifdef XML_CONTEXT_BYTES int offset, size; const char *buffer; ByteTestData *data = (ByteTestData *)userData; buffer = XML_GetInputContext(parser, &offset, &size); if (buffer == NULL) fail("Failed to get context buffer"); if (offset != data->start_element_len) fail("Context offset in unexpected position"); if (len != data->cdata_len) fail("CDATA length reported incorrectly"); if (size != data->total_string_len) fail("Context size is not full buffer"); if (XML_GetCurrentByteIndex(parser) != offset) fail("Character byte index incorrect"); if (XML_GetCurrentByteCount(parser) != len) fail("Character byte count incorrect"); if (s != buffer + offset) fail("Buffer position incorrect"); #else (void)userData; (void)s; (void)len; #endif } #define START_ELEMENT "" #define CDATA_TEXT "Hello" #define END_ELEMENT "" START_TEST(test_byte_info_at_cdata) { const char *text = START_ELEMENT CDATA_TEXT END_ELEMENT; int offset, size; ByteTestData data; /* Check initial context is empty */ if (XML_GetInputContext(parser, &offset, &size) != NULL) fail("Unexpected context at start of parse"); data.start_element_len = strlen(START_ELEMENT); data.cdata_len = strlen(CDATA_TEXT); data.total_string_len = strlen(text); XML_SetCharacterDataHandler(parser, byte_character_handler); XML_SetUserData(parser, &data); if (XML_Parse(parser, text, strlen(text), XML_TRUE) != XML_STATUS_OK) xml_failure(parser); } END_TEST #undef START_ELEMENT #undef CDATA_TEXT #undef END_ELEMENT /* Test predefined entities are correctly recognised */ START_TEST(test_predefined_entities) { const char *text = "<>&"'"; const char *result = "<>&\"'"; CharData storage; XML_SetDefaultHandler(parser, accumulate_characters); /* run_character_check uses XML_SetCharacterDataHandler(), which * unfortunately heads off a code path that we need to exercise. */ CharData_Init(&storage); XML_SetUserData(parser, &storage); if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) xml_failure(parser); /* The default handler doesn't translate the entities */ CharData_CheckXMLChars(&storage, text); /* Now try again and check the translation */ XML_ParserReset(parser, NULL); run_character_check(text, result); } END_TEST /* Regression test that an invalid tag in an external parameter * reference in an external DTD is correctly faulted. * * Only a few specific tags are legal in DTDs ignoring comments and * processing instructions, all of which begin with an exclamation * mark. "" is not one of them, so the parser should raise an * error on encountering it. */ static int XMLCALL external_entity_param(XML_Parser parser, const XML_Char *context, const XML_Char *UNUSED_P(base), const XML_Char *systemId, const XML_Char *UNUSED_P(publicId)) { const char *text1 = "\n" "\n" "\n" "%e1;\n"; const char *text2 = "\n" "\n"; XML_Parser ext_parser; if (systemId == NULL) return XML_STATUS_OK; ext_parser = XML_ExternalEntityParserCreate(parser, context, NULL); if (ext_parser == NULL) fail("Could not create external entity parser"); if (!strcmp(systemId, "004-1.ent")) { if (_XML_Parse_SINGLE_BYTES(ext_parser, text1, strlen(text1), XML_TRUE) != XML_STATUS_ERROR) fail("Inner DTD with invalid tag not rejected"); if (XML_GetErrorCode(ext_parser) != XML_ERROR_EXTERNAL_ENTITY_HANDLING) xml_failure(ext_parser); } else if (!strcmp(systemId, "004-2.ent")) { if (_XML_Parse_SINGLE_BYTES(ext_parser, text2, strlen(text2), XML_TRUE) != XML_STATUS_ERROR) fail("Invalid tag in external param not rejected"); if (XML_GetErrorCode(ext_parser) != XML_ERROR_SYNTAX) xml_failure(ext_parser); } else { fail("Unknown system ID"); } XML_ParserFree(ext_parser); return XML_STATUS_ERROR; } START_TEST(test_invalid_tag_in_dtd) { const char *text = "\n" "\n"; XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS); XML_SetExternalEntityRefHandler(parser, external_entity_param); expect_failure(text, XML_ERROR_EXTERNAL_ENTITY_HANDLING, "Invalid tag IN DTD external param not rejected"); } 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", NULL, XML_ERROR_INVALID_TOKEN }, { /* FIrst two bytes of a three-byte char */ "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 /* Test recursive parsing */ static int XMLCALL external_entity_valuer(XML_Parser parser, const XML_Char *context, const XML_Char *UNUSED_P(base), const XML_Char *systemId, const XML_Char *UNUSED_P(publicId)) { const char *text1 = "\n" "\n" "\n" "%e1;\n"; XML_Parser ext_parser; if (systemId == NULL) return XML_STATUS_OK; ext_parser = XML_ExternalEntityParserCreate(parser, context, NULL); if (ext_parser == NULL) fail("Could not create external entity parser"); if (!strcmp(systemId, "004-1.ent")) { if (_XML_Parse_SINGLE_BYTES(ext_parser, text1, strlen(text1), XML_TRUE) == XML_STATUS_ERROR) xml_failure(ext_parser); } else if (!strcmp(systemId, "004-2.ent")) { ExtFaults *fault = (ExtFaults *)XML_GetUserData(parser); enum XML_Status status; enum XML_Error error; status = _XML_Parse_SINGLE_BYTES(ext_parser, fault->parse_text, strlen(fault->parse_text), XML_TRUE); if (fault->error == XML_ERROR_NONE) { if (status == XML_STATUS_ERROR) xml_failure(ext_parser); } else { if (status != XML_STATUS_ERROR) fail(fault->fail_text); error = XML_GetErrorCode(ext_parser); if (error != fault->error && (fault->error != XML_ERROR_XML_DECL || error != XML_ERROR_TEXT_DECL)) xml_failure(ext_parser); } } XML_ParserFree(ext_parser); return XML_STATUS_OK; } START_TEST(test_external_entity_values) { const char *text = "\n" "\n"; ExtFaults data_004_2[] = { { "", NULL, NULL, XML_ERROR_NONE }, { "", "Invalid token not faulted", NULL, XML_ERROR_INVALID_TOKEN }, { "'wombat", "Unterminated string not faulted", NULL, XML_ERROR_UNCLOSED_TOKEN }, { "\xe2\x82", "Partial UTF-8 character not faulted", NULL, XML_ERROR_PARTIAL_CHAR }, { "\n", NULL, NULL, XML_ERROR_NONE }, { "", "Malformed XML declaration not faulted", NULL, XML_ERROR_XML_DECL }, { /* UTF-8 BOM */ "\xEF\xBB\xBF", NULL, NULL, XML_ERROR_NONE }, { "\n$", "Invalid token after text declaration not faulted", NULL, XML_ERROR_INVALID_TOKEN }, { "\n'wombat", "Unterminated string after text decl not faulted", NULL, XML_ERROR_UNCLOSED_TOKEN }, { "\n\xe2\x82", "Partial UTF-8 character after text decl not faulted", NULL, XML_ERROR_PARTIAL_CHAR }, { "%e1;", "Recursive parameter entity not faulted", NULL, XML_ERROR_RECURSIVE_ENTITY_REF }, { NULL, NULL, NULL, XML_ERROR_NONE } }; int i; for (i = 0; data_004_2[i].parse_text != NULL; i++) { XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS); XML_SetExternalEntityRefHandler(parser, external_entity_valuer); XML_SetUserData(parser, &data_004_2[i]); if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) xml_failure(parser); XML_ParserReset(parser, NULL); } } END_TEST /* Test the recursive parse interacts with a not standalone handler */ static int XMLCALL external_entity_not_standalone(XML_Parser parser, const XML_Char *context, const XML_Char *UNUSED_P(base), const XML_Char *systemId, const XML_Char *UNUSED_P(publicId)) { const char *text1 = "\n" "\n" "%e1;\n"; const char *text2 = ""; XML_Parser ext_parser; if (systemId == NULL) return XML_STATUS_OK; ext_parser = XML_ExternalEntityParserCreate(parser, context, NULL); if (ext_parser == NULL) fail("Could not create external entity parser"); if (!strcmp(systemId, "foo")) { XML_SetNotStandaloneHandler(ext_parser, reject_not_standalone_handler); if (_XML_Parse_SINGLE_BYTES(ext_parser, text1, strlen(text1), XML_TRUE) != XML_STATUS_ERROR) fail("Expected not standalone rejection"); if (XML_GetErrorCode(ext_parser) != XML_ERROR_NOT_STANDALONE) xml_failure(ext_parser); XML_SetNotStandaloneHandler(ext_parser, NULL); return XML_STATUS_ERROR; } else if (!strcmp(systemId, "bar")) { if (_XML_Parse_SINGLE_BYTES(ext_parser, text2, strlen(text2), XML_TRUE) == XML_STATUS_ERROR) xml_failure(ext_parser); } return XML_STATUS_OK; } START_TEST(test_ext_entity_not_standalone) { const char *text = "\n" ""; XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS); XML_SetExternalEntityRefHandler(parser, external_entity_not_standalone); expect_failure(text, XML_ERROR_EXTERNAL_ENTITY_HANDLING, "Standalone rejection not caught"); } END_TEST static int XMLCALL external_entity_value_aborter(XML_Parser parser, const XML_Char *context, const XML_Char *UNUSED_P(base), const XML_Char *systemId, const XML_Char *UNUSED_P(publicId)) { const char *text1 = "\n" "\n" "\n" "%e1;\n"; const char *text2 = ""; XML_Parser ext_parser; if (systemId == NULL) return XML_STATUS_OK; ext_parser = XML_ExternalEntityParserCreate(parser, context, NULL); if (ext_parser == NULL) fail("Could not create external entity parser"); if (!strcmp(systemId, "004-1.ent")) { if (_XML_Parse_SINGLE_BYTES(ext_parser, text1, strlen(text1), XML_TRUE) == XML_STATUS_ERROR) xml_failure(ext_parser); } if (!strcmp(systemId, "004-2.ent")) { XML_SetXmlDeclHandler(ext_parser, entity_suspending_xdecl_handler); XML_SetUserData(ext_parser, ext_parser); if (_XML_Parse_SINGLE_BYTES(ext_parser, text2, strlen(text2), XML_TRUE) != XML_STATUS_ERROR) fail("Aborted parse not faulted"); if (XML_GetErrorCode(ext_parser) != XML_ERROR_ABORTED) xml_failure(ext_parser); } XML_ParserFree(ext_parser); return XML_STATUS_OK; } START_TEST(test_ext_entity_value_abort) { const char *text = "\n" "\n"; XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS); XML_SetExternalEntityRefHandler(parser, external_entity_value_aborter); resumable = XML_FALSE; if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) xml_failure(parser); } END_TEST START_TEST(test_bad_public_doctype) { const char *text = "\n" "\n" ""; /* Setting a handler provokes a particular code path */ XML_SetDoctypeDeclHandler(parser, dummy_start_doctype_handler, dummy_end_doctype_handler); expect_failure(text, XML_ERROR_PUBLICID, "Bad Public ID not failed"); } END_TEST /* Test based on ibm/valid/P32/ibm32v04.xml */ START_TEST(test_attribute_enum_value) { const char *text = "\n" "\n" "This is a \n \n\nyellow tiger"; ExtTest dtd_data = { "\n" "\n" "", NULL, NULL }; XML_SetExternalEntityRefHandler(parser, external_entity_loader); XML_SetUserData(parser, &dtd_data); XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS); /* An attribute list handler provokes a different code path */ XML_SetAttlistDeclHandler(parser, dummy_attlist_decl_handler); run_ext_character_check(text, &dtd_data, "This is a \n \n\nyellow tiger"); } END_TEST /* Slightly bizarrely, the library seems to silently ignore entity * definitions for predefined entities, even when they are wrong. The * language of the XML 1.0 spec is somewhat unhelpful as to what ought * to happen, so this is currently treated as acceptable. */ START_TEST(test_predefined_entity_redefinition) { const char *text = "\n" "]>\n" "'"; run_character_check(text, "'"); } END_TEST /* Test that the parser stops processing the DTD after an unresolved * parameter entity is encountered. */ START_TEST(test_dtd_stop_processing) { const char *text = "\n" "]>"; XML_SetEntityDeclHandler(parser, dummy_entity_decl_handler); dummy_handler_flags = 0; if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) xml_failure(parser); if (dummy_handler_flags != 0) fail("DTD processing still going after undefined PE"); } END_TEST /* Test public notations with no system ID */ START_TEST(test_public_notation_no_sysid) { const char *text = "\n" "\n" "]>\n"; dummy_handler_flags = 0; XML_SetNotationDeclHandler(parser, dummy_notation_decl_handler); if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) xml_failure(parser); if (dummy_handler_flags != DUMMY_NOTATION_DECL_HANDLER_FLAG) fail("Notation declaration handler not called"); } END_TEST static void XMLCALL record_element_start_handler(void *userData, const XML_Char *name, const XML_Char **UNUSED_P(atts)) { CharData_AppendString((CharData *)userData, name); } START_TEST(test_nested_groups) { const char *text = "\n" "" "]>\n" ""; CharData storage; CharData_Init(&storage); XML_SetElementDeclHandler(parser, dummy_element_decl_handler); XML_SetStartElementHandler(parser, record_element_start_handler); XML_SetUserData(parser, &storage); dummy_handler_flags = 0; if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) xml_failure(parser); CharData_CheckString(&storage, "doce"); if (dummy_handler_flags != DUMMY_ELEMENT_DECL_HANDLER_FLAG) fail("Element handler not fired"); } END_TEST START_TEST(test_group_choice) { const char *text = "\n" "\n" "\n" "\n" "]>\n" "\n" "\n" "This is a foo\n" "\n" "\n"; XML_SetElementDeclHandler(parser, dummy_element_decl_handler); dummy_handler_flags = 0; if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) xml_failure(parser); if (dummy_handler_flags != DUMMY_ELEMENT_DECL_HANDLER_FLAG) fail("Element handler flag not raised"); } END_TEST static int XMLCALL external_entity_public(XML_Parser parser, const XML_Char *context, const XML_Char *UNUSED_P(base), const XML_Char *systemId, const XML_Char *publicId) { const char *text1 = (const char *)XML_GetUserData(parser); const char *text2 = ""; const char *text = NULL; XML_Parser ext_parser; int parse_res; ext_parser = XML_ExternalEntityParserCreate(parser, context, NULL); if (ext_parser == NULL) return XML_STATUS_ERROR; if (systemId != NULL && !strcmp(systemId, "http://example.org/")) { text = text1; } else if (publicId != NULL && !strcmp(publicId, "foo")) { text = text2; } else fail("Unexpected parameters to external entity parser"); parse_res = _XML_Parse_SINGLE_BYTES(ext_parser, text, strlen(text), XML_TRUE); XML_ParserFree(ext_parser); return parse_res; } START_TEST(test_standalone_parameter_entity) { const char *text = "\n" "'>\n" "%entity;\n" "]>\n" ""; char dtd_data[] = "\n"; XML_SetUserData(parser, dtd_data); XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS); XML_SetExternalEntityRefHandler(parser, external_entity_public); if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) xml_failure(parser); } END_TEST /* Test skipping of parameter entity in an external DTD */ /* Derived from ibm/invalid/P69/ibm69i01.xml */ START_TEST(test_skipped_parameter_entity) { const char *text = "\n" "\n" "]>\n" ""; ExtTest dtd_data = { "%pe2;", NULL, NULL }; XML_SetExternalEntityRefHandler(parser, external_entity_loader); XML_SetUserData(parser, &dtd_data); XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS); XML_SetSkippedEntityHandler(parser, dummy_skip_handler); dummy_handler_flags = 0; if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) xml_failure(parser); if (dummy_handler_flags != DUMMY_SKIP_HANDLER_FLAG) fail("Skip handler not executed"); } END_TEST /* Test recursive parameter entity definition rejected in external DTD */ START_TEST(test_recursive_external_parameter_entity) { const char *text = "\n" "\n" "]>\n" ""; ExtFaults dtd_data = { "\n%pe2;", "Recursive external parameter entity not faulted", NULL, XML_ERROR_RECURSIVE_ENTITY_REF }; XML_SetExternalEntityRefHandler(parser, external_entity_faulter); XML_SetUserData(parser, &dtd_data); XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS); expect_failure(text, XML_ERROR_EXTERNAL_ENTITY_HANDLING, "Recursive external parameter not spotted"); } END_TEST /* Test undefined parameter entity in external entity handler */ static int XMLCALL external_entity_devaluer(XML_Parser parser, const XML_Char *context, const XML_Char *UNUSED_P(base), const XML_Char *systemId, const XML_Char *UNUSED_P(publicId)) { const char *text = "\n" "\n" "%e1;\n"; XML_Parser ext_parser; int clear_handler = (intptr_t)XML_GetUserData(parser); if (systemId == NULL || !strcmp(systemId, "bar")) return XML_STATUS_OK; if (strcmp(systemId, "foo")) fail("Unexpected system ID"); ext_parser = XML_ExternalEntityParserCreate(parser, context, NULL); if (ext_parser == NULL) fail("Could note create external entity parser"); if (clear_handler) XML_SetExternalEntityRefHandler(ext_parser, NULL); if (_XML_Parse_SINGLE_BYTES(ext_parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) xml_failure(ext_parser); return XML_STATUS_OK; } START_TEST(test_undefined_ext_entity_in_external_dtd) { const char *text = "\n" "\n"; XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS); XML_SetExternalEntityRefHandler(parser, external_entity_devaluer); XML_SetUserData(parser, (void *)(intptr_t)XML_FALSE); if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) xml_failure(parser); /* Now repeat without the external entity ref handler invoking * another copy of itself. */ XML_ParserReset(parser, NULL); XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS); XML_SetExternalEntityRefHandler(parser, external_entity_devaluer); XML_SetUserData(parser, (void *)(intptr_t)XML_TRUE); if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) xml_failure(parser); } END_TEST static void XMLCALL aborting_xdecl_handler(void *UNUSED_P(userData), const XML_Char *UNUSED_P(version), const XML_Char *UNUSED_P(encoding), int UNUSED_P(standalone)) { XML_StopParser(parser, resumable); XML_SetXmlDeclHandler(parser, NULL); } /* Test suspending the parse on receiving an XML declaration works */ START_TEST(test_suspend_xdecl) { const char *text = long_character_data_text; XML_SetXmlDeclHandler(parser, aborting_xdecl_handler); resumable = XML_TRUE; if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), XML_TRUE) != XML_STATUS_SUSPENDED) xml_failure(parser); if (XML_GetErrorCode(parser) != XML_ERROR_NONE) xml_failure(parser); /* Attempt to start a new parse while suspended */ if (XML_Parse(parser, text, strlen(text), XML_TRUE) != XML_STATUS_ERROR) fail("Attempt to parse while suspended not faulted"); if (XML_GetErrorCode(parser) != XML_ERROR_SUSPENDED) fail("Suspended parse not faulted with correct error"); } END_TEST /* Test aborting the parse in an epilog works */ static void XMLCALL selective_aborting_default_handler(void *userData, const XML_Char *s, int len) { const char *match = (const char *)userData; if (match == NULL || (strlen(match) == (unsigned)len && !strncmp(match, s, len))) { XML_StopParser(parser, resumable); XML_SetDefaultHandler(parser, NULL); } } START_TEST(test_abort_epilog) { const char *text = "\n\r\n"; char match[] = "\r"; XML_SetDefaultHandler(parser, selective_aborting_default_handler); XML_SetUserData(parser, match); resumable = XML_FALSE; if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), XML_TRUE) != XML_STATUS_ERROR) fail("Abort not triggered"); if (XML_GetErrorCode(parser) != XML_ERROR_ABORTED) xml_failure(parser); } END_TEST /* Test a different code path for abort in the epilog */ START_TEST(test_abort_epilog_2) { const char *text = "\n"; char match[] = "\n"; XML_SetDefaultHandler(parser, selective_aborting_default_handler); XML_SetUserData(parser, match); resumable = XML_FALSE; expect_failure(text, XML_ERROR_ABORTED, "Abort not triggered"); } END_TEST /* Test suspension from the epilog */ START_TEST(test_suspend_epilog) { const char *text = "\n"; char match[] = "\n"; XML_SetDefaultHandler(parser, selective_aborting_default_handler); XML_SetUserData(parser, match); resumable = XML_TRUE; if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), XML_TRUE) != XML_STATUS_SUSPENDED) xml_failure(parser); } END_TEST START_TEST(test_unfinished_epilog) { const char *text = "<"; expect_failure(text, XML_ERROR_UNCLOSED_TOKEN, "Incomplete epilog entry not faulted"); } END_TEST START_TEST(test_partial_char_in_epilog) { const char *text = "\xe2\x82"; /* First check that no fault is raised if the parse is not finished */ if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), XML_FALSE) == XML_STATUS_ERROR) xml_failure(parser); /* Now check that it is faulted once we finish */ if (XML_ParseBuffer(parser, 0, XML_TRUE) != XML_STATUS_ERROR) fail("Partial character in epilog not faulted"); if (XML_GetErrorCode(parser) != XML_ERROR_PARTIAL_CHAR) xml_failure(parser); } END_TEST START_TEST(test_hash_collision) { /* For full coverage of the lookup routine, we need to ensure a * hash collision even though we can only tell that we have one * through breakpoint debugging or coverage statistics. The * following will cause a hash collision on machines with a 64-bit * long type; others will have to experiment. The full coverage * tests invoked from qa.sh usually provide a hash collision, but * not always. This is an attempt to provide insurance. */ #define COLLIDING_HASH_SALT 0xffffffffff99fc90 const char * text = "\n" "\n" "This is a foo\n" "\n" "\n" "\n" "This triggers the table growth and collides with b2\n" "\n"; XML_SetHashSalt(parser, COLLIDING_HASH_SALT); if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) xml_failure(parser); } END_TEST #undef COLLIDING_HASH_SALT /* Test resuming a parse suspended in entity substitution */ static void XMLCALL start_element_suspender(void *UNUSED_P(userData), const XML_Char *name, const XML_Char **UNUSED_P(atts)) { if (!strcmp(name, "suspend")) XML_StopParser(parser, XML_TRUE); } START_TEST(test_suspend_resume_internal_entity) { const char *text = "HiHo'>\n" "]>\n" "&foo;\n"; const char *expected1 = "Hi"; const char *expected2 = "HiHo"; CharData storage; CharData_Init(&storage); XML_SetStartElementHandler(parser, start_element_suspender); XML_SetCharacterDataHandler(parser, accumulate_characters); XML_SetUserData(parser, &storage); if (XML_Parse(parser, text, strlen(text), XML_TRUE) != XML_STATUS_SUSPENDED) xml_failure(parser); CharData_CheckXMLChars(&storage, ""); if (XML_ResumeParser(parser) != XML_STATUS_SUSPENDED) xml_failure(parser); CharData_CheckXMLChars(&storage, expected1); if (XML_ResumeParser(parser) != XML_STATUS_OK) xml_failure(parser); CharData_CheckXMLChars(&storage, expected2); } END_TEST /* Test syntax error is caught at parse resumption */ START_TEST(test_resume_entity_with_syntax_error) { const char *text = "Hi'>\n" "]>\n" "&foo;\n"; XML_SetStartElementHandler(parser, start_element_suspender); if (XML_Parse(parser, text, strlen(text), XML_TRUE) != XML_STATUS_SUSPENDED) xml_failure(parser); if (XML_ResumeParser(parser) != XML_STATUS_ERROR) fail("Syntax error in entity not faulted"); if (XML_GetErrorCode(parser) != XML_ERROR_TAG_MISMATCH) xml_failure(parser); } END_TEST /* Test suspending and resuming in a parameter entity substitution */ static void XMLCALL element_decl_suspender(void *UNUSED_P(userData), const XML_Char *UNUSED_P(name), XML_Content *model) { XML_StopParser(parser, XML_TRUE); XML_FreeContentModel(parser, model); } START_TEST(test_suspend_resume_parameter_entity) { const char *text = "'>\n" "%foo;\n" "]>\n" "Hello, world"; const char *expected = "Hello, world"; CharData storage; CharData_Init(&storage); XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS); XML_SetElementDeclHandler(parser, element_decl_suspender); XML_SetCharacterDataHandler(parser, accumulate_characters); XML_SetUserData(parser, &storage); if (XML_Parse(parser, text, strlen(text), XML_TRUE) != XML_STATUS_SUSPENDED) xml_failure(parser); CharData_CheckXMLChars(&storage, ""); if (XML_ResumeParser(parser) != XML_STATUS_OK) xml_failure(parser); CharData_CheckXMLChars(&storage, expected); } END_TEST /* Test attempting to use parser after an error is faulted */ START_TEST(test_restart_on_error) { const char *text = "<$doc>"; if (XML_Parse(parser, text, strlen(text), XML_TRUE) != XML_STATUS_ERROR) fail("Invalid tag name not faulted"); if (XML_GetErrorCode(parser) != XML_ERROR_INVALID_TOKEN) xml_failure(parser); if (XML_Parse(parser, NULL, 0, XML_TRUE) != XML_STATUS_ERROR) fail("Restarting invalid parse not faulted"); if (XML_GetErrorCode(parser) != XML_ERROR_INVALID_TOKEN) xml_failure(parser); } END_TEST /* Test that angle brackets in an attribute default value are faulted */ START_TEST(test_reject_lt_in_attribute_value) { const char *text = "'>]>\n" ""; expect_failure(text, XML_ERROR_INVALID_TOKEN, "Bad attribute default not faulted"); } END_TEST START_TEST(test_reject_unfinished_param_in_att_value) { const char *text = "]>\n" ""; expect_failure(text, XML_ERROR_INVALID_TOKEN, "Bad attribute default not faulted"); } END_TEST START_TEST(test_trailing_cr_in_att_value) { const char *text = ""; if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) xml_failure(parser); } END_TEST /* Try parsing a general entity within a parameter entity in a * standalone internal DTD. Covers a corner case in the parser. */ START_TEST(test_standalone_internal_entity) { const char *text = "\n" "\n" " '>\n" " \n" " %pe;\n" "]>\n" ""; XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS); if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) xml_failure(parser); } END_TEST /* Test that a reference to an unknown external entity is skipped */ START_TEST(test_skipped_external_entity) { const char *text = "\n" "\n"; ExtTest test_data = { "\n" "\n", NULL, NULL }; XML_SetUserData(parser, &test_data); XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS); XML_SetExternalEntityRefHandler(parser, external_entity_loader); if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) xml_failure(parser); } END_TEST /* Test a different form of unknown external entity */ typedef struct ext_hdlr_data { const char *parse_text; XML_ExternalEntityRefHandler handler; } ExtHdlrData; static int XMLCALL external_entity_oneshot_loader(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)) { ExtHdlrData *test_data = (ExtHdlrData *)XML_GetUserData(parser); XML_Parser ext_parser; ext_parser = XML_ExternalEntityParserCreate(parser, context, NULL); if (ext_parser == NULL) fail("Could not create external entity parser."); /* Use the requested entity parser for further externals */ XML_SetExternalEntityRefHandler(ext_parser, test_data->handler); if ( _XML_Parse_SINGLE_BYTES(ext_parser, test_data->parse_text, strlen(test_data->parse_text), XML_TRUE) == XML_STATUS_ERROR) { xml_failure(ext_parser); } return XML_STATUS_OK; } START_TEST(test_skipped_null_loaded_ext_entity) { const char *text = "\n" ""; ExtHdlrData test_data = { "\n" "\n" "%pe2;\n", external_entity_null_loader }; XML_SetUserData(parser, &test_data); XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS); XML_SetExternalEntityRefHandler(parser, external_entity_oneshot_loader); if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) xml_failure(parser); } END_TEST START_TEST(test_skipped_unloaded_ext_entity) { const char *text = "\n" ""; ExtHdlrData test_data = { "\n" "\n" "%pe2;\n", NULL }; XML_SetUserData(parser, &test_data); XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS); XML_SetExternalEntityRefHandler(parser, external_entity_oneshot_loader); if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) xml_failure(parser); } END_TEST /* Test that a parameter entity value ending with a carriage return * has it translated internally into a newline. */ START_TEST(test_param_entity_with_trailing_cr) { #define PARAM_ENTITY_NAME "pe" #define PARAM_ENTITY_CORE_VALUE "" const char *text = "\n" ""; ExtTest test_data = { "\n" "%" PARAM_ENTITY_NAME ";\n", NULL, NULL }; XML_SetUserData(parser, &test_data); XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS); XML_SetExternalEntityRefHandler(parser, external_entity_loader); XML_SetEntityDeclHandler(parser, param_entity_match_handler); entity_name_to_match = PARAM_ENTITY_NAME; entity_value_to_match = PARAM_ENTITY_CORE_VALUE "\n"; entity_match_flag = ENTITY_MATCH_NOT_FOUND; if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) xml_failure(parser); if (entity_match_flag == ENTITY_MATCH_FAIL) fail("Parameter entity CR->NEWLINE conversion failed"); else if (entity_match_flag == ENTITY_MATCH_NOT_FOUND) fail("Parameter entity not parsed"); } #undef PARAM_ENTITY_NAME #undef PARAM_ENTITY_CORE_VALUE END_TEST START_TEST(test_invalid_character_entity) { const char *text = "\n" "]>\n" "&entity;"; expect_failure(text, XML_ERROR_BAD_CHAR_REF, "Out of range character reference not faulted"); } END_TEST /* Test that processing instructions are picked up by a default handler */ static void XMLCALL default_matching_handler(void *userData, const XML_Char *s, int len) { const char *target = (const char *)userData; if ((int)strlen(target) == len && !strncmp(target, s, len)) dummy_handler_flags |= DUMMY_DEFAULT_HANDLER_FLAG; } START_TEST(test_pi_handled_in_default) { #define PI_TEXT "" const char *text = PI_TEXT "\n"; char pi_text[] = PI_TEXT; XML_SetDefaultHandler(parser, default_matching_handler); XML_SetUserData(parser, pi_text); dummy_handler_flags = 0; if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), XML_TRUE)== XML_STATUS_ERROR) xml_failure(parser); if (dummy_handler_flags != DUMMY_DEFAULT_HANDLER_FLAG) fail("Processing instruction not picked up by default handler"); } #undef PI_TEXT END_TEST /* Test that comments are picked up by a default handler */ START_TEST(test_comment_handled_in_default) { #define COMMENT_TEXT "" const char *text = COMMENT_TEXT "\n"; char comment_text[] = COMMENT_TEXT; XML_SetDefaultHandler(parser, default_matching_handler); XML_SetUserData(parser, comment_text); dummy_handler_flags = 0; if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) xml_failure(parser); if (dummy_handler_flags != DUMMY_DEFAULT_HANDLER_FLAG) fail("Comment not picked up by default handler"); } #undef COMMENT_TEXT END_TEST /* * Namespaces tests. */ static void namespace_setup(void) { parser = XML_ParserCreateNS(NULL, ' '); if (parser == NULL) fail("Parser not created."); } static void namespace_teardown(void) { basic_teardown(); } /* Check that an element name and attribute name match the expected values. The expected values are passed as an array reference of string pointers provided as the userData argument; the first is the expected element name, and the second is the expected attribute name. */ static int triplet_start_flag = XML_FALSE; static int triplet_end_flag = XML_FALSE; static void XMLCALL triplet_start_checker(void *userData, const XML_Char *name, const XML_Char **atts) { char **elemstr = (char **)userData; char buffer[1024]; if (strcmp(elemstr[0], name) != 0) { sprintf(buffer, "unexpected start string: '%s'", name); fail(buffer); } if (strcmp(elemstr[1], atts[0]) != 0) { sprintf(buffer, "unexpected attribute string: '%s'", atts[0]); fail(buffer); } triplet_start_flag = XML_TRUE; } /* Check that the element name passed to the end-element handler matches the expected value. The expected value is passed as the first element in an array of strings passed as the userData argument. */ static void XMLCALL triplet_end_checker(void *userData, const XML_Char *name) { char **elemstr = (char **)userData; if (strcmp(elemstr[0], name) != 0) { char buffer[1024]; sprintf(buffer, "unexpected end string: '%s'", name); fail(buffer); } triplet_end_flag = XML_TRUE; } START_TEST(test_return_ns_triplet) { const char *text = ""; const char *epilog = ""; const char *elemstr[] = { "http://example.org/ e foo", "http://example.org/ a bar" }; XML_SetReturnNSTriplet(parser, XML_TRUE); XML_SetUserData(parser, elemstr); XML_SetElementHandler(parser, triplet_start_checker, triplet_end_checker); XML_SetNamespaceDeclHandler(parser, dummy_start_namespace_decl_handler, dummy_end_namespace_decl_handler); triplet_start_flag = XML_FALSE; triplet_end_flag = XML_FALSE; dummy_handler_flags = 0; if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), XML_FALSE) == XML_STATUS_ERROR) xml_failure(parser); if (!triplet_start_flag) fail("triplet_start_checker not invoked"); /* Check that unsetting "return triplets" fails while still parsing */ XML_SetReturnNSTriplet(parser, XML_FALSE); if (_XML_Parse_SINGLE_BYTES(parser, epilog, strlen(epilog), XML_TRUE) == XML_STATUS_ERROR) xml_failure(parser); if (!triplet_end_flag) fail("triplet_end_checker not invoked"); if (dummy_handler_flags != (DUMMY_START_NS_DECL_HANDLER_FLAG | DUMMY_END_NS_DECL_HANDLER_FLAG)) fail("Namespace handlers not called"); } END_TEST static void XMLCALL overwrite_start_checker(void *userData, const XML_Char *name, const XML_Char **atts) { CharData *storage = (CharData *) userData; CharData_AppendString(storage, "start "); CharData_AppendXMLChars(storage, name, -1); while (*atts != NULL) { CharData_AppendString(storage, "\nattribute "); CharData_AppendXMLChars(storage, *atts, -1); atts += 2; } CharData_AppendString(storage, "\n"); } static void XMLCALL overwrite_end_checker(void *userData, const XML_Char *name) { CharData *storage = (CharData *) userData; CharData_AppendString(storage, "end "); CharData_AppendXMLChars(storage, name, -1); CharData_AppendString(storage, "\n"); } static void run_ns_tagname_overwrite_test(const char *text, const char *result) { CharData storage; CharData_Init(&storage); XML_SetUserData(parser, &storage); XML_SetElementHandler(parser, overwrite_start_checker, overwrite_end_checker); if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) xml_failure(parser); CharData_CheckString(&storage, result); } /* Regression test for SF bug #566334. */ START_TEST(test_ns_tagname_overwrite) { const char *text = "\n" " \n" " \n" ""; const char *result = "start http://example.org/ e\n" "start http://example.org/ f\n" "attribute http://example.org/ attr\n" "end http://example.org/ f\n" "start http://example.org/ g\n" "attribute http://example.org/ attr2\n" "end http://example.org/ g\n" "end http://example.org/ e\n"; run_ns_tagname_overwrite_test(text, result); } END_TEST /* Regression test for SF bug #566334. */ START_TEST(test_ns_tagname_overwrite_triplet) { const char *text = "\n" " \n" " \n" ""; const char *result = "start http://example.org/ e n\n" "start http://example.org/ f n\n" "attribute http://example.org/ attr n\n" "end http://example.org/ f n\n" "start http://example.org/ g n\n" "attribute http://example.org/ attr2 n\n" "end http://example.org/ g n\n" "end http://example.org/ e n\n"; XML_SetReturnNSTriplet(parser, XML_TRUE); run_ns_tagname_overwrite_test(text, result); } END_TEST /* Regression test for SF bug #620343. */ static void XMLCALL start_element_fail(void *UNUSED_P(userData), const XML_Char *UNUSED_P(name), const XML_Char **UNUSED_P(atts)) { /* We should never get here. */ fail("should never reach start_element_fail()"); } static void XMLCALL start_ns_clearing_start_element(void *userData, const XML_Char *UNUSED_P(prefix), const XML_Char *UNUSED_P(uri)) { XML_SetStartElementHandler((XML_Parser) userData, NULL); } START_TEST(test_start_ns_clears_start_element) { /* This needs to use separate start/end tags; using the empty tag syntax doesn't cause the problematic path through Expat to be taken. */ const char *text = ""; XML_SetStartElementHandler(parser, start_element_fail); XML_SetStartNamespaceDeclHandler(parser, start_ns_clearing_start_element); XML_SetEndNamespaceDeclHandler(parser, dummy_end_namespace_decl_handler); XML_UseParserAsHandlerArg(parser); if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) xml_failure(parser); } END_TEST /* Regression test for SF bug #616863. */ static int XMLCALL external_entity_handler(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)) { intptr_t callno = 1 + (intptr_t)XML_GetUserData(parser); const char *text; XML_Parser p2; if (callno == 1) text = ("\n" "\n" "\n"); else text = ("" ""); XML_SetUserData(parser, (void *) callno); p2 = XML_ExternalEntityParserCreate(parser, context, NULL); if (_XML_Parse_SINGLE_BYTES(p2, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) { xml_failure(p2); return XML_STATUS_ERROR; } XML_ParserFree(p2); return XML_STATUS_OK; } START_TEST(test_default_ns_from_ext_subset_and_ext_ge) { const char *text = "\n" "\n" "]>\n" "\n" "&en;\n" ""; XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS); XML_SetExternalEntityRefHandler(parser, external_entity_handler); /* We actually need to set this handler to tickle this bug. */ XML_SetStartElementHandler(parser, dummy_start_element); XML_SetUserData(parser, NULL); if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) xml_failure(parser); } END_TEST /* Regression test #1 for SF bug #673791. */ START_TEST(test_ns_prefix_with_empty_uri_1) { const char *text = "\n" " \n" ""; expect_failure(text, XML_ERROR_UNDECLARING_PREFIX, "Did not report re-setting namespace" " URI with prefix to ''."); } END_TEST /* Regression test #2 for SF bug #673791. */ START_TEST(test_ns_prefix_with_empty_uri_2) { const char *text = "\n" ""; expect_failure(text, XML_ERROR_UNDECLARING_PREFIX, "Did not report setting namespace URI with prefix to ''."); } END_TEST /* Regression test #3 for SF bug #673791. */ START_TEST(test_ns_prefix_with_empty_uri_3) { const char *text = "\n" " \n" "]>\n" ""; expect_failure(text, XML_ERROR_UNDECLARING_PREFIX, "Didn't report attr default setting NS w/ prefix to ''."); } END_TEST /* Regression test #4 for SF bug #673791. */ START_TEST(test_ns_prefix_with_empty_uri_4) { const char *text = "\n" " \n" "]>\n" ""; /* Packaged info expected by the end element handler; the weird structuring lets us re-use the triplet_end_checker() function also used for another test. */ const char *elemstr[] = { "http://example.org/ doc prefix" }; XML_SetReturnNSTriplet(parser, XML_TRUE); XML_SetUserData(parser, elemstr); XML_SetEndElementHandler(parser, triplet_end_checker); if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) xml_failure(parser); } 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); } END_TEST /* Regression test for SF bug #692964: two prefixes for one namespace. */ START_TEST(test_ns_duplicate_attrs_diff_prefixes) { const char *text = ""; expect_failure(text, XML_ERROR_DUPLICATE_ATTRIBUTE, "did not report multiple attributes with same URI+name"); } 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) { const char *text = ""; expect_failure(text, XML_ERROR_UNBOUND_PREFIX, "did not report unbound prefix on attribute"); } END_TEST /* Regression test for SF bug #695401: unbound prefix. */ START_TEST(test_ns_unbound_prefix_on_element) { const char *text = ""; expect_failure(text, XML_ERROR_UNBOUND_PREFIX, "did not report unbound prefix on element"); } END_TEST /* Test that the parsing status is correctly reset by XML_ParserReset(). * We usE test_return_ns_triplet() for our example parse to improve * coverage of tidying up code executed. */ START_TEST(test_ns_parser_reset) { XML_ParsingStatus status; XML_GetParsingStatus(parser, &status); if (status.parsing != XML_INITIALIZED) fail("parsing status doesn't start INITIALIZED"); test_return_ns_triplet(); XML_GetParsingStatus(parser, &status); if (status.parsing != XML_FINISHED) fail("parsing status doesn't end FINISHED"); XML_ParserReset(parser, NULL); XML_GetParsingStatus(parser, &status); if (status.parsing != XML_INITIALIZED) fail("parsing status doesn't reset to INITIALIZED"); } END_TEST /* Test that long element names with namespaces are handled correctly */ START_TEST(test_ns_long_element) { const char *text = "" ""; const char *elemstr[] = { "http://example.org/" " thisisalongenoughelementnametotriggerareallocation foo", "http://example.org/ a bar" }; 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) xml_failure(parser); } 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) #define REALLOC_ALWAYS_SUCCEED (-1) static int allocation_count = ALLOC_ALWAYS_SUCCEED; static int reallocation_count = REALLOC_ALWAYS_SUCCEED; /* Crocked allocator for allocation failure tests */ static void *duff_allocator(size_t size) { if (allocation_count == 0) return NULL; if (allocation_count != ALLOC_ALWAYS_SUCCEED) allocation_count--; return malloc(size); } /* Crocked reallocator for allocation failure tests */ static void *duff_reallocator(void *ptr, size_t size) { if (reallocation_count == 0) return NULL; if (reallocation_count != REALLOC_ALWAYS_SUCCEED) reallocation_count--; return realloc(ptr, size); } /* Test that a failure to allocate the parser structure fails gracefully */ START_TEST(test_misc_alloc_create_parser) { XML_Memory_Handling_Suite memsuite = { duff_allocator, realloc, free }; unsigned int i; /* Something this simple shouldn't need more than 10 allocations */ for (i = 0; i < 10; i++) { allocation_count = i; parser = XML_ParserCreate_MM(NULL, &memsuite, NULL); if (parser != NULL) break; } if (i == 0) fail("Parser unexpectedly ignored failing allocator"); else if (i == 10) fail("Parser not created with allocation count 10"); } END_TEST /* Test memory allocation failures for a parser with an encoding */ START_TEST(test_misc_alloc_create_parser_with_encoding) { XML_Memory_Handling_Suite memsuite = { duff_allocator, realloc, free }; unsigned int i; /* Try several levels of allocation */ for (i = 0; i < 10; i++) { allocation_count = i; parser = XML_ParserCreate_MM("us-ascii", &memsuite, NULL); if (parser != NULL) break; } if (i == 0) fail("Parser ignored failing allocator"); else if (i == 10) fail("Parser not created with allocation count 10"); } END_TEST /* Test that freeing a NULL parser doesn't cause an explosion. * (Not actually tested anywhere else) */ START_TEST(test_misc_null_parser) { XML_ParserFree(NULL); } END_TEST /* Test that XML_ErrorString rejects out-of-range codes */ START_TEST(test_misc_error_string) { if (XML_ErrorString((enum XML_Error)-1) != NULL) fail("Negative error code not rejected"); if (XML_ErrorString((enum XML_Error)100) != NULL) fail("Large error code not rejected"); } END_TEST /* Test the version information is consistent */ /* Since we are working in XML_LChars (potentially 16-bits), we * can't use the standard C library functions for character * manipulation and have to roll our own. */ static int parse_version(const XML_LChar *version_text, XML_Expat_Version *version_struct) { while (*version_text != 0x00) { if (*version_text >= ASCII_0 && *version_text <= ASCII_9) break; version_text++; } if (*version_text == 0x00) return XML_FALSE; /* version_struct->major = strtoul(version_text, 10, &version_text) */ version_struct->major = 0; while (*version_text >= ASCII_0 && *version_text <= ASCII_9) { version_struct->major = 10 * version_struct->major + (*version_text++ - ASCII_0); } if (*version_text++ != ASCII_PERIOD) return XML_FALSE; /* Now for the minor version number */ version_struct->minor = 0; while (*version_text >= ASCII_0 && *version_text <= ASCII_9) { version_struct->minor = 10 * version_struct->minor + (*version_text++ - ASCII_0); } if (*version_text++ != ASCII_PERIOD) return XML_FALSE; /* Finally the micro version number */ version_struct->micro = 0; while (*version_text >= ASCII_0 && *version_text <= ASCII_9) { version_struct->micro = 10 * version_struct->micro + (*version_text++ - ASCII_0); } if (*version_text != 0x00) return XML_FALSE; return XML_TRUE; } static int versions_equal(const XML_Expat_Version *first, const XML_Expat_Version *second) { return (first->major == second->major && first->minor == second->minor && first->micro == second->micro); } START_TEST(test_misc_version) { XML_Expat_Version read_version = XML_ExpatVersionInfo(); /* Silence compiler warning with the following assignment */ XML_Expat_Version parsed_version = { 0, 0, 0 }; const XML_LChar *version_text = XML_ExpatVersion(); if (version_text == NULL) fail("Could not obtain version text"); if (!parse_version(version_text, &parsed_version)) fail("Unable to parse version text"); if (!versions_equal(&read_version, &parsed_version)) fail("Version mismatch"); } END_TEST /* Test feature information */ START_TEST(test_misc_features) { const XML_Feature *features = XML_GetFeatureList(); /* Prevent problems with double-freeing parsers */ parser = NULL; if (features == NULL) fail("Failed to get feature information"); /* Loop through the features checking what we can */ while (features->feature != XML_FEATURE_END) { switch(features->feature) { case XML_FEATURE_SIZEOF_XML_CHAR: if (features->value != sizeof(XML_Char)) fail("Incorrect size of XML_Char"); break; case XML_FEATURE_SIZEOF_XML_LCHAR: if (features->value != sizeof(XML_LChar)) fail("Incorrect size of XML_LChar"); break; default: break; } features++; } } END_TEST /* Regression test for GitHub Issue #17: memory leak parsing attribute * values with mixed bound and unbound namespaces. */ START_TEST(test_misc_attribute_leak) { const char *text = ""; XML_Memory_Handling_Suite memsuite = { tracking_malloc, tracking_realloc, tracking_free }; parser = XML_ParserCreate_MM("UTF-8", &memsuite, "\n"); expect_failure(text, XML_ERROR_UNBOUND_PREFIX, "Unbound prefixes not found"); XML_ParserFree(parser); /* Prevent the teardown trying to double free */ parser = NULL; if (!tracking_report()) fail("Memory leak found"); } END_TEST static void alloc_setup(void) { XML_Memory_Handling_Suite memsuite = { duff_allocator, duff_reallocator, free }; /* Ensure the parser creation will go through */ allocation_count = ALLOC_ALWAYS_SUCCEED; reallocation_count = REALLOC_ALWAYS_SUCCEED; parser = XML_ParserCreate_MM(NULL, &memsuite, NULL); if (parser == NULL) fail("Parser not created"); } static void alloc_teardown(void) { basic_teardown(); } /* Test the effects of allocation failures on xml declaration processing */ START_TEST(test_alloc_parse_xdecl) { const char *text = "\n" "Hello, world"; int i; int repeat = 0; #define MAX_ALLOC_COUNT 10 for (i = 0; i < MAX_ALLOC_COUNT; i++) { /* Repeat some (most) counts to defeat cached allocations */ if (i == 2 && repeat != 1) { i--; repeat++; } else if (i == 3) { i -= 2; repeat++; } allocation_count = i; XML_SetXmlDeclHandler(parser, dummy_xdecl_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 /* As above, but with an encoding big enough to cause storing the * version information to expand the string pool being used. */ static int XMLCALL long_encoding_handler(void *UNUSED_P(userData), const XML_Char *UNUSED_P(encoding), XML_Encoding *info) { int i; for (i = 0; i < 256; i++) info->map[i] = i; info->data = NULL; info->convert = NULL; info->release = NULL; return XML_STATUS_OK; } START_TEST(test_alloc_parse_xdecl_2) { const char *text = "" "Hello, world"; int i; int repeat = 0; #define MAX_ALLOC_COUNT 10 for (i = 0; i < MAX_ALLOC_COUNT; i++) { /* Repeat counts to defeat cached allocations */ if (i == 4 && repeat == 3) { i -= 2; repeat++; } else if ((i == 2 && repeat < 3) || (i == 3 && repeat > 3)) { i--; repeat++; } allocation_count = i; XML_SetXmlDeclHandler(parser, dummy_xdecl_handler); XML_SetUnknownEncodingHandler(parser, long_encoding_handler, NULL); 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 /* 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_pi_3) { const char *text = ""; int i; #define MAX_ALLOC_COUNT 10 for (i = 0; i < MAX_ALLOC_COUNT; i++) { /* Every allocation bar the last is cached */ allocation_count = (i == 0) ? 0 : 1; 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, const XML_Char *UNUSED_P(base), const XML_Char *UNUSED_P(systemId), const XML_Char *UNUSED_P(publicId)) { XML_Parser new_parser; unsigned int i; /* Try a few different allocation levels */ for (i = 0; i < 10; i++) { allocation_count = i; new_parser = XML_ExternalEntityParserCreate(parser, context, NULL); if (new_parser != NULL) { XML_ParserFree(new_parser); break; } } if (i == 0) fail("External parser creation ignored failing allocator"); else if (i == 10) fail("Extern parser not created with allocation count 10"); /* Make sure other random allocation doesn't now fail */ allocation_count = ALLOC_ALWAYS_SUCCEED; /* Make sure the failure code path is executed too */ return XML_STATUS_ERROR; } /* Test that external parser creation running out of memory is * correctly reported. Based on the external entity test cases. */ START_TEST(test_alloc_create_external_parser) { const char *text = "\n" "\n" "&entity;"; char foo_text[] = ""; XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS); XML_SetUserData(parser, foo_text); XML_SetExternalEntityRefHandler(parser, external_entity_duff_loader); if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), XML_TRUE) != XML_STATUS_ERROR) { fail("External parser allocator returned success incorrectly"); } } END_TEST /* More external parser memory allocation testing */ START_TEST(test_alloc_run_external_parser) { const char *text = "\n" "\n" "&entity;"; char foo_text[] = ""; unsigned int i; for (i = 0; i < 10; i++) { XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS); XML_SetUserData(parser, foo_text); XML_SetExternalEntityRefHandler(parser, external_entity_null_loader); allocation_count = i; if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), XML_TRUE) != XML_STATUS_ERROR) break; /* Re-use the parser */ XML_ParserReset(parser, NULL); } if (i == 0) fail("Parsing ignored failing allocator"); else if (i == 10) fail("Parsing failed with allocation count 10"); } END_TEST static int XMLCALL external_entity_dbl_handler(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)) { intptr_t callno = (intptr_t)XML_GetUserData(parser); const char *text; XML_Parser new_parser; int i; if (callno == 0) { /* First time through, check how many calls to malloc occur */ text = ("\n" "\n" "\n"); allocation_count = 10000; new_parser = XML_ExternalEntityParserCreate(parser, context, NULL); if (new_parser == NULL) { fail("Unable to allocate first external parser"); return XML_STATUS_ERROR; } /* Stash the number of calls in the user data */ XML_SetUserData(parser, (void *)(intptr_t)(10000 - allocation_count)); } else { text = ("" ""); /* Try at varying levels to exercise more code paths */ for (i = 0; i < 20; i++) { allocation_count = callno + i; new_parser = XML_ExternalEntityParserCreate(parser, context, NULL); if (new_parser != NULL) break; } if (i == 0) { fail("Second external parser unexpectedly created"); XML_ParserFree(new_parser); return XML_STATUS_ERROR; } else if (i == 20) { fail("Second external parser not created"); return XML_STATUS_ERROR; } } allocation_count = ALLOC_ALWAYS_SUCCEED; if (_XML_Parse_SINGLE_BYTES(new_parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) { xml_failure(new_parser); return XML_STATUS_ERROR; } XML_ParserFree(new_parser); return XML_STATUS_OK; } /* Test that running out of memory in dtdCopy is correctly reported. * Based on test_default_ns_from_ext_subset_and_ext_ge() */ START_TEST(test_alloc_dtd_copy_default_atts) { const char *text = "\n" "\n" "]>\n" "\n" "&en;\n" ""; XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS); XML_SetExternalEntityRefHandler(parser, external_entity_dbl_handler); XML_SetUserData(parser, NULL); if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) xml_failure(parser); } END_TEST static int XMLCALL external_entity_dbl_handler_2(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)) { intptr_t callno = (intptr_t)XML_GetUserData(parser); const char *text; XML_Parser new_parser; enum XML_Status rv; if (callno == 0) { /* Try different allocation levels for whole exercise */ text = ("\n" "\n" "\n"); XML_SetUserData(parser, (void *)(intptr_t)1); new_parser = XML_ExternalEntityParserCreate(parser, context, NULL); if (new_parser == NULL) return XML_STATUS_ERROR; rv = _XML_Parse_SINGLE_BYTES(new_parser, text, strlen(text), XML_TRUE); } else { /* Just run through once */ text = ("" ""); new_parser = XML_ExternalEntityParserCreate(parser, context, NULL); if (new_parser == NULL) return XML_STATUS_ERROR; rv =_XML_Parse_SINGLE_BYTES(new_parser, text, strlen(text), XML_TRUE); } XML_ParserFree(new_parser); if (rv == XML_STATUS_ERROR) return XML_STATUS_ERROR; return XML_STATUS_OK; } /* Test more external entity allocation failure paths */ START_TEST(test_alloc_external_entity) { const char *text = "\n" "\n" "]>\n" "\n" "&en;\n" ""; int i; int repeat = 0; #define ALLOC_TEST_MAX_REPEATS 50 for (i = 0; i < ALLOC_TEST_MAX_REPEATS; i++) { /* Some allocation counts need to be repeated to follow all * the failure paths, given some allocations are cached. */ if (i == 11 && repeat == 7) { i -= 2; repeat++; } else if ((i == 1 && repeat < 2) || (i == 2 && repeat < 4) || (repeat < 7 && i == repeat+2) || (i == 10 && repeat == 8)) { i--; repeat++; } allocation_count = -1; XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS); XML_SetExternalEntityRefHandler(parser, external_entity_dbl_handler_2); XML_SetUserData(parser, NULL); allocation_count = i; if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), XML_TRUE) == XML_STATUS_OK) break; XML_ParserReset(parser, NULL); } allocation_count = -1; if (i == 0) fail("External entity parsed despite duff allocator"); if (i == ALLOC_TEST_MAX_REPEATS) fail("External entity not parsed at max allocation count"); #undef ALLOC_TEST_MAX_REPEATS } 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() */ 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 XML_STATUS_ERROR; if (!XML_SetEncoding(ext_parser, "utf-8")) { XML_ParserFree(ext_parser); return XML_STATUS_ERROR; } status = _XML_Parse_SINGLE_BYTES(ext_parser, text, strlen(text), XML_TRUE); XML_ParserFree(ext_parser); if (status == XML_STATUS_ERROR) return XML_STATUS_ERROR; return XML_STATUS_OK; } 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), const XML_Char *encoding, XML_Encoding *info) { if (!strcmp(encoding, "unsupported-encoding")) { int i; for (i = 0; i < 256; i++) info->map[i] = i; info->data = NULL; info->convert = NULL; info->release = dummy_release; return XML_STATUS_OK; } return XML_STATUS_ERROR; } /* Test the effects of allocation failure in internal entities. * Based on test_unknown_encoding_internal_entity */ START_TEST(test_alloc_internal_entity) { const char *text = "\n" "]>\n" ""; unsigned int i; int repeated = 0; for (i = 0; i < 10; i++) { /* Again, repeat some counts to account for caching */ if (repeated < 2 && i == 2) { i--; repeated++; } XML_SetUnknownEncodingHandler(parser, unknown_released_encoding_handler, NULL); 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("Internal entity worked despite failing allocations"); else if (i == 10) fail("Internal entity failed at allocation count 10"); } END_TEST /* Test the robustness against allocation failure of element handling * Based on test_dtd_default_handling(). */ START_TEST(test_alloc_dtd_default_handling) { const char *text = "\n" "\n" "\n" "\n" "\n" "\n" "\n" "]>\n" ""; const char *expected = "\n\n\n\n\n\n\n\n\ntext in doc"; CharData storage; int i; #define MAX_ALLOC_COUNT 15 int repeat = 0; for (i = 0; i < MAX_ALLOC_COUNT; i++) { /* Repeat some counts to catch cached allocations */ if ((repeat < 4 && i == 2) || (repeat == 4 && i == 4) || (repeat == 5 && i == 5) || (repeat == 6 && i == 8)) { i--; repeat++; } allocation_count = i; dummy_handler_flags = 0; XML_SetDefaultHandler(parser, accumulate_characters); XML_SetDoctypeDeclHandler(parser, dummy_start_doctype_handler, dummy_end_doctype_handler); XML_SetEntityDeclHandler(parser, dummy_entity_decl_handler); XML_SetNotationDeclHandler(parser, dummy_notation_decl_handler); XML_SetElementDeclHandler(parser, dummy_element_decl_handler); XML_SetAttlistDeclHandler(parser, dummy_attlist_decl_handler); XML_SetProcessingInstructionHandler(parser, dummy_pi_handler); XML_SetCommentHandler(parser, dummy_comment_handler); XML_SetCdataSectionHandler(parser, dummy_start_cdata_handler, dummy_end_cdata_handler); XML_SetUnparsedEntityDeclHandler( parser, dummy_unparsed_entity_decl_handler); CharData_Init(&storage); XML_SetUserData(parser, &storage); XML_SetCharacterDataHandler(parser, accumulate_characters); if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), XML_TRUE) != XML_STATUS_ERROR) break; XML_ParserReset(parser, NULL); } if (i == 0) fail("Default DTD parsed despite allocation failures"); if (i == MAX_ALLOC_COUNT) fail("Default DTD not parsed with maximum alloc count"); CharData_CheckXMLChars(&storage, expected); if (dummy_handler_flags != (DUMMY_START_DOCTYPE_HANDLER_FLAG | DUMMY_END_DOCTYPE_HANDLER_FLAG | DUMMY_ENTITY_DECL_HANDLER_FLAG | DUMMY_NOTATION_DECL_HANDLER_FLAG | DUMMY_ELEMENT_DECL_HANDLER_FLAG | DUMMY_ATTLIST_DECL_HANDLER_FLAG | DUMMY_COMMENT_HANDLER_FLAG | DUMMY_PI_HANDLER_FLAG | DUMMY_START_CDATA_HANDLER_FLAG | DUMMY_END_CDATA_HANDLER_FLAG | DUMMY_UNPARSED_ENTITY_DECL_HANDLER_FLAG)) fail("Not all handlers were called"); } #undef MAX_ALLOC_COUNT END_TEST /* Test robustness of XML_SetEncoding() with a failing allocator */ START_TEST(test_alloc_explicit_encoding) { int i; for (i = 0; i < 5; i++) { allocation_count = i; if (XML_SetEncoding(parser, "us-ascii") == XML_STATUS_OK) break; } if (i == 0) fail("Encoding set despite failing allocator"); else if (i == 5) fail("Encoding not set at allocation count 5"); } END_TEST /* Test robustness of XML_SetBase against a failing allocator */ START_TEST(test_alloc_set_base) { const XML_Char *new_base = "/local/file/name.xml"; int i; for (i = 0; i < 5; i++) { allocation_count = i; if (XML_SetBase(parser, new_base) == XML_STATUS_OK) break; } if (i == 0) fail("Base set despite failing allocator"); else if (i == 5) fail("Base not set with allocation count 5"); } END_TEST /* Test buffer extension in the face of a duff reallocator */ START_TEST(test_alloc_realloc_buffer) { const char *text = get_buffer_test_text; void *buffer; int i; int repeat = 0; /* Get a smallish buffer */ for (i = 0; i < 10; i++) { /* Repeat some indexes for cached allocations */ if (repeat < 6 && i == 2) { i--; repeat++; } reallocation_count = i; buffer = XML_GetBuffer(parser, 1536); if (buffer == NULL) fail("1.5K buffer reallocation failed"); memcpy(buffer, text, strlen(text)); if (XML_ParseBuffer(parser, strlen(text), XML_FALSE) == XML_STATUS_OK) break; XML_ParserReset(parser, NULL); } reallocation_count = -1; if (i == 0) fail("Parse succeeded with no reallocation"); else if (i == 10) fail("Parse failed with reallocation count 10"); } END_TEST /* Same test for external entity parsers */ static int XMLCALL external_entity_reallocator(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 = get_buffer_test_text; XML_Parser ext_parser; void *buffer; enum XML_Status status; ext_parser = XML_ExternalEntityParserCreate(parser, context, NULL); if (ext_parser == NULL) fail("Could not create external entity parser"); reallocation_count = (intptr_t)XML_GetUserData(parser); buffer = XML_GetBuffer(ext_parser, 1536); if (buffer == NULL) fail("Buffer allocation failed"); memcpy(buffer, text, strlen(text)); status = XML_ParseBuffer(ext_parser, strlen(text), XML_FALSE); reallocation_count = -1; XML_ParserFree(ext_parser); return (status == XML_STATUS_OK) ? XML_STATUS_OK : XML_STATUS_ERROR; } START_TEST(test_alloc_ext_entity_realloc_buffer) { const char *text = "\n" "]>\n" "&en;"; int i; for (i = 0; i < 10; i++) { XML_SetExternalEntityRefHandler(parser, external_entity_reallocator); XML_SetUserData(parser, (void *)(intptr_t)i); if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), XML_TRUE) == XML_STATUS_OK) break; XML_ParserReset(parser, NULL); } if (i == 0) fail("Succeeded with no reallocations"); if (i == 10) fail("Failed with 10 reallocations"); } END_TEST /* Test elements with many attributes are handled correctly */ START_TEST(test_alloc_realloc_many_attributes) { const char *text = "\n" "\n" "\n" "]>\n" "" ""; 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("Parse succeeded despite no reallocations"); if (i == MAX_REALLOC_COUNT) fail("Parse failed at max reallocations"); #undef MAX_REALLOC_COUNT } END_TEST /* Test handling of a public entity with failing allocator */ START_TEST(test_alloc_public_entity_value) { const char *text = "\n" "\n"; char dtd_text[] = "\n" "\n" "\n" "%e1;\n"; int i; #define MAX_ALLOC_COUNT 25 int repeat = 0; for (i = 0; i < MAX_ALLOC_COUNT; i++) { /* Repeat certain counts to defeat cached allocations */ if ((i == 2 && repeat < 2) || (i == 3 && repeat == 2) || (i == 8 && repeat == 3) || (i == 9 && repeat == 4) || (i == 18 && repeat == 5) || (i == 19 && repeat == 6)) { i--; repeat++; } allocation_count = i; dummy_handler_flags = 0; XML_SetUserData(parser, dtd_text); XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS); XML_SetExternalEntityRefHandler(parser, external_entity_public); /* Provoke a particular code path */ XML_SetEntityDeclHandler(parser, dummy_entity_decl_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 allocation"); if (i == MAX_ALLOC_COUNT) fail("Parsing failed at max allocation count"); if (dummy_handler_flags != DUMMY_ENTITY_DECL_HANDLER_FLAG) fail("Entity declaration handler not called"); } #undef MAX_ALLOC_COUNT END_TEST START_TEST(test_alloc_realloc_subst_public_entity_value) { const char *text = "\n" "\n"; char dtd_text[] = "\n" "\n" "%ThisIsAStupidlyLongParameterNameIntendedToTriggerPoolGrowth12345" "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP" "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP" "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP" "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP" "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP" "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP" "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP" "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP" "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP" "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP" "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP" "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP" "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP" "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP" "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP;"; int i; #define MAX_REALLOC_COUNT 10 for (i = 0; i < MAX_REALLOC_COUNT; i++) { reallocation_count = i; XML_SetUserData(parser, dtd_text); XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS); XML_SetExternalEntityRefHandler(parser, external_entity_public); 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 reallocation"); if (i == MAX_REALLOC_COUNT) fail("Parsing failed at max reallocation count"); } #undef MAX_REALLOC_COUNT END_TEST START_TEST(test_alloc_parse_public_doctype) { const char *text = "\n" "\n" ""; int i; #define MAX_ALLOC_COUNT 10 int repeat = 0; for (i = 0; i < MAX_ALLOC_COUNT; i++) { /* Repeat certain counts to defeat cached allocations */ if (i == 4 && repeat == 5) { i -= 2; repeat++; } else if ((i == 2 && repeat < 3) || (i == 3 && repeat < 5)) { i--; repeat++; } allocation_count = i; dummy_handler_flags = 0; XML_SetDoctypeDeclHandler(parser, dummy_start_doctype_decl_handler, dummy_end_doctype_decl_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 at maximum allocation count"); if (dummy_handler_flags != (DUMMY_START_DOCTYPE_DECL_HANDLER_FLAG | DUMMY_END_DOCTYPE_DECL_HANDLER_FLAG)) fail("Doctype handler functions not called"); } #undef MAX_ALLOC_COUNT END_TEST START_TEST(test_alloc_parse_public_doctype_long_name) { const char *text = "\n" "\n" ""; int i; #define MAX_ALLOC_COUNT 10 int repeat = 0; for (i = 0; i < MAX_ALLOC_COUNT; i++) { /* Repeat some counts to defeat cached allocations */ if (i == 4 && repeat == 6) { i -= 2; repeat++; } else if ((i == 2 && repeat < 3) || (i == 3 && repeat < 6)) { i--; repeat++; } allocation_count = i; XML_SetDoctypeDeclHandler(parser, dummy_start_doctype_decl_handler, dummy_end_doctype_decl_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 at maximum allocation count"); } #undef MAX_ALLOC_COUNT END_TEST static int XMLCALL external_entity_alloc(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 = (const char *)XML_GetUserData(parser); XML_Parser ext_parser; int parse_res; ext_parser = XML_ExternalEntityParserCreate(parser, context, NULL); if (ext_parser == NULL) return XML_STATUS_ERROR; parse_res = _XML_Parse_SINGLE_BYTES(ext_parser, text, strlen(text), XML_TRUE); XML_ParserFree(ext_parser); return parse_res; } /* Test foreign DTD handling */ START_TEST(test_alloc_set_foreign_dtd) { const char *text1 = "\n" "&entity;"; char text2[] = ""; int i; #define MAX_ALLOC_COUNT 10 int repeat = 0; for (i = 0; i < MAX_ALLOC_COUNT; i++) { /* Repeat some counts to deal with cached allocations */ if (i == 9 && repeat == 2) { i -= 2; repeat++; } else if (i == 2 && repeat < 2) { i--; repeat++; } allocation_count = i; XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS); XML_SetUserData(parser, &text2); XML_SetExternalEntityRefHandler(parser, external_entity_alloc); if (XML_UseForeignDTD(parser, XML_TRUE) != XML_ERROR_NONE) fail("Could not set foreign DTD"); if (_XML_Parse_SINGLE_BYTES(parser, text1, strlen(text1), 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 at maximum allocation count"); } #undef MAX_ALLOC_COUNT END_TEST /* Test based on ibm/valid/P32/ibm32v04.xml */ START_TEST(test_alloc_attribute_enum_value) { const char *text = "\n" "\n" "This is a \n \n\nyellow tiger"; char dtd_text[] = "\n" "\n" ""; int i; #define MAX_ALLOC_COUNT 20 int repeat = 0; for (i = 0; i < MAX_ALLOC_COUNT; i++) { /* Repeat some counts to allow for cached allocations */ if (i == 13 && repeat == 5) { i -= 2; repeat++; } else if ((i == 2 && repeat < 2) || (i == 3 && repeat == 2) || (i == 8 && repeat == 3) || (i == 9 && repeat == 4)) { i--; repeat++; } allocation_count = i; XML_SetExternalEntityRefHandler(parser, external_entity_alloc); XML_SetUserData(parser, dtd_text); XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS); /* An attribute list handler provokes a different code path */ XML_SetAttlistDeclHandler(parser, dummy_attlist_decl_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 at maximum allocation count"); } #undef MAX_ALLOC_COUNT END_TEST /* Test attribute enums sufficient to overflow the string pool */ START_TEST(test_alloc_realloc_attribute_enum_value) { const char *text = "\n" "\n" "This is a yellow tiger"; /* We wish to define a collection of attribute enums that will * cause the string pool storing them to have to expand. This * means more than 1024 bytes, including the parentheses and * separator bars. */ char dtd_text[] = "\n" ""; int i; #define MAX_REALLOC_COUNT 10 for (i = 0; i < MAX_REALLOC_COUNT; i++) { reallocation_count = i; XML_SetExternalEntityRefHandler(parser, external_entity_alloc); XML_SetUserData(parser, dtd_text); XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS); /* An attribute list handler provokes a different code path */ XML_SetAttlistDeclHandler(parser, dummy_attlist_decl_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 reallocator"); if (i == MAX_REALLOC_COUNT) fail("Parse failed at maximum reallocation count"); } #undef MAX_REALLOC_COUNT END_TEST /* Test attribute enums in a #IMPLIED attribute forcing pool growth */ START_TEST(test_alloc_realloc_implied_attribute) { /* Forcing this particular code path is a balancing act. The * addition of the closing parenthesis and terminal NUL must be * what pushes the string of enums over the 1024-byte limit, * otherwise a different code path will pick up the realloc. */ const char *text = "\n" "\n" "]>"; int i; #define MAX_REALLOC_COUNT 10 for (i = 0; i < MAX_REALLOC_COUNT; i++) { reallocation_count = i; XML_SetAttlistDeclHandler(parser, dummy_attlist_decl_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 reallocator"); if (i == MAX_REALLOC_COUNT) fail("Parse failed at maximum reallocation count"); } #undef MAX_REALLOC_COUNT END_TEST /* Test attribute enums in a defaulted attribute forcing pool growth */ START_TEST(test_alloc_realloc_default_attribute) { /* Forcing this particular code path is a balancing act. The * addition of the closing parenthesis and terminal NUL must be * what pushes the string of enums over the 1024-byte limit, * otherwise a different code path will pick up the realloc. */ const char *text = "\n" "\n]>"; int i; #define MAX_REALLOC_COUNT 10 for (i = 0; i < MAX_REALLOC_COUNT; i++) { reallocation_count = i; XML_SetAttlistDeclHandler(parser, dummy_attlist_decl_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 reallocator"); if (i == MAX_REALLOC_COUNT) fail("Parse failed at maximum reallocation count"); } #undef MAX_REALLOC_COUNT END_TEST /* Test long notation name with dodgy allocator */ START_TEST(test_alloc_notation) { const char *text = "\n" "\n" "\n" "]>\n"; int i; #define MAX_ALLOC_COUNT 10 int repeat = 0; for (i = 0; i < MAX_ALLOC_COUNT; i++) { /* Repeat some counts to allow for cached allocations */ if ((i == 2 && repeat < 4) || (i == 3)) { i--; repeat++; } allocation_count = i; dummy_handler_flags = 0; XML_SetNotationDeclHandler(parser, dummy_notation_decl_handler); XML_SetEntityDeclHandler(parser, dummy_entity_decl_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 allocation failures"); if (i == MAX_ALLOC_COUNT) fail("Parse failed at maximum allocation count"); if (dummy_handler_flags != (DUMMY_ENTITY_DECL_HANDLER_FLAG | DUMMY_NOTATION_DECL_HANDLER_FLAG)) fail("Entity declaration handler not called"); } #undef MAX_ALLOC_COUNT END_TEST /* Test public notation with dodgy allocator */ START_TEST(test_alloc_public_notation) { const char *text = "\n" "\n" "\n" "]>\n"; int i; #define MAX_ALLOC_COUNT 10 int repeat = 0; for (i = 0; i < MAX_ALLOC_COUNT; i++) { /* Repeat some counts to allow for cached allocations */ if ((i == 2 && repeat < 5) || (i == 3 && repeat == 5)) { i--; repeat++; } allocation_count = i; dummy_handler_flags = 0; XML_SetNotationDeclHandler(parser, dummy_notation_decl_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 allocation failures"); if (i == MAX_ALLOC_COUNT) fail("Parse failed at maximum allocation count"); if (dummy_handler_flags != DUMMY_NOTATION_DECL_HANDLER_FLAG) fail("Notation handler not called"); } #undef MAX_ALLOC_COUNT END_TEST /* Test public notation with dodgy allocator */ START_TEST(test_alloc_system_notation) { const char *text = "\n" "\n" "\n" "]>\n"; int i; #define MAX_ALLOC_COUNT 10 int repeat = 0; for (i = 0; i < MAX_ALLOC_COUNT; i++) { /* Repeat some counts to allow for cached allocations */ if ((i == 2 && repeat < 5) || (i == 3 && repeat == 5)) { i--; repeat++; } allocation_count = i; dummy_handler_flags = 0; XML_SetNotationDeclHandler(parser, dummy_notation_decl_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 allocation failures"); if (i == MAX_ALLOC_COUNT) fail("Parse failed at maximum allocation count"); if (dummy_handler_flags != DUMMY_NOTATION_DECL_HANDLER_FLAG) fail("Notation handler not called"); } #undef MAX_ALLOC_COUNT END_TEST START_TEST(test_alloc_nested_groups) { const char *text = "\n" "" "]>\n" ""; CharData storage; int i; #define MAX_ALLOC_COUNT 10 int repeat = 0; for (i = 0; i < MAX_ALLOC_COUNT; i++) { /* Repeat some counts to allow for cached allocations */ if ((i == 2 && repeat < 3) || (i == 3 && repeat == 3)) { i--; repeat++; } allocation_count = i; CharData_Init(&storage); XML_SetElementDeclHandler(parser, dummy_element_decl_handler); XML_SetStartElementHandler(parser, record_element_start_handler); XML_SetUserData(parser, &storage); dummy_handler_flags = 0; 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 reallocator"); if (i == MAX_ALLOC_COUNT) fail("Parse failed at maximum reallocation count"); CharData_CheckString(&storage, "doce"); if (dummy_handler_flags != DUMMY_ELEMENT_DECL_HANDLER_FLAG) fail("Element handler not fired"); } #undef MAX_ALLOC_COUNT END_TEST START_TEST(test_alloc_realloc_nested_groups) { const char *text = "\n" "" "]>\n" ""; CharData storage; int i; #define MAX_REALLOC_COUNT 10 for (i = 0; i < MAX_REALLOC_COUNT; i++) { reallocation_count = i; CharData_Init(&storage); XML_SetElementDeclHandler(parser, dummy_element_decl_handler); XML_SetStartElementHandler(parser, record_element_start_handler); XML_SetUserData(parser, &storage); dummy_handler_flags = 0; 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 reallocator"); if (i == MAX_REALLOC_COUNT) fail("Parse failed at maximum reallocation count"); CharData_CheckString(&storage, "doce"); if (dummy_handler_flags != DUMMY_ELEMENT_DECL_HANDLER_FLAG) fail("Element handler not fired"); } #undef MAX_REALLOC_COUNT END_TEST START_TEST(test_alloc_realloc_group_choice) { const char *text = "\n" "]>\n" "\n" "\n" "This is a foo\n" "\n" "\n"; int i; #define MAX_REALLOC_COUNT 10 for (i = 0; i < MAX_REALLOC_COUNT; i++) { reallocation_count = i; XML_SetElementDeclHandler(parser, dummy_element_decl_handler); dummy_handler_flags = 0; 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 reallocator"); if (i == MAX_REALLOC_COUNT) fail("Parse failed at maximum reallocation count"); if (dummy_handler_flags != DUMMY_ELEMENT_DECL_HANDLER_FLAG) fail("Element handler flag not raised"); } #undef MAX_REALLOC_COUNT END_TEST START_TEST(test_alloc_pi_in_epilog) { const char *text = "\n" ""; int i; #define MAX_ALLOC_COUNT 10 int repeat = 0; for (i = 0; i < MAX_ALLOC_COUNT; i++) { /* Repeat certain counts to allow for cached allocations */ if (i == 3 && repeat == 1) { i -= 2; repeat++; } else if (i == 2 && repeat < 4 && repeat != 1) { i--; repeat++; } allocation_count = i; XML_SetProcessingInstructionHandler(parser, dummy_pi_handler); dummy_handler_flags = 0; if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), XML_TRUE) != XML_STATUS_ERROR) break; XML_ParserReset(parser, NULL); } if (i == 0) fail("Parse completed despite failing allocator"); if (i == MAX_ALLOC_COUNT) fail("Parse failed at maximum allocation count"); if (dummy_handler_flags != DUMMY_PI_HANDLER_FLAG) fail("Processing instruction handler not invoked"); } #undef MAX_ALLOC_COUNT END_TEST START_TEST(test_alloc_comment_in_epilog) { const char *text = "\n" ""; int i; #define MAX_ALLOC_COUNT 10 int repeat = 0; for (i = 0; i < MAX_ALLOC_COUNT; i++) { /* Repeat certain counts to allow for cached allocations */ if (i == 3 && repeat == 1) { i -= 2; repeat++; } else if (i == 2 && repeat < 4 && repeat != 1) { i--; repeat++; } allocation_count = i; XML_SetCommentHandler(parser, dummy_comment_handler); dummy_handler_flags = 0; if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), XML_TRUE) != XML_STATUS_ERROR) break; XML_ParserReset(parser, NULL); } if (i == 0) fail("Parse completed despite failing allocator"); if (i == MAX_ALLOC_COUNT) fail("Parse failed at maximum allocation count"); if (dummy_handler_flags != DUMMY_COMMENT_HANDLER_FLAG) fail("Processing instruction handler not invoked"); } #undef MAX_ALLOC_COUNT END_TEST START_TEST(test_alloc_realloc_long_attribute_value) { const char *text = "]>\n" ""; 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("Parse succeeded despite failing reallocator"); if (i == MAX_REALLOC_COUNT) fail("Parse failed at maximum reallocation count"); } #undef MAX_REALLOC_COUNT END_TEST START_TEST(test_alloc_attribute_whitespace) { const char *text = ""; int i; #define MAX_ALLOC_COUNT 10 int repeat = 0; for (i = 0; i < MAX_ALLOC_COUNT; i++) { /* Repeat some counts to defeat cached allocations */ if (i == 3 && repeat == 1) { i -= 2; repeat++; } else if ((i == 2 && (repeat == 0 || repeat == 2 || repeat == 3)) || (i == 3 && repeat == 4)) { i--; repeat++; } 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("Parse succeeded despite failing allocator"); if (i == MAX_ALLOC_COUNT) fail("Parse failed at maximum allocation count"); } #undef MAX_ALLOC_COUNT END_TEST START_TEST(test_alloc_attribute_predefined_entity) { const char *text = ""; int i; #define MAX_ALLOC_COUNT 10 int repeat = 0; for (i = 0; i < MAX_ALLOC_COUNT; i++) { /* Repeat some counts to defeat cached allocations */ if (i == 3 && repeat == 1) { i -= 2; repeat++; } else if ((i == 2 && (repeat == 0 || repeat == 2 || repeat == 3)) || (i == 3 && repeat == 4)) { i--; repeat++; } 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("Parse succeeded despite failing allocator"); if (i == MAX_ALLOC_COUNT) fail("Parse failed at maximum allocation count"); } #undef MAX_ALLOC_COUNT END_TEST /* Test that a character reference at the end of a suitably long * default value for an attribute can trigger pool growth, and recovers * if the allocator fails on it. */ START_TEST(test_alloc_long_attr_default_with_char_ref) { const char *text = "]>\n" ""; int i; #define MAX_ALLOC_COUNT 10 int repeat = 0; for (i = 0; i < MAX_ALLOC_COUNT; i++) { /* Repeat some counts to defeat cached allocations */ if ((i == 2 && repeat < 3) || (i == 3 && repeat == 3) || (i == 4 && repeat == 4)) { i--; repeat++; } 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("Parse succeeded despite failing allocator"); if (i == MAX_ALLOC_COUNT) fail("Parse failed at maximum allocation count"); } #undef MAX_ALLOC_COUNT END_TEST /* Test that a long character reference substitution triggers a pool * expansion correctly for an attribute value. */ START_TEST(test_alloc_long_attr_value) { const char *text = "]>\n" ""; int i; #define MAX_ALLOC_COUNT 10 int repeat = 0; for (i = 0; i < MAX_ALLOC_COUNT; i++) { /* Repeat some counts to defeat cached allocations */ if ((i == 2 && repeat < 3) || (i == 3 && repeat < 6) || (i == 4 && repeat == 6) || (i == 5 && repeat == 7)) { i--; repeat++; } 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("Parse succeeded despite failing allocator"); if (i == MAX_ALLOC_COUNT) fail("Parse failed at maximum allocation count"); } #undef MAX_ALLOC_COUNT END_TEST /* Test that an error in a nested parameter entity substitution is * handled correctly. It seems unlikely that the code path being * exercised can be reached purely by carefully crafted XML, but an * allocation error in the right place will definitely do it. */ START_TEST(test_alloc_nested_entities) { const char *text = "\n" ""; ExtFaults test_data = { "\n" "\n" "%pe2;", "Memory Fail not faulted", NULL, XML_ERROR_NO_MEMORY }; /* Causes an allocation error in a nested storeEntityValue() */ allocation_count = 12; XML_SetUserData(parser, &test_data); XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS); XML_SetExternalEntityRefHandler(parser, external_entity_faulter); expect_failure(text, XML_ERROR_EXTERNAL_ENTITY_HANDLING, "Entity allocation failure not noted"); } END_TEST START_TEST(test_alloc_realloc_param_entity_newline) { const char *text = "\n" ""; char dtd_text[] = "\n'>" "%pe;\n"; int i; #define MAX_REALLOC_COUNT 5 for (i = 0; i < MAX_REALLOC_COUNT; i++) { reallocation_count = i; XML_SetUserData(parser, dtd_text); XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS); XML_SetExternalEntityRefHandler(parser, external_entity_alloc); 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 reallocator"); if (i == MAX_REALLOC_COUNT) fail("Parse failed at maximum reallocation count"); } #undef MAX_REALLOC_COUNT END_TEST START_TEST(test_alloc_realloc_ce_extends_pe) { const char *text = "\n" ""; char dtd_text[] = "\n'>" "%pe;\n"; int i; #define MAX_REALLOC_COUNT 5 for (i = 0; i < MAX_REALLOC_COUNT; i++) { reallocation_count = i; XML_SetUserData(parser, dtd_text); XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS); XML_SetExternalEntityRefHandler(parser, external_entity_alloc); 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 reallocator"); if (i == MAX_REALLOC_COUNT) fail("Parse failed at maximum reallocation count"); } #undef MAX_REALLOC_COUNT END_TEST START_TEST(test_alloc_realloc_attributes) { const char *text = "]>\n" "wombat\n"; int i; #define MAX_REALLOC_COUNT 5 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("Parse succeeded despite failing reallocator"); if (i == MAX_REALLOC_COUNT) fail("Parse failed at maximum reallocation count"); } #undef MAX_REALLOC_COUNT END_TEST START_TEST(test_alloc_long_doc_name) { const char *text = /* 64 characters per line */ ""; int i; #define MAX_ALLOC_COUNT 10 int repeat = 0; for (i = 0; i < MAX_ALLOC_COUNT; i++) { /* Repeat certain counts to defeat cached allocations */ if ((i == 2 && repeat < 4) || (i == 3 && repeat < 6)) { i--; repeat++; } 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 reallocations"); else if (i == MAX_ALLOC_COUNT) fail("Parsing failed even at max reallocation count"); } #undef MAX_ALLOC_COUNT END_TEST START_TEST(test_alloc_long_base) { const char *text = "\n" "]>\n" "&e;"; char entity_text[] = "Hello world"; const char *base = /* 64 characters per line */ "LongBaseURI/that/will/overflow/an/internal/buffer/and/cause/it/t" "o/have/to/grow/PQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/"; int i; #define MAX_ALLOC_COUNT 20 int repeat = 0; for (i = 0; i < MAX_ALLOC_COUNT; i++) { /* Repeat some counts to defeat cached allocations */ if (i == 4 && repeat == 4) { i -= 2; repeat++; } else if ((i == 2 && repeat < 4) || (i == 3 && repeat == 5) || (i == 4 && repeat == 6)) { i--; repeat++; } allocation_count = i; XML_SetUserData(parser, entity_text); XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS); XML_SetExternalEntityRefHandler(parser, external_entity_alloc); if (XML_SetBase(parser, base) == XML_STATUS_ERROR) { XML_ParserReset(parser, NULL); continue; } 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 START_TEST(test_alloc_long_public_id) { const char *text = "\n" "]>\n" "&e;"; char entity_text[] = "Hello world"; int i; #define MAX_ALLOC_COUNT 20 int repeat = 0; for (i = 0; i < MAX_ALLOC_COUNT; i++) { /* Repeat certain counts to defeat allocation caching */ if (i == 4 && repeat == 4) { i -= 2; repeat++; } else if ((i == 2 && repeat < 3) || (i == 3 && (repeat == 3 || repeat == 5)) || (i == 4 && repeat == 6)) { i--; repeat++; } allocation_count = i; XML_SetUserData(parser, entity_text); XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS); XML_SetExternalEntityRefHandler(parser, external_entity_alloc); 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 START_TEST(test_alloc_long_entity_value) { const char *text = "\n" " \n" "]>\n" "&e2;"; char entity_text[] = "Hello world"; int i; #define MAX_ALLOC_COUNT 20 int repeat = 0; for (i = 0; i < MAX_ALLOC_COUNT; i++) { /* Repeat certain counts to defeat cached allocations */ if (i == 5 && repeat == 4) { i -= 2; repeat++; } else if ((i == 2 && repeat < 3) || (i == 3 && repeat == 3) || (i == 4 && repeat == 5) || (i == 5 && repeat == 6)) { i--; repeat++; } allocation_count = i; XML_SetUserData(parser, entity_text); XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS); XML_SetExternalEntityRefHandler(parser, external_entity_alloc); 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 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.org/'>" ""; 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 /* Check handling of long prefix names (pool growth) */ START_TEST(test_nsalloc_realloc_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.org/'>" ""; int i; #define MAX_REALLOC_COUNT 12 for (i = 0; i < MAX_REALLOC_COUNT; i++) { /* Every reallocation bar the last is cached */ reallocation_count = (i == 0) ? 0 : 1; 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 even at max reallocation count"); } #undef MAX_REALLOC_COUNT END_TEST /* Check handling of even long prefix names (different code path) */ START_TEST(test_nsalloc_realloc_longer_prefix) { const char *text = "<" /* 64 characters per line */ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "Q:foo xmlns:" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "Q='http://example.org/'>" ""; int i; #define MAX_REALLOC_COUNT 12 for (i = 0; i < MAX_REALLOC_COUNT; i++) { /* Every reallocation bar the last is cached */ reallocation_count = (i == 0) ? 0 : 1; 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 even at max reallocation count"); } #undef MAX_REALLOC_COUNT END_TEST START_TEST(test_nsalloc_long_namespace) { const char *text = "<" /* 64 characters per line */ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" ":e xmlns:" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "='http://example.org/'>\n" "<" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" ":f " "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" ":attr='foo'/>\n" ""; int i; #define MAX_ALLOC_COUNT 10 int repeat = 0; for (i = 0; i < MAX_ALLOC_COUNT; i++) { /* Repeat some counts to defeat cached allocations */ if ((i == 4 && (repeat == 3 || repeat == 6)) || (i == 7 && repeat == 11)) { i -= 2; repeat++; } else if ((i == 2 && repeat < 2) || (i == 3 && (repeat == 2 || repeat == 4 || repeat == 5 || repeat == 7 || repeat == 8)) || (i == 5 && (repeat == 9 || repeat == 10)) || (i == 6 && repeat == 12) || (i == 7 && repeat == 13)) { i--; repeat++; } 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 /* Using a slightly shorter namespace name provokes allocations in * slightly different places in the code. */ START_TEST(test_nsalloc_less_long_namespace) { const char *text = "<" /* 64 characters per line */ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz012345678" ":e xmlns:" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz012345678" "='http://example.org/'>\n" "<" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz012345678" ":f " "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz012345678" ":att='foo'/>\n" ""; int i; #define MAX_ALLOC_COUNT 10 int repeat = 0; for (i = 0; i < MAX_ALLOC_COUNT; i++) { /* Repeat some counts to defeat cached allocations */ if ((i == 4 && (repeat == 3 || repeat == 5)) || (i == 7 && repeat == 10)) { i -= 2; repeat++; } else if ((i == 2 && repeat < 2) || (i == 3 && (repeat == 2 || repeat == 4 || repeat == 6)) || (i == 4 && repeat == 7) || (i == 5 && (repeat == 8 || repeat == 9)) || (i == 6 && (repeat == 11 || repeat == 12)) || (i == 7 && repeat == 13)) { i--; repeat++; } 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 typedef struct ExtOption { const char *system_id; const char *parse_text; } ExtOption; static int XMLCALL external_entity_optioner(XML_Parser parser, const XML_Char *context, const XML_Char *UNUSED_P(base), const XML_Char *systemId, const XML_Char *UNUSED_P(publicId)) { ExtOption *options = (ExtOption *)XML_GetUserData(parser); XML_Parser ext_parser; while (options->parse_text != NULL) { if (!strcmp(systemId, options->system_id)) { ext_parser = XML_ExternalEntityParserCreate(parser, context, NULL); if (ext_parser == NULL) return XML_STATUS_ERROR; if (_XML_Parse_SINGLE_BYTES(ext_parser, options->parse_text, strlen(options->parse_text), XML_TRUE) == XML_STATUS_ERROR) return XML_STATUS_ERROR; XML_ParserFree(ext_parser); return XML_STATUS_OK; } options++; } fail("No suitable option found"); return XML_STATUS_ERROR; } START_TEST(test_nsalloc_long_context) { const char *text = "\n" " \n" "]>\n" "\n" "&en;" ""; ExtOption options[] = { { "foo", ""}, { "bar", "" }, { NULL, NULL } }; int i; #define MAX_ALLOC_COUNT 40 int repeat = 0; for (i = 0; i < MAX_ALLOC_COUNT; i++) { /* Repeat some counts to defeat allocation caching */ if ((i == 4 && repeat == 3) || (i == 13 && repeat == 9) || (i == 14 && repeat == 10)) { i -= 2; repeat++; } else if ((i == 2 && repeat < 2) || (i == 3 && (repeat == 2 || repeat == 4 || repeat == 5)) || (i == 4 && repeat == 6) || (i == 5 && repeat == 7) || (i == 7 && repeat == 8) || (i == 13 && repeat == 11)) { i--; repeat++; } allocation_count = i; XML_SetUserData(parser, options); XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS); XML_SetExternalEntityRefHandler(parser, external_entity_optioner); 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 /* This function is void; it will throw a fail() on error, so if it * returns normally it must have succeeded. */ static void context_realloc_test(XML_Parser parser, const char *text) { ExtOption options[] = { { "foo", ""}, { "bar", "" }, { NULL, NULL } }; int i; #define MAX_REALLOC_COUNT 5 for (i = 0; i < MAX_REALLOC_COUNT; i++) { reallocation_count = i; XML_SetUserData(parser, options); XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS); XML_SetExternalEntityRefHandler(parser, external_entity_optioner); 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 even at max reallocation count"); #undef MAX_REALLOC_COUNT } START_TEST(test_nsalloc_realloc_long_context) { const char *text = "\n" "]>\n" "\n" "&en;" ""; context_realloc_test(parser, text); } END_TEST START_TEST(test_nsalloc_realloc_long_context_2) { const char *text = "\n" "]>\n" "\n" "&en;" ""; context_realloc_test(parser, text); } END_TEST START_TEST(test_nsalloc_realloc_long_context_3) { const char *text = "\n" "]>\n" "\n" "&en;" ""; context_realloc_test(parser, text); } END_TEST START_TEST(test_nsalloc_realloc_long_context_4) { const char *text = "\n" "]>\n" "\n" "&en;" ""; context_realloc_test(parser, text); } END_TEST START_TEST(test_nsalloc_realloc_long_context_5) { const char *text = "\n" "]>\n" "\n" "&en;" ""; context_realloc_test(parser, text); } END_TEST START_TEST(test_nsalloc_realloc_long_context_6) { const char *text = "\n" "]>\n" "\n" "&en;" ""; context_realloc_test(parser, text); } END_TEST START_TEST(test_nsalloc_realloc_long_context_7) { const char *text = "\n" "]>\n" "\n" "&en;" ""; context_realloc_test(parser, text); } END_TEST START_TEST(test_nsalloc_realloc_long_ge_name) { const char *text = "\n" "]>\n" "\n" "&" /* 64 characters per line */ "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP" "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP" "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP" "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP" "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP" "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP" "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP" "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP" "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP" "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP" "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP" "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP" "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP" "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP" "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP" "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP" ";" ""; ExtOption options[] = { { "foo", "" }, { "bar", "" }, { NULL, NULL } }; int i; #define MAX_REALLOC_COUNT 5 int repeat = 0; for (i = 0; i < MAX_REALLOC_COUNT; i++) { /* Repeat some counts to defeat caching */ if (i == 2 && repeat < 3) { i--; repeat++; } reallocation_count = i; XML_SetUserData(parser, options); XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS); XML_SetExternalEntityRefHandler(parser, external_entity_optioner); 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 even at max reallocation count"); #undef MAX_REALLOC_COUNT } END_TEST /* Test that when a namespace is passed through the context mechanism * to an external entity parser, the parsers handle reallocation * failures correctly. The prefix is exactly the right length to * provoke particular uncommon code paths. */ START_TEST(test_nsalloc_realloc_long_context_in_dtd) { const char *text = "\n" "]>\n" "<" "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP" "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP" "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP" "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP" "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP" "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP" "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP" "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP" "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP" "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP" "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP" "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP" "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP" "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP" "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP" "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP" ":doc xmlns:" "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP" "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP" "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP" "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP" "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP" "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP" "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP" "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP" "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP" "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP" "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP" "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP" "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP" "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP" "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP" "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP" "='foo/Second'>&First;"; ExtOption options[] = { { "foo/First", "Hello world" }, { NULL, NULL } }; int i; #define MAX_REALLOC_COUNT 10 int repeat = 0; for (i = 0; i < MAX_REALLOC_COUNT; i++) { /* Repeat some counts to defeat allocation caching */ if (i == 2 && repeat < 11) { i--; repeat++; } reallocation_count = i; XML_SetUserData(parser, options); XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS); XML_SetExternalEntityRefHandler(parser, external_entity_optioner); 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 even at max reallocation count"); #undef MAX_REALLOC_COUNT } END_TEST START_TEST(test_nsalloc_long_default_in_ext) { const char *text = "\n" " \n" "]>\n" "&x;"; ExtOption options[] = { { "foo", ""}, { NULL, NULL } }; int i; #define MAX_ALLOC_COUNT 30 int repeat = 0; for (i = 0; i < MAX_ALLOC_COUNT; i++) { /* Repeat some counts to defeat allocation caching */ if ((i == 4 && repeat == 3) || (i == 8 && repeat == 9)) { i -= 2; repeat++; } else if ((i == 2 && repeat < 2) || (i == 3 && (repeat == 2 || repeat == 4 || repeat == 5)) || (i == 4 && repeat == 6) || (i == 5 && repeat == 7) || (i == 6 && repeat == 8)) { i--; repeat++; } allocation_count = i; XML_SetUserData(parser, options); XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS); XML_SetExternalEntityRefHandler(parser, external_entity_optioner); 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 START_TEST(test_nsalloc_long_systemid_in_ext) { const char *text = "\n" "]>\n" "&en;"; ExtOption options[] = { { "foo", "" }, { "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/" "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/" "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/" "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/" "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/" "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/" "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/" "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/" "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/" "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/" "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/" "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/" "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/" "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/" "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/" "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/", "" }, { NULL, NULL } }; int i; #define MAX_ALLOC_COUNT 30 int repeat = 0; for (i = 0; i < MAX_ALLOC_COUNT; i++) { /* Repeat some counts to allow for cached allocations */ if ((i == 4 && repeat == 3) || (i == 10 && repeat == 8)) { i -= 2; repeat++; } else if ((i == 2 && repeat < 2) || (i == 3 && (repeat == 2 || repeat == 4 || repeat == 5)) || (i == 4 && repeat == 6) || (i == 5 && repeat == 7) || (i == 9 && repeat == 8)) { i--; repeat++; } allocation_count = i; XML_SetUserData(parser, options); XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS); XML_SetExternalEntityRefHandler(parser, external_entity_optioner); 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 static Suite * make_suite(void) { Suite *s = suite_create("basic"); TCase *tc_basic = tcase_create("basic tests"); 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); tcase_add_test(tc_basic, test_nul_byte); tcase_add_test(tc_basic, test_u0000_char); tcase_add_test(tc_basic, test_siphash_self); tcase_add_test(tc_basic, test_siphash_spec); tcase_add_test(tc_basic, test_bom_utf8); tcase_add_test(tc_basic, test_bom_utf16_be); tcase_add_test(tc_basic, test_bom_utf16_le); tcase_add_test(tc_basic, test_illegal_utf8); tcase_add_test(tc_basic, test_utf8_auto_align); tcase_add_test(tc_basic, test_utf16); tcase_add_test(tc_basic, test_utf16_le_epilog_newline); tcase_add_test(tc_basic, test_not_utf16); tcase_add_test(tc_basic, test_bad_encoding); tcase_add_test(tc_basic, test_latin1_umlauts); /* Regression test for SF bug #491986. */ tcase_add_test(tc_basic, test_danish_latin1); /* Regression test for SF bug #514281. */ tcase_add_test(tc_basic, test_french_charref_hexidecimal); tcase_add_test(tc_basic, test_french_charref_decimal); tcase_add_test(tc_basic, test_french_latin1); tcase_add_test(tc_basic, test_french_utf8); tcase_add_test(tc_basic, test_utf8_false_rejection); tcase_add_test(tc_basic, test_line_number_after_parse); tcase_add_test(tc_basic, test_column_number_after_parse); tcase_add_test(tc_basic, test_line_and_column_numbers_inside_handlers); 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); tcase_add_test(tc_basic, test_unknown_encoding_internal_entity); tcase_add_test(tc_basic, test_unrecognised_encoding_internal_entity); tcase_add_test(tc_basic, test_wfc_undeclared_entity_unread_external_subset); tcase_add_test(tc_basic, test_wfc_undeclared_entity_no_external_subset); tcase_add_test(tc_basic, test_wfc_undeclared_entity_standalone); tcase_add_test(tc_basic, test_wfc_undeclared_entity_with_external_subset); tcase_add_test(tc_basic, test_not_standalone_handler_reject); tcase_add_test(tc_basic, test_not_standalone_handler_accept); tcase_add_test(tc_basic, test_wfc_undeclared_entity_with_external_subset_standalone); tcase_add_test(tc_basic, test_entity_with_external_subset_unless_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_bad_encoding_2); tcase_add_test(tc_basic, test_ext_entity_invalid_parse); tcase_add_test(tc_basic, test_ext_entity_invalid_suspended_parse); tcase_add_test(tc_basic, test_dtd_default_handling); tcase_add_test(tc_basic, test_dtd_attr_handling); tcase_add_test(tc_basic, test_empty_ns_without_namespaces); tcase_add_test(tc_basic, test_ns_in_attribute_default_without_namespaces); tcase_add_test(tc_basic, test_stop_parser_between_char_data_calls); tcase_add_test(tc_basic, test_suspend_parser_between_char_data_calls); 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); tcase_add_test(tc_basic, test_set_foreign_dtd); tcase_add_test(tc_basic, test_foreign_dtd_not_standalone); tcase_add_test(tc_basic, test_invalid_foreign_dtd); tcase_add_test(tc_basic, test_foreign_dtd_with_doctype); tcase_add_test(tc_basic, test_foreign_dtd_without_external_subset); tcase_add_test(tc_basic, test_empty_foreign_dtd); tcase_add_test(tc_basic, test_set_base); tcase_add_test(tc_basic, test_attributes); 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); tcase_add_test(tc_basic, test_get_buffer_1); tcase_add_test(tc_basic, test_get_buffer_2); tcase_add_test(tc_basic, test_byte_info_at_end); 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_predefined_entities); 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); tcase_add_test(tc_basic, test_external_entity_values); tcase_add_test(tc_basic, test_ext_entity_not_standalone); tcase_add_test(tc_basic, test_ext_entity_value_abort); tcase_add_test(tc_basic, test_bad_public_doctype); tcase_add_test(tc_basic, test_attribute_enum_value); tcase_add_test(tc_basic, test_predefined_entity_redefinition); tcase_add_test(tc_basic, test_dtd_stop_processing); tcase_add_test(tc_basic, test_public_notation_no_sysid); tcase_add_test(tc_basic, test_nested_groups); tcase_add_test(tc_basic, test_group_choice); tcase_add_test(tc_basic, test_standalone_parameter_entity); tcase_add_test(tc_basic, test_skipped_parameter_entity); tcase_add_test(tc_basic, test_recursive_external_parameter_entity); tcase_add_test(tc_basic, test_undefined_ext_entity_in_external_dtd); tcase_add_test(tc_basic, test_suspend_xdecl); tcase_add_test(tc_basic, test_abort_epilog); tcase_add_test(tc_basic, test_abort_epilog_2); tcase_add_test(tc_basic, test_suspend_epilog); tcase_add_test(tc_basic, test_unfinished_epilog); tcase_add_test(tc_basic, test_partial_char_in_epilog); tcase_add_test(tc_basic, test_hash_collision); tcase_add_test(tc_basic, test_suspend_resume_internal_entity); tcase_add_test(tc_basic, test_resume_entity_with_syntax_error); tcase_add_test(tc_basic, test_suspend_resume_parameter_entity); tcase_add_test(tc_basic, test_restart_on_error); tcase_add_test(tc_basic, test_reject_lt_in_attribute_value); tcase_add_test(tc_basic, test_reject_unfinished_param_in_att_value); tcase_add_test(tc_basic, test_trailing_cr_in_att_value); tcase_add_test(tc_basic, test_standalone_internal_entity); tcase_add_test(tc_basic, test_skipped_external_entity); tcase_add_test(tc_basic, test_skipped_null_loaded_ext_entity); tcase_add_test(tc_basic, test_skipped_unloaded_ext_entity); tcase_add_test(tc_basic, test_param_entity_with_trailing_cr); tcase_add_test(tc_basic, test_invalid_character_entity); tcase_add_test(tc_basic, test_pi_handled_in_default); tcase_add_test(tc_basic, test_comment_handled_in_default); suite_add_tcase(s, tc_namespace); tcase_add_checked_fixture(tc_namespace, namespace_setup, namespace_teardown); tcase_add_test(tc_namespace, test_return_ns_triplet); tcase_add_test(tc_namespace, test_ns_tagname_overwrite); tcase_add_test(tc_namespace, test_ns_tagname_overwrite_triplet); tcase_add_test(tc_namespace, test_start_ns_clears_start_element); tcase_add_test(tc_namespace, test_default_ns_from_ext_subset_and_ext_ge); tcase_add_test(tc_namespace, test_ns_prefix_with_empty_uri_1); 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_null_parser); tcase_add_test(tc_misc, test_misc_error_string); tcase_add_test(tc_misc, test_misc_version); tcase_add_test(tc_misc, test_misc_features); 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_xdecl); tcase_add_test(tc_alloc, test_alloc_parse_xdecl_2); 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_pi_3); 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); tcase_add_test(tc_alloc, test_alloc_realloc_buffer); tcase_add_test(tc_alloc, test_alloc_ext_entity_realloc_buffer); tcase_add_test(tc_alloc, test_alloc_realloc_many_attributes); tcase_add_test(tc_alloc, test_alloc_public_entity_value); tcase_add_test(tc_alloc, test_alloc_realloc_subst_public_entity_value); tcase_add_test(tc_alloc, test_alloc_parse_public_doctype); tcase_add_test(tc_alloc, test_alloc_parse_public_doctype_long_name); tcase_add_test(tc_alloc, test_alloc_set_foreign_dtd); tcase_add_test(tc_alloc, test_alloc_attribute_enum_value); tcase_add_test(tc_alloc, test_alloc_realloc_attribute_enum_value); tcase_add_test(tc_alloc, test_alloc_realloc_implied_attribute); tcase_add_test(tc_alloc, test_alloc_realloc_default_attribute); tcase_add_test(tc_alloc, test_alloc_notation); tcase_add_test(tc_alloc, test_alloc_public_notation); tcase_add_test(tc_alloc, test_alloc_system_notation); tcase_add_test(tc_alloc, test_alloc_nested_groups); tcase_add_test(tc_alloc, test_alloc_realloc_nested_groups); tcase_add_test(tc_alloc, test_alloc_realloc_group_choice); tcase_add_test(tc_alloc, test_alloc_pi_in_epilog); tcase_add_test(tc_alloc, test_alloc_comment_in_epilog); tcase_add_test(tc_alloc, test_alloc_realloc_long_attribute_value); tcase_add_test(tc_alloc, test_alloc_attribute_whitespace); tcase_add_test(tc_alloc, test_alloc_attribute_predefined_entity); tcase_add_test(tc_alloc, test_alloc_long_attr_default_with_char_ref); tcase_add_test(tc_alloc, test_alloc_long_attr_value); tcase_add_test(tc_alloc, test_alloc_nested_entities); tcase_add_test(tc_alloc, test_alloc_realloc_param_entity_newline); tcase_add_test(tc_alloc, test_alloc_realloc_ce_extends_pe); tcase_add_test(tc_alloc, test_alloc_realloc_attributes); tcase_add_test(tc_alloc, test_alloc_long_doc_name); tcase_add_test(tc_alloc, test_alloc_long_base); tcase_add_test(tc_alloc, test_alloc_long_public_id); tcase_add_test(tc_alloc, test_alloc_long_entity_value); 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); tcase_add_test(tc_nsalloc, test_nsalloc_realloc_long_prefix); tcase_add_test(tc_nsalloc, test_nsalloc_realloc_longer_prefix); tcase_add_test(tc_nsalloc, test_nsalloc_long_namespace); tcase_add_test(tc_nsalloc, test_nsalloc_less_long_namespace); tcase_add_test(tc_nsalloc, test_nsalloc_long_context); tcase_add_test(tc_nsalloc, test_nsalloc_realloc_long_context); tcase_add_test(tc_nsalloc, test_nsalloc_realloc_long_context_2); tcase_add_test(tc_nsalloc, test_nsalloc_realloc_long_context_3); tcase_add_test(tc_nsalloc, test_nsalloc_realloc_long_context_4); tcase_add_test(tc_nsalloc, test_nsalloc_realloc_long_context_5); tcase_add_test(tc_nsalloc, test_nsalloc_realloc_long_context_6); tcase_add_test(tc_nsalloc, test_nsalloc_realloc_long_context_7); tcase_add_test(tc_nsalloc, test_nsalloc_realloc_long_ge_name); tcase_add_test(tc_nsalloc, test_nsalloc_realloc_long_context_in_dtd); tcase_add_test(tc_nsalloc, test_nsalloc_long_default_in_ext); tcase_add_test(tc_nsalloc, test_nsalloc_long_systemid_in_ext); return s; } int main(int argc, char *argv[]) { int i, nf; int verbosity = CK_NORMAL; Suite *s = make_suite(); SRunner *sr = srunner_create(s); /* run the tests for internal helper functions */ testhelper_is_whitespace_normalized(); for (i = 1; i < argc; ++i) { char *opt = argv[i]; if (strcmp(opt, "-v") == 0 || strcmp(opt, "--verbose") == 0) verbosity = CK_VERBOSE; else if (strcmp(opt, "-q") == 0 || strcmp(opt, "--quiet") == 0) verbosity = CK_SILENT; else { fprintf(stderr, "runtests: unknown option '%s'\n", opt); return 2; } } if (verbosity != CK_SILENT) printf("Expat version: %s\n", XML_ExpatVersion()); srunner_run_all(sr, verbosity); nf = srunner_ntests_failed(sr); srunner_free(sr); return (nf == 0) ? EXIT_SUCCESS : EXIT_FAILURE; }