1bd8f1dc3Sbluhm /* Tests in the "miscellaneous" test case for the Expat test suite 2bd8f1dc3Sbluhm __ __ _ 3bd8f1dc3Sbluhm ___\ \/ /_ __ __ _| |_ 4bd8f1dc3Sbluhm / _ \\ /| '_ \ / _` | __| 5bd8f1dc3Sbluhm | __// \| |_) | (_| | |_ 6bd8f1dc3Sbluhm \___/_/\_\ .__/ \__,_|\__| 7bd8f1dc3Sbluhm |_| XML parser 8bd8f1dc3Sbluhm 9bd8f1dc3Sbluhm Copyright (c) 2001-2006 Fred L. Drake, Jr. <fdrake@users.sourceforge.net> 10bd8f1dc3Sbluhm Copyright (c) 2003 Greg Stein <gstein@users.sourceforge.net> 11bd8f1dc3Sbluhm Copyright (c) 2005-2007 Steven Solie <steven@solie.ca> 12bd8f1dc3Sbluhm Copyright (c) 2005-2012 Karl Waclawek <karl@waclawek.net> 13bd8f1dc3Sbluhm Copyright (c) 2016-2024 Sebastian Pipping <sebastian@pipping.org> 14bd8f1dc3Sbluhm Copyright (c) 2017-2022 Rhodri James <rhodri@wildebeest.org.uk> 15bd8f1dc3Sbluhm Copyright (c) 2017 Joe Orton <jorton@redhat.com> 16bd8f1dc3Sbluhm Copyright (c) 2017 José Gutiérrez de la Concha <jose@zeroc.com> 17bd8f1dc3Sbluhm Copyright (c) 2018 Marco Maggi <marco.maggi-ipsu@poste.it> 18bd8f1dc3Sbluhm Copyright (c) 2019 David Loffredo <loffredo@steptools.com> 19bd8f1dc3Sbluhm Copyright (c) 2020 Tim Gates <tim.gates@iress.com> 20bd8f1dc3Sbluhm Copyright (c) 2021 Donghee Na <donghee.na@python.org> 21bd8f1dc3Sbluhm Copyright (c) 2023 Sony Corporation / Snild Dolkow <snild@sony.com> 22bd8f1dc3Sbluhm Licensed under the MIT license: 23bd8f1dc3Sbluhm 24bd8f1dc3Sbluhm Permission is hereby granted, free of charge, to any person obtaining 25bd8f1dc3Sbluhm a copy of this software and associated documentation files (the 26bd8f1dc3Sbluhm "Software"), to deal in the Software without restriction, including 27bd8f1dc3Sbluhm without limitation the rights to use, copy, modify, merge, publish, 28bd8f1dc3Sbluhm distribute, sublicense, and/or sell copies of the Software, and to permit 29bd8f1dc3Sbluhm persons to whom the Software is furnished to do so, subject to the 30bd8f1dc3Sbluhm following conditions: 31bd8f1dc3Sbluhm 32bd8f1dc3Sbluhm The above copyright notice and this permission notice shall be included 33bd8f1dc3Sbluhm in all copies or substantial portions of the Software. 34bd8f1dc3Sbluhm 35bd8f1dc3Sbluhm THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 36bd8f1dc3Sbluhm EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 37bd8f1dc3Sbluhm MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 38bd8f1dc3Sbluhm NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 39bd8f1dc3Sbluhm DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 40bd8f1dc3Sbluhm OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 41bd8f1dc3Sbluhm USE OR OTHER DEALINGS IN THE SOFTWARE. 42bd8f1dc3Sbluhm */ 43bd8f1dc3Sbluhm 44bd8f1dc3Sbluhm #if defined(NDEBUG) 45bd8f1dc3Sbluhm # undef NDEBUG /* because test suite relies on assert(...) at the moment */ 46bd8f1dc3Sbluhm #endif 47bd8f1dc3Sbluhm 48bd8f1dc3Sbluhm #include <assert.h> 49bd8f1dc3Sbluhm #include <string.h> 50bd8f1dc3Sbluhm 51bd8f1dc3Sbluhm #include "expat_config.h" 52bd8f1dc3Sbluhm 53bd8f1dc3Sbluhm #include "expat.h" 54bd8f1dc3Sbluhm #include "internal.h" 55bd8f1dc3Sbluhm #include "minicheck.h" 56bd8f1dc3Sbluhm #include "memcheck.h" 57bd8f1dc3Sbluhm #include "common.h" 58bd8f1dc3Sbluhm #include "ascii.h" /* for ASCII_xxx */ 59bd8f1dc3Sbluhm #include "handlers.h" 60bd8f1dc3Sbluhm #include "misc_tests.h" 61bd8f1dc3Sbluhm 62bd8f1dc3Sbluhm /* Test that a failure to allocate the parser structure fails gracefully */ 63bd8f1dc3Sbluhm START_TEST(test_misc_alloc_create_parser) { 64bd8f1dc3Sbluhm XML_Memory_Handling_Suite memsuite = {duff_allocator, realloc, free}; 65bd8f1dc3Sbluhm unsigned int i; 66bd8f1dc3Sbluhm const unsigned int max_alloc_count = 10; 67bd8f1dc3Sbluhm 68bd8f1dc3Sbluhm /* Something this simple shouldn't need more than 10 allocations */ 69bd8f1dc3Sbluhm for (i = 0; i < max_alloc_count; i++) { 70bd8f1dc3Sbluhm g_allocation_count = i; 71bd8f1dc3Sbluhm g_parser = XML_ParserCreate_MM(NULL, &memsuite, NULL); 72bd8f1dc3Sbluhm if (g_parser != NULL) 73bd8f1dc3Sbluhm break; 74bd8f1dc3Sbluhm } 75bd8f1dc3Sbluhm if (i == 0) 76bd8f1dc3Sbluhm fail("Parser unexpectedly ignored failing allocator"); 77bd8f1dc3Sbluhm else if (i == max_alloc_count) 78bd8f1dc3Sbluhm fail("Parser not created with max allocation count"); 79bd8f1dc3Sbluhm } 80bd8f1dc3Sbluhm END_TEST 81bd8f1dc3Sbluhm 82bd8f1dc3Sbluhm /* Test memory allocation failures for a parser with an encoding */ 83bd8f1dc3Sbluhm START_TEST(test_misc_alloc_create_parser_with_encoding) { 84bd8f1dc3Sbluhm XML_Memory_Handling_Suite memsuite = {duff_allocator, realloc, free}; 85bd8f1dc3Sbluhm unsigned int i; 86bd8f1dc3Sbluhm const unsigned int max_alloc_count = 10; 87bd8f1dc3Sbluhm 88bd8f1dc3Sbluhm /* Try several levels of allocation */ 89bd8f1dc3Sbluhm for (i = 0; i < max_alloc_count; i++) { 90bd8f1dc3Sbluhm g_allocation_count = i; 91bd8f1dc3Sbluhm g_parser = XML_ParserCreate_MM(XCS("us-ascii"), &memsuite, NULL); 92bd8f1dc3Sbluhm if (g_parser != NULL) 93bd8f1dc3Sbluhm break; 94bd8f1dc3Sbluhm } 95bd8f1dc3Sbluhm if (i == 0) 96bd8f1dc3Sbluhm fail("Parser ignored failing allocator"); 97bd8f1dc3Sbluhm else if (i == max_alloc_count) 98bd8f1dc3Sbluhm fail("Parser not created with max allocation count"); 99bd8f1dc3Sbluhm } 100bd8f1dc3Sbluhm END_TEST 101bd8f1dc3Sbluhm 102bd8f1dc3Sbluhm /* Test that freeing a NULL parser doesn't cause an explosion. 103bd8f1dc3Sbluhm * (Not actually tested anywhere else) 104bd8f1dc3Sbluhm */ 105bd8f1dc3Sbluhm START_TEST(test_misc_null_parser) { 106bd8f1dc3Sbluhm XML_ParserFree(NULL); 107bd8f1dc3Sbluhm } 108bd8f1dc3Sbluhm END_TEST 109bd8f1dc3Sbluhm 110bd8f1dc3Sbluhm #if defined(__has_feature) 111bd8f1dc3Sbluhm # if __has_feature(undefined_behavior_sanitizer) 112bd8f1dc3Sbluhm # define EXPAT_TESTS_UBSAN 1 113bd8f1dc3Sbluhm # else 114bd8f1dc3Sbluhm # define EXPAT_TESTS_UBSAN 0 115bd8f1dc3Sbluhm # endif 116bd8f1dc3Sbluhm #else 117bd8f1dc3Sbluhm # define EXPAT_TESTS_UBSAN 0 118bd8f1dc3Sbluhm #endif 119bd8f1dc3Sbluhm 120bd8f1dc3Sbluhm /* Test that XML_ErrorString rejects out-of-range codes */ 121bd8f1dc3Sbluhm START_TEST(test_misc_error_string) { 122bd8f1dc3Sbluhm #if ! EXPAT_TESTS_UBSAN // because this would trigger UBSan 123bd8f1dc3Sbluhm union { 124bd8f1dc3Sbluhm enum XML_Error xml_error; 125bd8f1dc3Sbluhm int integer; 126bd8f1dc3Sbluhm } trickery; 127bd8f1dc3Sbluhm 128bd8f1dc3Sbluhm assert_true(sizeof(enum XML_Error) == sizeof(int)); // self-test 129bd8f1dc3Sbluhm 130bd8f1dc3Sbluhm trickery.integer = -1; 131bd8f1dc3Sbluhm if (XML_ErrorString(trickery.xml_error) != NULL) 132bd8f1dc3Sbluhm fail("Negative error code not rejected"); 133bd8f1dc3Sbluhm 134bd8f1dc3Sbluhm trickery.integer = 100; 135bd8f1dc3Sbluhm if (XML_ErrorString(trickery.xml_error) != NULL) 136bd8f1dc3Sbluhm fail("Large error code not rejected"); 137bd8f1dc3Sbluhm #endif 138bd8f1dc3Sbluhm } 139bd8f1dc3Sbluhm END_TEST 140bd8f1dc3Sbluhm 141bd8f1dc3Sbluhm /* Test the version information is consistent */ 142bd8f1dc3Sbluhm 143bd8f1dc3Sbluhm /* Since we are working in XML_LChars (potentially 16-bits), we 144bd8f1dc3Sbluhm * can't use the standard C library functions for character 145bd8f1dc3Sbluhm * manipulation and have to roll our own. 146bd8f1dc3Sbluhm */ 147bd8f1dc3Sbluhm static int 148bd8f1dc3Sbluhm parse_version(const XML_LChar *version_text, 149bd8f1dc3Sbluhm XML_Expat_Version *version_struct) { 150bd8f1dc3Sbluhm if (! version_text) 151bd8f1dc3Sbluhm return XML_FALSE; 152bd8f1dc3Sbluhm 153bd8f1dc3Sbluhm while (*version_text != 0x00) { 154bd8f1dc3Sbluhm if (*version_text >= ASCII_0 && *version_text <= ASCII_9) 155bd8f1dc3Sbluhm break; 156bd8f1dc3Sbluhm version_text++; 157bd8f1dc3Sbluhm } 158bd8f1dc3Sbluhm if (*version_text == 0x00) 159bd8f1dc3Sbluhm return XML_FALSE; 160bd8f1dc3Sbluhm 161bd8f1dc3Sbluhm /* version_struct->major = strtoul(version_text, 10, &version_text) */ 162bd8f1dc3Sbluhm version_struct->major = 0; 163bd8f1dc3Sbluhm while (*version_text >= ASCII_0 && *version_text <= ASCII_9) { 164bd8f1dc3Sbluhm version_struct->major 165bd8f1dc3Sbluhm = 10 * version_struct->major + (*version_text++ - ASCII_0); 166bd8f1dc3Sbluhm } 167bd8f1dc3Sbluhm if (*version_text++ != ASCII_PERIOD) 168bd8f1dc3Sbluhm return XML_FALSE; 169bd8f1dc3Sbluhm 170bd8f1dc3Sbluhm /* Now for the minor version number */ 171bd8f1dc3Sbluhm version_struct->minor = 0; 172bd8f1dc3Sbluhm while (*version_text >= ASCII_0 && *version_text <= ASCII_9) { 173bd8f1dc3Sbluhm version_struct->minor 174bd8f1dc3Sbluhm = 10 * version_struct->minor + (*version_text++ - ASCII_0); 175bd8f1dc3Sbluhm } 176bd8f1dc3Sbluhm if (*version_text++ != ASCII_PERIOD) 177bd8f1dc3Sbluhm return XML_FALSE; 178bd8f1dc3Sbluhm 179bd8f1dc3Sbluhm /* Finally the micro version number */ 180bd8f1dc3Sbluhm version_struct->micro = 0; 181bd8f1dc3Sbluhm while (*version_text >= ASCII_0 && *version_text <= ASCII_9) { 182bd8f1dc3Sbluhm version_struct->micro 183bd8f1dc3Sbluhm = 10 * version_struct->micro + (*version_text++ - ASCII_0); 184bd8f1dc3Sbluhm } 185bd8f1dc3Sbluhm if (*version_text != 0x00) 186bd8f1dc3Sbluhm return XML_FALSE; 187bd8f1dc3Sbluhm return XML_TRUE; 188bd8f1dc3Sbluhm } 189bd8f1dc3Sbluhm 190bd8f1dc3Sbluhm static int 191bd8f1dc3Sbluhm versions_equal(const XML_Expat_Version *first, 192bd8f1dc3Sbluhm const XML_Expat_Version *second) { 193bd8f1dc3Sbluhm return (first->major == second->major && first->minor == second->minor 194bd8f1dc3Sbluhm && first->micro == second->micro); 195bd8f1dc3Sbluhm } 196bd8f1dc3Sbluhm 197bd8f1dc3Sbluhm START_TEST(test_misc_version) { 198bd8f1dc3Sbluhm XML_Expat_Version read_version = XML_ExpatVersionInfo(); 199bd8f1dc3Sbluhm /* Silence compiler warning with the following assignment */ 200bd8f1dc3Sbluhm XML_Expat_Version parsed_version = {0, 0, 0}; 201bd8f1dc3Sbluhm const XML_LChar *version_text = XML_ExpatVersion(); 202bd8f1dc3Sbluhm 203bd8f1dc3Sbluhm if (version_text == NULL) 204bd8f1dc3Sbluhm fail("Could not obtain version text"); 205bd8f1dc3Sbluhm assert(version_text != NULL); 206bd8f1dc3Sbluhm if (! parse_version(version_text, &parsed_version)) 207bd8f1dc3Sbluhm fail("Unable to parse version text"); 208bd8f1dc3Sbluhm if (! versions_equal(&read_version, &parsed_version)) 209bd8f1dc3Sbluhm fail("Version mismatch"); 210bd8f1dc3Sbluhm 211*aa071e6eSbluhm if (xcstrcmp(version_text, XCS("expat_2.6.4"))) /* needs bump on releases */ 212bd8f1dc3Sbluhm fail("XML_*_VERSION in expat.h out of sync?\n"); 213bd8f1dc3Sbluhm } 214bd8f1dc3Sbluhm END_TEST 215bd8f1dc3Sbluhm 216bd8f1dc3Sbluhm /* Test feature information */ 217bd8f1dc3Sbluhm START_TEST(test_misc_features) { 218bd8f1dc3Sbluhm const XML_Feature *features = XML_GetFeatureList(); 219bd8f1dc3Sbluhm 220bd8f1dc3Sbluhm /* Prevent problems with double-freeing parsers */ 221bd8f1dc3Sbluhm g_parser = NULL; 222bd8f1dc3Sbluhm if (features == NULL) { 223bd8f1dc3Sbluhm fail("Failed to get feature information"); 224bd8f1dc3Sbluhm } else { 225bd8f1dc3Sbluhm /* Loop through the features checking what we can */ 226bd8f1dc3Sbluhm while (features->feature != XML_FEATURE_END) { 227bd8f1dc3Sbluhm switch (features->feature) { 228bd8f1dc3Sbluhm case XML_FEATURE_SIZEOF_XML_CHAR: 229bd8f1dc3Sbluhm if (features->value != sizeof(XML_Char)) 230bd8f1dc3Sbluhm fail("Incorrect size of XML_Char"); 231bd8f1dc3Sbluhm break; 232bd8f1dc3Sbluhm case XML_FEATURE_SIZEOF_XML_LCHAR: 233bd8f1dc3Sbluhm if (features->value != sizeof(XML_LChar)) 234bd8f1dc3Sbluhm fail("Incorrect size of XML_LChar"); 235bd8f1dc3Sbluhm break; 236bd8f1dc3Sbluhm default: 237bd8f1dc3Sbluhm break; 238bd8f1dc3Sbluhm } 239bd8f1dc3Sbluhm features++; 240bd8f1dc3Sbluhm } 241bd8f1dc3Sbluhm } 242bd8f1dc3Sbluhm } 243bd8f1dc3Sbluhm END_TEST 244bd8f1dc3Sbluhm 245bd8f1dc3Sbluhm /* Regression test for GitHub Issue #17: memory leak parsing attribute 246bd8f1dc3Sbluhm * values with mixed bound and unbound namespaces. 247bd8f1dc3Sbluhm */ 248bd8f1dc3Sbluhm START_TEST(test_misc_attribute_leak) { 249bd8f1dc3Sbluhm const char *text = "<D xmlns:L=\"D\" l:a='' L:a=''/>"; 250bd8f1dc3Sbluhm XML_Memory_Handling_Suite memsuite 251bd8f1dc3Sbluhm = {tracking_malloc, tracking_realloc, tracking_free}; 252bd8f1dc3Sbluhm 253bd8f1dc3Sbluhm g_parser = XML_ParserCreate_MM(XCS("UTF-8"), &memsuite, XCS("\n")); 254bd8f1dc3Sbluhm expect_failure(text, XML_ERROR_UNBOUND_PREFIX, "Unbound prefixes not found"); 255bd8f1dc3Sbluhm XML_ParserFree(g_parser); 256bd8f1dc3Sbluhm /* Prevent the teardown trying to double free */ 257bd8f1dc3Sbluhm g_parser = NULL; 258bd8f1dc3Sbluhm 259bd8f1dc3Sbluhm if (! tracking_report()) 260bd8f1dc3Sbluhm fail("Memory leak found"); 261bd8f1dc3Sbluhm } 262bd8f1dc3Sbluhm END_TEST 263bd8f1dc3Sbluhm 264bd8f1dc3Sbluhm /* Test parser created for UTF-16LE is successful */ 265bd8f1dc3Sbluhm START_TEST(test_misc_utf16le) { 266bd8f1dc3Sbluhm const char text[] = 267bd8f1dc3Sbluhm /* <?xml version='1.0'?><q>Hi</q> */ 268bd8f1dc3Sbluhm "<\0?\0x\0m\0l\0 \0" 269bd8f1dc3Sbluhm "v\0e\0r\0s\0i\0o\0n\0=\0'\0\x31\0.\0\x30\0'\0?\0>\0" 270bd8f1dc3Sbluhm "<\0q\0>\0H\0i\0<\0/\0q\0>\0"; 271bd8f1dc3Sbluhm const XML_Char *expected = XCS("Hi"); 272bd8f1dc3Sbluhm CharData storage; 273bd8f1dc3Sbluhm 274bd8f1dc3Sbluhm g_parser = XML_ParserCreate(XCS("UTF-16LE")); 275bd8f1dc3Sbluhm if (g_parser == NULL) 276bd8f1dc3Sbluhm fail("Parser not created"); 277bd8f1dc3Sbluhm 278bd8f1dc3Sbluhm CharData_Init(&storage); 279bd8f1dc3Sbluhm XML_SetUserData(g_parser, &storage); 280bd8f1dc3Sbluhm XML_SetCharacterDataHandler(g_parser, accumulate_characters); 281bd8f1dc3Sbluhm if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)sizeof(text) - 1, XML_TRUE) 282bd8f1dc3Sbluhm == XML_STATUS_ERROR) 283bd8f1dc3Sbluhm xml_failure(g_parser); 284bd8f1dc3Sbluhm CharData_CheckXMLChars(&storage, expected); 285bd8f1dc3Sbluhm } 286bd8f1dc3Sbluhm END_TEST 287bd8f1dc3Sbluhm 288bd8f1dc3Sbluhm START_TEST(test_misc_stop_during_end_handler_issue_240_1) { 289bd8f1dc3Sbluhm XML_Parser parser; 290bd8f1dc3Sbluhm DataIssue240 *mydata; 291bd8f1dc3Sbluhm enum XML_Status result; 292bd8f1dc3Sbluhm const char *const doc1 = "<doc><e1/><e><foo/></e></doc>"; 293bd8f1dc3Sbluhm 294bd8f1dc3Sbluhm parser = XML_ParserCreate(NULL); 295bd8f1dc3Sbluhm XML_SetElementHandler(parser, start_element_issue_240, end_element_issue_240); 296bd8f1dc3Sbluhm mydata = (DataIssue240 *)malloc(sizeof(DataIssue240)); 297bd8f1dc3Sbluhm mydata->parser = parser; 298bd8f1dc3Sbluhm mydata->deep = 0; 299bd8f1dc3Sbluhm XML_SetUserData(parser, mydata); 300bd8f1dc3Sbluhm 301bd8f1dc3Sbluhm result = _XML_Parse_SINGLE_BYTES(parser, doc1, (int)strlen(doc1), 1); 302bd8f1dc3Sbluhm XML_ParserFree(parser); 303bd8f1dc3Sbluhm free(mydata); 304bd8f1dc3Sbluhm if (result != XML_STATUS_ERROR) 305bd8f1dc3Sbluhm fail("Stopping the parser did not work as expected"); 306bd8f1dc3Sbluhm } 307bd8f1dc3Sbluhm END_TEST 308bd8f1dc3Sbluhm 309bd8f1dc3Sbluhm START_TEST(test_misc_stop_during_end_handler_issue_240_2) { 310bd8f1dc3Sbluhm XML_Parser parser; 311bd8f1dc3Sbluhm DataIssue240 *mydata; 312bd8f1dc3Sbluhm enum XML_Status result; 313bd8f1dc3Sbluhm const char *const doc2 = "<doc><elem/></doc>"; 314bd8f1dc3Sbluhm 315bd8f1dc3Sbluhm parser = XML_ParserCreate(NULL); 316bd8f1dc3Sbluhm XML_SetElementHandler(parser, start_element_issue_240, end_element_issue_240); 317bd8f1dc3Sbluhm mydata = (DataIssue240 *)malloc(sizeof(DataIssue240)); 318bd8f1dc3Sbluhm mydata->parser = parser; 319bd8f1dc3Sbluhm mydata->deep = 0; 320bd8f1dc3Sbluhm XML_SetUserData(parser, mydata); 321bd8f1dc3Sbluhm 322bd8f1dc3Sbluhm result = _XML_Parse_SINGLE_BYTES(parser, doc2, (int)strlen(doc2), 1); 323bd8f1dc3Sbluhm XML_ParserFree(parser); 324bd8f1dc3Sbluhm free(mydata); 325bd8f1dc3Sbluhm if (result != XML_STATUS_ERROR) 326bd8f1dc3Sbluhm fail("Stopping the parser did not work as expected"); 327bd8f1dc3Sbluhm } 328bd8f1dc3Sbluhm END_TEST 329bd8f1dc3Sbluhm 330bd8f1dc3Sbluhm START_TEST(test_misc_deny_internal_entity_closing_doctype_issue_317) { 331bd8f1dc3Sbluhm const char *const inputOne = "<!DOCTYPE d [\n" 332bd8f1dc3Sbluhm "<!ENTITY % e ']><d/>'>\n" 333bd8f1dc3Sbluhm "\n" 334bd8f1dc3Sbluhm "%e;"; 335*aa071e6eSbluhm const char *const inputTwo 336*aa071e6eSbluhm = "<!DOCTYPE d [\n" 337*aa071e6eSbluhm "<!ENTITY % e1 ']><d/>'><!ENTITY % e2 '%e1;'>\n" 338bd8f1dc3Sbluhm "\n" 339bd8f1dc3Sbluhm "%e2;"; 340bd8f1dc3Sbluhm const char *const inputThree = "<!DOCTYPE d [\n" 341bd8f1dc3Sbluhm "<!ENTITY % e ']><d'>\n" 342bd8f1dc3Sbluhm "\n" 343*aa071e6eSbluhm "%e;/>"; 344bd8f1dc3Sbluhm const char *const inputIssue317 = "<!DOCTYPE doc [\n" 345bd8f1dc3Sbluhm "<!ENTITY % foo ']>\n" 346bd8f1dc3Sbluhm "<doc>Hell<oc (#PCDATA)*>'>\n" 347bd8f1dc3Sbluhm "%foo;\n" 348bd8f1dc3Sbluhm "]>\n" 349bd8f1dc3Sbluhm "<doc>Hello, world</dVc>"; 350bd8f1dc3Sbluhm 351bd8f1dc3Sbluhm const char *const inputs[] = {inputOne, inputTwo, inputThree, inputIssue317}; 352bd8f1dc3Sbluhm size_t inputIndex = 0; 353bd8f1dc3Sbluhm 354bd8f1dc3Sbluhm for (; inputIndex < sizeof(inputs) / sizeof(inputs[0]); inputIndex++) { 355bd8f1dc3Sbluhm set_subtest("%s", inputs[inputIndex]); 356bd8f1dc3Sbluhm XML_Parser parser; 357bd8f1dc3Sbluhm enum XML_Status parseResult; 358bd8f1dc3Sbluhm int setParamEntityResult; 359bd8f1dc3Sbluhm XML_Size lineNumber; 360bd8f1dc3Sbluhm XML_Size columnNumber; 361bd8f1dc3Sbluhm const char *const input = inputs[inputIndex]; 362bd8f1dc3Sbluhm 363bd8f1dc3Sbluhm parser = XML_ParserCreate(NULL); 364bd8f1dc3Sbluhm setParamEntityResult 365bd8f1dc3Sbluhm = XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS); 366bd8f1dc3Sbluhm if (setParamEntityResult != 1) 367bd8f1dc3Sbluhm fail("Failed to set XML_PARAM_ENTITY_PARSING_ALWAYS."); 368bd8f1dc3Sbluhm 369bd8f1dc3Sbluhm parseResult = _XML_Parse_SINGLE_BYTES(parser, input, (int)strlen(input), 0); 370bd8f1dc3Sbluhm if (parseResult != XML_STATUS_ERROR) { 371bd8f1dc3Sbluhm parseResult = _XML_Parse_SINGLE_BYTES(parser, "", 0, 1); 372bd8f1dc3Sbluhm if (parseResult != XML_STATUS_ERROR) { 373bd8f1dc3Sbluhm fail("Parsing was expected to fail but succeeded."); 374bd8f1dc3Sbluhm } 375bd8f1dc3Sbluhm } 376bd8f1dc3Sbluhm 377bd8f1dc3Sbluhm if (XML_GetErrorCode(parser) != XML_ERROR_INVALID_TOKEN) 378bd8f1dc3Sbluhm fail("Error code does not match XML_ERROR_INVALID_TOKEN"); 379bd8f1dc3Sbluhm 380bd8f1dc3Sbluhm lineNumber = XML_GetCurrentLineNumber(parser); 381bd8f1dc3Sbluhm if (lineNumber != 4) 382bd8f1dc3Sbluhm fail("XML_GetCurrentLineNumber does not work as expected."); 383bd8f1dc3Sbluhm 384bd8f1dc3Sbluhm columnNumber = XML_GetCurrentColumnNumber(parser); 385bd8f1dc3Sbluhm if (columnNumber != 0) 386bd8f1dc3Sbluhm fail("XML_GetCurrentColumnNumber does not work as expected."); 387bd8f1dc3Sbluhm 388bd8f1dc3Sbluhm XML_ParserFree(parser); 389bd8f1dc3Sbluhm } 390bd8f1dc3Sbluhm } 391bd8f1dc3Sbluhm END_TEST 392bd8f1dc3Sbluhm 393bd8f1dc3Sbluhm START_TEST(test_misc_tag_mismatch_reset_leak) { 394bd8f1dc3Sbluhm #ifdef XML_NS 395bd8f1dc3Sbluhm const char *const text = "<open xmlns='https://namespace1.test'></close>"; 396bd8f1dc3Sbluhm XML_Parser parser = XML_ParserCreateNS(NULL, XCS('\n')); 397bd8f1dc3Sbluhm 398bd8f1dc3Sbluhm if (_XML_Parse_SINGLE_BYTES(parser, text, (int)strlen(text), XML_TRUE) 399bd8f1dc3Sbluhm != XML_STATUS_ERROR) 400bd8f1dc3Sbluhm fail("Call to parse was expected to fail"); 401bd8f1dc3Sbluhm if (XML_GetErrorCode(parser) != XML_ERROR_TAG_MISMATCH) 402bd8f1dc3Sbluhm fail("Call to parse was expected to fail from a closing tag mismatch"); 403bd8f1dc3Sbluhm 404bd8f1dc3Sbluhm XML_ParserReset(parser, NULL); 405bd8f1dc3Sbluhm 406bd8f1dc3Sbluhm if (_XML_Parse_SINGLE_BYTES(parser, text, (int)strlen(text), XML_TRUE) 407bd8f1dc3Sbluhm != XML_STATUS_ERROR) 408bd8f1dc3Sbluhm fail("Call to parse was expected to fail"); 409bd8f1dc3Sbluhm if (XML_GetErrorCode(parser) != XML_ERROR_TAG_MISMATCH) 410bd8f1dc3Sbluhm fail("Call to parse was expected to fail from a closing tag mismatch"); 411bd8f1dc3Sbluhm 412bd8f1dc3Sbluhm XML_ParserFree(parser); 413bd8f1dc3Sbluhm #endif 414bd8f1dc3Sbluhm } 415bd8f1dc3Sbluhm END_TEST 416bd8f1dc3Sbluhm 417bd8f1dc3Sbluhm START_TEST(test_misc_create_external_entity_parser_with_null_context) { 418bd8f1dc3Sbluhm // With XML_DTD undefined, the only supported case of external entities 419bd8f1dc3Sbluhm // is pattern "<!ENTITY entity123 SYSTEM 'filename123'>". A NULL context 420bd8f1dc3Sbluhm // was causing a segfault through a null pointer dereference in function 421bd8f1dc3Sbluhm // setContext, previously. 422bd8f1dc3Sbluhm XML_Parser parser = XML_ParserCreate(NULL); 423bd8f1dc3Sbluhm XML_Parser ext_parser = XML_ExternalEntityParserCreate(parser, NULL, NULL); 424bd8f1dc3Sbluhm #ifdef XML_DTD 425bd8f1dc3Sbluhm assert_true(ext_parser != NULL); 426bd8f1dc3Sbluhm XML_ParserFree(ext_parser); 427bd8f1dc3Sbluhm #else 428bd8f1dc3Sbluhm assert_true(ext_parser == NULL); 429bd8f1dc3Sbluhm #endif /* XML_DTD */ 430bd8f1dc3Sbluhm XML_ParserFree(parser); 431bd8f1dc3Sbluhm } 432bd8f1dc3Sbluhm END_TEST 433bd8f1dc3Sbluhm 434bd8f1dc3Sbluhm START_TEST(test_misc_general_entities_support) { 435bd8f1dc3Sbluhm const char *const doc 436bd8f1dc3Sbluhm = "<!DOCTYPE r [\n" 437bd8f1dc3Sbluhm "<!ENTITY e1 'v1'>\n" 438bd8f1dc3Sbluhm "<!ENTITY e2 SYSTEM 'v2'>\n" 439bd8f1dc3Sbluhm "]>\n" 440bd8f1dc3Sbluhm "<r a1='[&e1;]'>[&e1;][&e2;][&'><"]</r>"; 441bd8f1dc3Sbluhm 442bd8f1dc3Sbluhm CharData storage; 443bd8f1dc3Sbluhm CharData_Init(&storage); 444bd8f1dc3Sbluhm 445bd8f1dc3Sbluhm XML_Parser parser = XML_ParserCreate(NULL); 446bd8f1dc3Sbluhm XML_SetUserData(parser, &storage); 447bd8f1dc3Sbluhm XML_SetStartElementHandler(parser, accumulate_start_element); 448bd8f1dc3Sbluhm XML_SetExternalEntityRefHandler(parser, 449bd8f1dc3Sbluhm external_entity_failer__if_not_xml_ge); 450bd8f1dc3Sbluhm XML_SetEntityDeclHandler(parser, accumulate_entity_decl); 451*aa071e6eSbluhm XML_SetCharacterDataHandler(parser, accumulate_characters); 452bd8f1dc3Sbluhm 453bd8f1dc3Sbluhm if (_XML_Parse_SINGLE_BYTES(parser, doc, (int)strlen(doc), XML_TRUE) 454bd8f1dc3Sbluhm != XML_STATUS_OK) { 455bd8f1dc3Sbluhm xml_failure(parser); 456bd8f1dc3Sbluhm } 457bd8f1dc3Sbluhm 458bd8f1dc3Sbluhm XML_ParserFree(parser); 459bd8f1dc3Sbluhm 460bd8f1dc3Sbluhm CharData_CheckXMLChars(&storage, 461bd8f1dc3Sbluhm /* clang-format off */ 462bd8f1dc3Sbluhm #if XML_GE == 1 463bd8f1dc3Sbluhm XCS("e1=v1\n") 464bd8f1dc3Sbluhm XCS("e2=(null)\n") 465bd8f1dc3Sbluhm XCS("(r(a1=[v1]))\n") 466bd8f1dc3Sbluhm XCS("[v1][][&'><\"]") 467bd8f1dc3Sbluhm #else 468bd8f1dc3Sbluhm XCS("e1=&e1;\n") 469bd8f1dc3Sbluhm XCS("e2=(null)\n") 470bd8f1dc3Sbluhm XCS("(r(a1=[&e1;]))\n") 471bd8f1dc3Sbluhm XCS("[&e1;][&e2;][&'><\"]") 472bd8f1dc3Sbluhm #endif 473bd8f1dc3Sbluhm ); 474bd8f1dc3Sbluhm /* clang-format on */ 475bd8f1dc3Sbluhm } 476bd8f1dc3Sbluhm END_TEST 477bd8f1dc3Sbluhm 478bd8f1dc3Sbluhm static void XMLCALL 479bd8f1dc3Sbluhm resumable_stopping_character_handler(void *userData, const XML_Char *s, 480bd8f1dc3Sbluhm int len) { 481bd8f1dc3Sbluhm UNUSED_P(s); 482bd8f1dc3Sbluhm UNUSED_P(len); 483bd8f1dc3Sbluhm XML_Parser parser = (XML_Parser)userData; 484bd8f1dc3Sbluhm XML_StopParser(parser, XML_TRUE); 485bd8f1dc3Sbluhm } 486bd8f1dc3Sbluhm 487bd8f1dc3Sbluhm // NOTE: This test needs active LeakSanitizer to be of actual use 488bd8f1dc3Sbluhm START_TEST(test_misc_char_handler_stop_without_leak) { 489bd8f1dc3Sbluhm const char *const data 490bd8f1dc3Sbluhm = "<!DOCTYPE t1[<!ENTITY e1 'angle<'><!ENTITY e2 '&e1;'>]><t1>&e2;"; 491bd8f1dc3Sbluhm XML_Parser parser = XML_ParserCreate(NULL); 492bd8f1dc3Sbluhm assert_true(parser != NULL); 493bd8f1dc3Sbluhm XML_SetUserData(parser, parser); 494bd8f1dc3Sbluhm XML_SetCharacterDataHandler(parser, resumable_stopping_character_handler); 495bd8f1dc3Sbluhm _XML_Parse_SINGLE_BYTES(parser, data, (int)strlen(data), XML_FALSE); 496bd8f1dc3Sbluhm XML_ParserFree(parser); 497bd8f1dc3Sbluhm } 498bd8f1dc3Sbluhm END_TEST 499bd8f1dc3Sbluhm 500*aa071e6eSbluhm START_TEST(test_misc_resumeparser_not_crashing) { 501*aa071e6eSbluhm XML_Parser parser = XML_ParserCreate(NULL); 502*aa071e6eSbluhm XML_GetBuffer(parser, 1); 503*aa071e6eSbluhm XML_StopParser(parser, /*resumable=*/XML_TRUE); 504*aa071e6eSbluhm XML_ResumeParser(parser); // could crash here, previously 505*aa071e6eSbluhm XML_ParserFree(parser); 506*aa071e6eSbluhm } 507*aa071e6eSbluhm END_TEST 508*aa071e6eSbluhm 509*aa071e6eSbluhm START_TEST(test_misc_stopparser_rejects_unstarted_parser) { 510*aa071e6eSbluhm const XML_Bool cases[] = {XML_TRUE, XML_FALSE}; 511*aa071e6eSbluhm for (size_t i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) { 512*aa071e6eSbluhm const XML_Bool resumable = cases[i]; 513*aa071e6eSbluhm XML_Parser parser = XML_ParserCreate(NULL); 514*aa071e6eSbluhm assert_true(XML_GetErrorCode(parser) == XML_ERROR_NONE); 515*aa071e6eSbluhm assert_true(XML_StopParser(parser, resumable) == XML_STATUS_ERROR); 516*aa071e6eSbluhm assert_true(XML_GetErrorCode(parser) == XML_ERROR_NOT_STARTED); 517*aa071e6eSbluhm XML_ParserFree(parser); 518*aa071e6eSbluhm } 519*aa071e6eSbluhm } 520*aa071e6eSbluhm END_TEST 521*aa071e6eSbluhm 522bd8f1dc3Sbluhm void 523bd8f1dc3Sbluhm make_miscellaneous_test_case(Suite *s) { 524bd8f1dc3Sbluhm TCase *tc_misc = tcase_create("miscellaneous tests"); 525bd8f1dc3Sbluhm 526bd8f1dc3Sbluhm suite_add_tcase(s, tc_misc); 527bd8f1dc3Sbluhm tcase_add_checked_fixture(tc_misc, NULL, basic_teardown); 528bd8f1dc3Sbluhm 529bd8f1dc3Sbluhm tcase_add_test(tc_misc, test_misc_alloc_create_parser); 530bd8f1dc3Sbluhm tcase_add_test(tc_misc, test_misc_alloc_create_parser_with_encoding); 531bd8f1dc3Sbluhm tcase_add_test(tc_misc, test_misc_null_parser); 532bd8f1dc3Sbluhm tcase_add_test(tc_misc, test_misc_error_string); 533bd8f1dc3Sbluhm tcase_add_test(tc_misc, test_misc_version); 534bd8f1dc3Sbluhm tcase_add_test(tc_misc, test_misc_features); 535bd8f1dc3Sbluhm tcase_add_test(tc_misc, test_misc_attribute_leak); 536bd8f1dc3Sbluhm tcase_add_test(tc_misc, test_misc_utf16le); 537bd8f1dc3Sbluhm tcase_add_test(tc_misc, test_misc_stop_during_end_handler_issue_240_1); 538bd8f1dc3Sbluhm tcase_add_test(tc_misc, test_misc_stop_during_end_handler_issue_240_2); 539bd8f1dc3Sbluhm tcase_add_test__ifdef_xml_dtd( 540bd8f1dc3Sbluhm tc_misc, test_misc_deny_internal_entity_closing_doctype_issue_317); 541bd8f1dc3Sbluhm tcase_add_test(tc_misc, test_misc_tag_mismatch_reset_leak); 542bd8f1dc3Sbluhm tcase_add_test(tc_misc, 543bd8f1dc3Sbluhm test_misc_create_external_entity_parser_with_null_context); 544bd8f1dc3Sbluhm tcase_add_test(tc_misc, test_misc_general_entities_support); 545bd8f1dc3Sbluhm tcase_add_test(tc_misc, test_misc_char_handler_stop_without_leak); 546*aa071e6eSbluhm tcase_add_test(tc_misc, test_misc_resumeparser_not_crashing); 547*aa071e6eSbluhm tcase_add_test(tc_misc, test_misc_stopparser_rejects_unstarted_parser); 548bd8f1dc3Sbluhm } 549