1 /* Tests in the "miscellaneous" test case for the Expat test suite 2 __ __ _ 3 ___\ \/ /_ __ __ _| |_ 4 / _ \\ /| '_ \ / _` | __| 5 | __// \| |_) | (_| | |_ 6 \___/_/\_\ .__/ \__,_|\__| 7 |_| XML parser 8 9 Copyright (c) 2001-2006 Fred L. Drake, Jr. <fdrake@users.sourceforge.net> 10 Copyright (c) 2003 Greg Stein <gstein@users.sourceforge.net> 11 Copyright (c) 2005-2007 Steven Solie <steven@solie.ca> 12 Copyright (c) 2005-2012 Karl Waclawek <karl@waclawek.net> 13 Copyright (c) 2016-2025 Sebastian Pipping <sebastian@pipping.org> 14 Copyright (c) 2017-2022 Rhodri James <rhodri@wildebeest.org.uk> 15 Copyright (c) 2017 Joe Orton <jorton@redhat.com> 16 Copyright (c) 2017 José Gutiérrez de la Concha <jose@zeroc.com> 17 Copyright (c) 2018 Marco Maggi <marco.maggi-ipsu@poste.it> 18 Copyright (c) 2019 David Loffredo <loffredo@steptools.com> 19 Copyright (c) 2020 Tim Gates <tim.gates@iress.com> 20 Copyright (c) 2021 Donghee Na <donghee.na@python.org> 21 Copyright (c) 2023 Sony Corporation / Snild Dolkow <snild@sony.com> 22 Licensed under the MIT license: 23 24 Permission is hereby granted, free of charge, to any person obtaining 25 a copy of this software and associated documentation files (the 26 "Software"), to deal in the Software without restriction, including 27 without limitation the rights to use, copy, modify, merge, publish, 28 distribute, sublicense, and/or sell copies of the Software, and to permit 29 persons to whom the Software is furnished to do so, subject to the 30 following conditions: 31 32 The above copyright notice and this permission notice shall be included 33 in all copies or substantial portions of the Software. 34 35 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 36 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 37 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 38 NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 39 DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 40 OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 41 USE OR OTHER DEALINGS IN THE SOFTWARE. 42 */ 43 44 #if defined(NDEBUG) 45 # undef NDEBUG /* because test suite relies on assert(...) at the moment */ 46 #endif 47 48 #include <assert.h> 49 #include <string.h> 50 51 #include "expat_config.h" 52 53 #include "expat.h" 54 #include "internal.h" 55 #include "minicheck.h" 56 #include "memcheck.h" 57 #include "common.h" 58 #include "ascii.h" /* for ASCII_xxx */ 59 #include "handlers.h" 60 #include "misc_tests.h" 61 62 void XMLCALL accumulate_characters_ext_handler(void *userData, 63 const XML_Char *s, int len); 64 65 /* Test that a failure to allocate the parser structure fails gracefully */ 66 START_TEST(test_misc_alloc_create_parser) { 67 XML_Memory_Handling_Suite memsuite = {duff_allocator, realloc, free}; 68 unsigned int i; 69 const unsigned int max_alloc_count = 10; 70 71 /* Something this simple shouldn't need more than 10 allocations */ 72 for (i = 0; i < max_alloc_count; i++) { 73 g_allocation_count = (int)i; 74 g_parser = XML_ParserCreate_MM(NULL, &memsuite, NULL); 75 if (g_parser != NULL) 76 break; 77 } 78 if (i == 0) 79 fail("Parser unexpectedly ignored failing allocator"); 80 else if (i == max_alloc_count) 81 fail("Parser not created with max allocation count"); 82 } 83 END_TEST 84 85 /* Test memory allocation failures for a parser with an encoding */ 86 START_TEST(test_misc_alloc_create_parser_with_encoding) { 87 XML_Memory_Handling_Suite memsuite = {duff_allocator, realloc, free}; 88 unsigned int i; 89 const unsigned int max_alloc_count = 10; 90 91 /* Try several levels of allocation */ 92 for (i = 0; i < max_alloc_count; i++) { 93 g_allocation_count = (int)i; 94 g_parser = XML_ParserCreate_MM(XCS("us-ascii"), &memsuite, NULL); 95 if (g_parser != NULL) 96 break; 97 } 98 if (i == 0) 99 fail("Parser ignored failing allocator"); 100 else if (i == max_alloc_count) 101 fail("Parser not created with max allocation count"); 102 } 103 END_TEST 104 105 /* Test that freeing a NULL parser doesn't cause an explosion. 106 * (Not actually tested anywhere else) 107 */ 108 START_TEST(test_misc_null_parser) { 109 XML_ParserFree(NULL); 110 } 111 END_TEST 112 113 #if defined(__has_feature) 114 # if __has_feature(undefined_behavior_sanitizer) 115 # define EXPAT_TESTS_UBSAN 1 116 # else 117 # define EXPAT_TESTS_UBSAN 0 118 # endif 119 #else 120 # define EXPAT_TESTS_UBSAN 0 121 #endif 122 123 /* Test that XML_ErrorString rejects out-of-range codes */ 124 START_TEST(test_misc_error_string) { 125 #if ! EXPAT_TESTS_UBSAN // because this would trigger UBSan 126 union { 127 enum XML_Error xml_error; 128 int integer; 129 } trickery; 130 131 assert_true(sizeof(enum XML_Error) == sizeof(int)); // self-test 132 133 trickery.integer = -1; 134 if (XML_ErrorString(trickery.xml_error) != NULL) 135 fail("Negative error code not rejected"); 136 137 trickery.integer = 100; 138 if (XML_ErrorString(trickery.xml_error) != NULL) 139 fail("Large error code not rejected"); 140 #endif 141 } 142 END_TEST 143 144 /* Test the version information is consistent */ 145 146 /* Since we are working in XML_LChars (potentially 16-bits), we 147 * can't use the standard C library functions for character 148 * manipulation and have to roll our own. 149 */ 150 static int 151 parse_version(const XML_LChar *version_text, 152 XML_Expat_Version *version_struct) { 153 if (! version_text) 154 return XML_FALSE; 155 156 while (*version_text != 0x00) { 157 if (*version_text >= ASCII_0 && *version_text <= ASCII_9) 158 break; 159 version_text++; 160 } 161 if (*version_text == 0x00) 162 return XML_FALSE; 163 164 /* version_struct->major = strtoul(version_text, 10, &version_text) */ 165 version_struct->major = 0; 166 while (*version_text >= ASCII_0 && *version_text <= ASCII_9) { 167 version_struct->major 168 = 10 * version_struct->major + (*version_text++ - ASCII_0); 169 } 170 if (*version_text++ != ASCII_PERIOD) 171 return XML_FALSE; 172 173 /* Now for the minor version number */ 174 version_struct->minor = 0; 175 while (*version_text >= ASCII_0 && *version_text <= ASCII_9) { 176 version_struct->minor 177 = 10 * version_struct->minor + (*version_text++ - ASCII_0); 178 } 179 if (*version_text++ != ASCII_PERIOD) 180 return XML_FALSE; 181 182 /* Finally the micro version number */ 183 version_struct->micro = 0; 184 while (*version_text >= ASCII_0 && *version_text <= ASCII_9) { 185 version_struct->micro 186 = 10 * version_struct->micro + (*version_text++ - ASCII_0); 187 } 188 if (*version_text != 0x00) 189 return XML_FALSE; 190 return XML_TRUE; 191 } 192 193 static int 194 versions_equal(const XML_Expat_Version *first, 195 const XML_Expat_Version *second) { 196 return (first->major == second->major && first->minor == second->minor 197 && first->micro == second->micro); 198 } 199 200 START_TEST(test_misc_version) { 201 XML_Expat_Version read_version = XML_ExpatVersionInfo(); 202 /* Silence compiler warning with the following assignment */ 203 XML_Expat_Version parsed_version = {0, 0, 0}; 204 const XML_LChar *version_text = XML_ExpatVersion(); 205 206 if (version_text == NULL) 207 fail("Could not obtain version text"); 208 assert(version_text != NULL); 209 if (! parse_version(version_text, &parsed_version)) 210 fail("Unable to parse version text"); 211 if (! versions_equal(&read_version, &parsed_version)) 212 fail("Version mismatch"); 213 214 if (xcstrcmp(version_text, XCS("expat_2.7.3")) 215 != 0) /* needs bump on releases */ 216 fail("XML_*_VERSION in expat.h out of sync?\n"); 217 } 218 END_TEST 219 220 /* Test feature information */ 221 START_TEST(test_misc_features) { 222 const XML_Feature *features = XML_GetFeatureList(); 223 224 /* Prevent problems with double-freeing parsers */ 225 g_parser = NULL; 226 if (features == NULL) { 227 fail("Failed to get feature information"); 228 } else { 229 /* Loop through the features checking what we can */ 230 while (features->feature != XML_FEATURE_END) { 231 switch (features->feature) { 232 case XML_FEATURE_SIZEOF_XML_CHAR: 233 if (features->value != sizeof(XML_Char)) 234 fail("Incorrect size of XML_Char"); 235 break; 236 case XML_FEATURE_SIZEOF_XML_LCHAR: 237 if (features->value != sizeof(XML_LChar)) 238 fail("Incorrect size of XML_LChar"); 239 break; 240 default: 241 break; 242 } 243 features++; 244 } 245 } 246 } 247 END_TEST 248 249 /* Regression test for GitHub Issue #17: memory leak parsing attribute 250 * values with mixed bound and unbound namespaces. 251 */ 252 START_TEST(test_misc_attribute_leak) { 253 const char *text = "<D xmlns:L=\"D\" l:a='' L:a=''/>"; 254 XML_Memory_Handling_Suite memsuite 255 = {tracking_malloc, tracking_realloc, tracking_free}; 256 257 g_parser = XML_ParserCreate_MM(XCS("UTF-8"), &memsuite, XCS("\n")); 258 expect_failure(text, XML_ERROR_UNBOUND_PREFIX, "Unbound prefixes not found"); 259 XML_ParserFree(g_parser); 260 /* Prevent the teardown trying to double free */ 261 g_parser = NULL; 262 263 if (! tracking_report()) 264 fail("Memory leak found"); 265 } 266 END_TEST 267 268 /* Test parser created for UTF-16LE is successful */ 269 START_TEST(test_misc_utf16le) { 270 const char text[] = 271 /* <?xml version='1.0'?><q>Hi</q> */ 272 "<\0?\0x\0m\0l\0 \0" 273 "v\0e\0r\0s\0i\0o\0n\0=\0'\0\x31\0.\0\x30\0'\0?\0>\0" 274 "<\0q\0>\0H\0i\0<\0/\0q\0>\0"; 275 const XML_Char *expected = XCS("Hi"); 276 CharData storage; 277 278 g_parser = XML_ParserCreate(XCS("UTF-16LE")); 279 if (g_parser == NULL) 280 fail("Parser not created"); 281 282 CharData_Init(&storage); 283 XML_SetUserData(g_parser, &storage); 284 XML_SetCharacterDataHandler(g_parser, accumulate_characters); 285 if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)sizeof(text) - 1, XML_TRUE) 286 == XML_STATUS_ERROR) 287 xml_failure(g_parser); 288 CharData_CheckXMLChars(&storage, expected); 289 } 290 END_TEST 291 292 START_TEST(test_misc_stop_during_end_handler_issue_240_1) { 293 XML_Parser parser; 294 DataIssue240 *mydata; 295 enum XML_Status result; 296 const char *const doc1 = "<doc><e1/><e><foo/></e></doc>"; 297 298 parser = XML_ParserCreate(NULL); 299 XML_SetElementHandler(parser, start_element_issue_240, end_element_issue_240); 300 mydata = (DataIssue240 *)malloc(sizeof(DataIssue240)); 301 assert_true(mydata != NULL); 302 mydata->parser = parser; 303 mydata->deep = 0; 304 XML_SetUserData(parser, mydata); 305 306 result = _XML_Parse_SINGLE_BYTES(parser, doc1, (int)strlen(doc1), 1); 307 XML_ParserFree(parser); 308 free(mydata); 309 if (result != XML_STATUS_ERROR) 310 fail("Stopping the parser did not work as expected"); 311 } 312 END_TEST 313 314 START_TEST(test_misc_stop_during_end_handler_issue_240_2) { 315 XML_Parser parser; 316 DataIssue240 *mydata; 317 enum XML_Status result; 318 const char *const doc2 = "<doc><elem/></doc>"; 319 320 parser = XML_ParserCreate(NULL); 321 XML_SetElementHandler(parser, start_element_issue_240, end_element_issue_240); 322 mydata = (DataIssue240 *)malloc(sizeof(DataIssue240)); 323 assert_true(mydata != NULL); 324 mydata->parser = parser; 325 mydata->deep = 0; 326 XML_SetUserData(parser, mydata); 327 328 result = _XML_Parse_SINGLE_BYTES(parser, doc2, (int)strlen(doc2), 1); 329 XML_ParserFree(parser); 330 free(mydata); 331 if (result != XML_STATUS_ERROR) 332 fail("Stopping the parser did not work as expected"); 333 } 334 END_TEST 335 336 START_TEST(test_misc_deny_internal_entity_closing_doctype_issue_317) { 337 const char *const inputOne 338 = "<!DOCTYPE d [\n" 339 "<!ENTITY % element_d '<!ELEMENT d (#PCDATA)*>'>\n" 340 "%element_d;\n" 341 "<!ENTITY % e ']><d/>'>\n" 342 "\n" 343 "%e;"; 344 const char *const inputTwo 345 = "<!DOCTYPE d [\n" 346 "<!ENTITY % element_d '<!ELEMENT d (#PCDATA)*>'>\n" 347 "%element_d;\n" 348 "<!ENTITY % e1 ']><d/>'><!ENTITY % e2 '%e1;'>\n" 349 "\n" 350 "%e2;"; 351 const char *const inputThree 352 = "<!DOCTYPE d [\n" 353 "<!ENTITY % element_d '<!ELEMENT d (#PCDATA)*>'>\n" 354 "%element_d;\n" 355 "<!ENTITY % e ']><d'>\n" 356 "\n" 357 "%e;/>"; 358 const char *const inputIssue317 359 = "<!DOCTYPE doc [\n" 360 "<!ENTITY % element_doc '<!ELEMENT doc (#PCDATA)*>'>\n" 361 "%element_doc;\n" 362 "<!ENTITY % foo ']>\n" 363 "<doc>Hell<oc (#PCDATA)*>'>\n" 364 "%foo;\n" 365 "]>\n" 366 "<doc>Hello, world</dVc>"; 367 368 const char *const inputs[] = {inputOne, inputTwo, inputThree, inputIssue317}; 369 const XML_Bool suspendOrNot[] = {XML_FALSE, XML_TRUE}; 370 size_t inputIndex = 0; 371 372 for (; inputIndex < sizeof(inputs) / sizeof(inputs[0]); inputIndex++) { 373 for (size_t suspendOrNotIndex = 0; 374 suspendOrNotIndex < sizeof(suspendOrNot) / sizeof(suspendOrNot[0]); 375 suspendOrNotIndex++) { 376 const char *const input = inputs[inputIndex]; 377 const XML_Bool suspend = suspendOrNot[suspendOrNotIndex]; 378 if (suspend && (g_chunkSize > 0)) { 379 // We cannot use _XML_Parse_SINGLE_BYTES below due to suspension, and 380 // so chunk sizes >0 would only repeat the very same test 381 // due to use of plain XML_Parse; we are saving upon that runtime: 382 return; 383 } 384 385 set_subtest("[input=%d suspend=%s] %s", (int)inputIndex, 386 suspend ? "true" : "false", input); 387 XML_Parser parser; 388 enum XML_Status parseResult; 389 int setParamEntityResult; 390 XML_Size lineNumber; 391 XML_Size columnNumber; 392 393 parser = XML_ParserCreate(NULL); 394 setParamEntityResult 395 = XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS); 396 if (setParamEntityResult != 1) 397 fail("Failed to set XML_PARAM_ENTITY_PARSING_ALWAYS."); 398 399 if (suspend) { 400 XML_SetUserData(parser, parser); 401 XML_SetElementDeclHandler(parser, suspend_after_element_declaration); 402 } 403 404 if (suspend) { 405 // can't use SINGLE_BYTES here, because it'll return early on 406 // suspension, and we won't know exactly how much input we actually 407 // managed to give Expat. 408 parseResult = XML_Parse(parser, input, (int)strlen(input), 0); 409 410 while (parseResult == XML_STATUS_SUSPENDED) { 411 parseResult = XML_ResumeParser(parser); 412 } 413 414 if (parseResult != XML_STATUS_ERROR) { 415 // can't use SINGLE_BYTES here, because it'll return early on 416 // suspension, and we won't know exactly how much input we actually 417 // managed to give Expat. 418 parseResult = XML_Parse(parser, "", 0, 1); 419 } 420 421 while (parseResult == XML_STATUS_SUSPENDED) { 422 parseResult = XML_ResumeParser(parser); 423 } 424 } else { 425 parseResult 426 = _XML_Parse_SINGLE_BYTES(parser, input, (int)strlen(input), 0); 427 428 if (parseResult != XML_STATUS_ERROR) { 429 parseResult = _XML_Parse_SINGLE_BYTES(parser, "", 0, 1); 430 } 431 } 432 433 if (parseResult != XML_STATUS_ERROR) { 434 fail("Parsing was expected to fail but succeeded."); 435 } 436 437 if (XML_GetErrorCode(parser) != XML_ERROR_INVALID_TOKEN) 438 fail("Error code does not match XML_ERROR_INVALID_TOKEN"); 439 440 lineNumber = XML_GetCurrentLineNumber(parser); 441 if (lineNumber != 6) 442 fail("XML_GetCurrentLineNumber does not work as expected."); 443 444 columnNumber = XML_GetCurrentColumnNumber(parser); 445 if (columnNumber != 0) 446 fail("XML_GetCurrentColumnNumber does not work as expected."); 447 448 XML_ParserFree(parser); 449 } 450 } 451 } 452 END_TEST 453 454 START_TEST(test_misc_tag_mismatch_reset_leak) { 455 #ifdef XML_NS 456 const char *const text = "<open xmlns='https://namespace1.test'></close>"; 457 XML_Parser parser = XML_ParserCreateNS(NULL, XCS('\n')); 458 459 if (_XML_Parse_SINGLE_BYTES(parser, text, (int)strlen(text), XML_TRUE) 460 != XML_STATUS_ERROR) 461 fail("Call to parse was expected to fail"); 462 if (XML_GetErrorCode(parser) != XML_ERROR_TAG_MISMATCH) 463 fail("Call to parse was expected to fail from a closing tag mismatch"); 464 465 XML_ParserReset(parser, NULL); 466 467 if (_XML_Parse_SINGLE_BYTES(parser, text, (int)strlen(text), XML_TRUE) 468 != XML_STATUS_ERROR) 469 fail("Call to parse was expected to fail"); 470 if (XML_GetErrorCode(parser) != XML_ERROR_TAG_MISMATCH) 471 fail("Call to parse was expected to fail from a closing tag mismatch"); 472 473 XML_ParserFree(parser); 474 #endif 475 } 476 END_TEST 477 478 START_TEST(test_misc_create_external_entity_parser_with_null_context) { 479 // With XML_DTD undefined, the only supported case of external entities 480 // is pattern "<!ENTITY entity123 SYSTEM 'filename123'>". A NULL context 481 // was causing a segfault through a null pointer dereference in function 482 // setContext, previously. 483 XML_Parser parser = XML_ParserCreate(NULL); 484 XML_Parser ext_parser = XML_ExternalEntityParserCreate(parser, NULL, NULL); 485 #ifdef XML_DTD 486 assert_true(ext_parser != NULL); 487 XML_ParserFree(ext_parser); 488 #else 489 assert_true(ext_parser == NULL); 490 #endif /* XML_DTD */ 491 XML_ParserFree(parser); 492 } 493 END_TEST 494 495 START_TEST(test_misc_general_entities_support) { 496 const char *const doc 497 = "<!DOCTYPE r [\n" 498 "<!ENTITY e1 'v1'>\n" 499 "<!ENTITY e2 SYSTEM 'v2'>\n" 500 "]>\n" 501 "<r a1='[&e1;]'>[&e1;][&e2;][&'><"]</r>"; 502 503 CharData storage; 504 CharData_Init(&storage); 505 506 XML_Parser parser = XML_ParserCreate(NULL); 507 XML_SetUserData(parser, &storage); 508 XML_SetStartElementHandler(parser, accumulate_start_element); 509 XML_SetExternalEntityRefHandler(parser, 510 external_entity_failer__if_not_xml_ge); 511 XML_SetEntityDeclHandler(parser, accumulate_entity_decl); 512 XML_SetCharacterDataHandler(parser, accumulate_characters); 513 514 if (_XML_Parse_SINGLE_BYTES(parser, doc, (int)strlen(doc), XML_TRUE) 515 != XML_STATUS_OK) { 516 xml_failure(parser); 517 } 518 519 XML_ParserFree(parser); 520 521 CharData_CheckXMLChars(&storage, 522 /* clang-format off */ 523 #if XML_GE == 1 524 XCS("e1=v1\n") 525 XCS("e2=(null)\n") 526 XCS("(r(a1=[v1]))\n") 527 XCS("[v1][][&'><\"]") 528 #else 529 XCS("e1=&e1;\n") 530 XCS("e2=(null)\n") 531 XCS("(r(a1=[&e1;]))\n") 532 XCS("[&e1;][&e2;][&'><\"]") 533 #endif 534 ); 535 /* clang-format on */ 536 } 537 END_TEST 538 539 static void XMLCALL 540 resumable_stopping_character_handler(void *userData, const XML_Char *s, 541 int len) { 542 UNUSED_P(s); 543 UNUSED_P(len); 544 XML_Parser parser = (XML_Parser)userData; 545 XML_StopParser(parser, XML_TRUE); 546 } 547 548 // NOTE: This test needs active LeakSanitizer to be of actual use 549 START_TEST(test_misc_char_handler_stop_without_leak) { 550 const char *const data 551 = "<!DOCTYPE t1[<!ENTITY e1 'angle<'><!ENTITY e2 '&e1;'>]><t1>&e2;"; 552 XML_Parser parser = XML_ParserCreate(NULL); 553 assert_true(parser != NULL); 554 XML_SetUserData(parser, parser); 555 XML_SetCharacterDataHandler(parser, resumable_stopping_character_handler); 556 _XML_Parse_SINGLE_BYTES(parser, data, (int)strlen(data), XML_FALSE); 557 XML_ParserFree(parser); 558 } 559 END_TEST 560 561 START_TEST(test_misc_resumeparser_not_crashing) { 562 XML_Parser parser = XML_ParserCreate(NULL); 563 XML_GetBuffer(parser, 1); 564 XML_StopParser(parser, /*resumable=*/XML_TRUE); 565 XML_ResumeParser(parser); // could crash here, previously 566 XML_ParserFree(parser); 567 } 568 END_TEST 569 570 START_TEST(test_misc_stopparser_rejects_unstarted_parser) { 571 const XML_Bool cases[] = {XML_TRUE, XML_FALSE}; 572 for (size_t i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) { 573 const XML_Bool resumable = cases[i]; 574 XML_Parser parser = XML_ParserCreate(NULL); 575 assert_true(XML_GetErrorCode(parser) == XML_ERROR_NONE); 576 assert_true(XML_StopParser(parser, resumable) == XML_STATUS_ERROR); 577 assert_true(XML_GetErrorCode(parser) == XML_ERROR_NOT_STARTED); 578 XML_ParserFree(parser); 579 } 580 } 581 END_TEST 582 583 /* Adaptation of accumulate_characters that takes ExtHdlrData input to work with 584 * test_renter_loop_finite_content below */ 585 void XMLCALL 586 accumulate_characters_ext_handler(void *userData, const XML_Char *s, int len) { 587 ExtHdlrData *const test_data = (ExtHdlrData *)userData; 588 CharData_AppendXMLChars(test_data->storage, s, len); 589 } 590 591 /* Test that internalEntityProcessor does not re-enter forever; 592 * based on files tests/xmlconf/xmltest/valid/ext-sa/012.{xml,ent} */ 593 START_TEST(test_renter_loop_finite_content) { 594 CharData storage; 595 CharData_Init(&storage); 596 const char *const text = "<!DOCTYPE doc [\n" 597 "<!ENTITY e1 '&e2;'>\n" 598 "<!ENTITY e2 '&e3;'>\n" 599 "<!ENTITY e3 SYSTEM '012.ent'>\n" 600 "<!ENTITY e4 '&e5;'>\n" 601 "<!ENTITY e5 '(e5)'>\n" 602 "<!ELEMENT doc (#PCDATA)>\n" 603 "]>\n" 604 "<doc>&e1;</doc>\n"; 605 ExtHdlrData test_data = {"&e4;\n", external_entity_null_loader, &storage}; 606 const XML_Char *const expected = XCS("(e5)\n"); 607 608 XML_Parser parser = XML_ParserCreate(NULL); 609 assert_true(parser != NULL); 610 XML_SetUserData(parser, &test_data); 611 XML_SetExternalEntityRefHandler(parser, external_entity_oneshot_loader); 612 XML_SetCharacterDataHandler(parser, accumulate_characters_ext_handler); 613 if (_XML_Parse_SINGLE_BYTES(parser, text, (int)strlen(text), XML_TRUE) 614 == XML_STATUS_ERROR) 615 xml_failure(parser); 616 617 CharData_CheckXMLChars(&storage, expected); 618 XML_ParserFree(parser); 619 } 620 END_TEST 621 622 // Inspired by function XML_OriginalString of Perl's XML::Parser 623 static char * 624 dup_original_string(XML_Parser parser) { 625 const int byte_count = XML_GetCurrentByteCount(parser); 626 627 assert_true(byte_count >= 0); 628 629 int offset = -1; 630 int size = -1; 631 632 const char *const context = XML_GetInputContext(parser, &offset, &size); 633 634 #if XML_CONTEXT_BYTES > 0 635 assert_true(context != NULL); 636 assert_true(offset >= 0); 637 assert_true(size >= 0); 638 return portable_strndup(context + offset, byte_count); 639 #else 640 assert_true(context == NULL); 641 return NULL; 642 #endif 643 } 644 645 static void 646 on_characters_issue_980(void *userData, const XML_Char *s, int len) { 647 (void)s; 648 (void)len; 649 XML_Parser parser = (XML_Parser)userData; 650 651 char *const original_string = dup_original_string(parser); 652 653 #if XML_CONTEXT_BYTES > 0 654 assert_true(original_string != NULL); 655 assert_true(strcmp(original_string, "&draft.day;") == 0); 656 free(original_string); 657 #else 658 assert_true(original_string == NULL); 659 #endif 660 } 661 662 START_TEST(test_misc_expected_event_ptr_issue_980) { 663 // NOTE: This is a tiny subset of sample "REC-xml-19980210.xml" 664 // from Perl's XML::Parser 665 const char *const doc = "<!DOCTYPE day [\n" 666 " <!ENTITY draft.day '10'>\n" 667 "]>\n" 668 "<day>&draft.day;</day>\n"; 669 670 XML_Parser parser = XML_ParserCreate(NULL); 671 XML_SetUserData(parser, parser); 672 XML_SetCharacterDataHandler(parser, on_characters_issue_980); 673 674 assert_true(_XML_Parse_SINGLE_BYTES(parser, doc, (int)strlen(doc), 675 /*isFinal=*/XML_TRUE) 676 == XML_STATUS_OK); 677 678 XML_ParserFree(parser); 679 } 680 END_TEST 681 682 START_TEST(test_misc_sync_entity_tolerated) { 683 const char *const doc = "<!DOCTYPE t0 [\n" 684 " <!ENTITY a '<t1></t1>'>\n" 685 " <!ENTITY b '<t2>two</t2>'>\n" 686 " <!ENTITY c '<t3>three<t4>four</t4>three</t3>'>\n" 687 " <!ENTITY d '<t5>&b;</t5>'>\n" 688 "]>\n" 689 "<t0>&a;&b;&c;&d;</t0>\n"; 690 XML_Parser parser = XML_ParserCreate(NULL); 691 692 assert_true(_XML_Parse_SINGLE_BYTES(parser, doc, (int)strlen(doc), 693 /*isFinal=*/XML_TRUE) 694 == XML_STATUS_OK); 695 696 XML_ParserFree(parser); 697 } 698 END_TEST 699 700 START_TEST(test_misc_async_entity_rejected) { 701 struct test_case { 702 const char *doc; 703 enum XML_Status expectedStatusNoGE; 704 enum XML_Error expectedErrorNoGE; 705 XML_Size expectedErrorLine; 706 XML_Size expectedErrorColumn; 707 }; 708 const struct test_case cases[] = { 709 // Opened by one entity, closed by another 710 {"<!DOCTYPE t0 [\n" 711 " <!ENTITY open '<t1>'>\n" 712 " <!ENTITY close '</t1>'>\n" 713 "]>\n" 714 "<t0>&open;&close;</t0>\n", 715 XML_STATUS_OK, XML_ERROR_NONE, 5, 4}, 716 // Opened by tag, closed by entity (non-root case) 717 {"<!DOCTYPE t0 [\n" 718 " <!ENTITY g0 ''>\n" 719 " <!ENTITY g1 '&g0;</t1>'>\n" 720 "]>\n" 721 "<t0><t1>&g1;</t0>\n", 722 XML_STATUS_ERROR, XML_ERROR_TAG_MISMATCH, 5, 8}, 723 // Opened by tag, closed by entity (root case) 724 {"<!DOCTYPE t0 [\n" 725 " <!ENTITY g0 ''>\n" 726 " <!ENTITY g1 '&g0;</t0>'>\n" 727 "]>\n" 728 "<t0>&g1;\n", 729 XML_STATUS_ERROR, XML_ERROR_NO_ELEMENTS, 5, 4}, 730 // Opened by entity, closed by tag <-- regression from 2.7.0 731 {"<!DOCTYPE t0 [\n" 732 " <!ENTITY g0 ''>\n" 733 " <!ENTITY g1 '<t1>&g0;'>\n" 734 "]>\n" 735 "<t0>&g1;</t1></t0>\n", 736 XML_STATUS_ERROR, XML_ERROR_TAG_MISMATCH, 5, 4}, 737 // Opened by tag, closed by entity; then the other way around 738 {"<!DOCTYPE t0 [\n" 739 " <!ENTITY open '<t1>'>\n" 740 " <!ENTITY close '</t1>'>\n" 741 "]>\n" 742 "<t0><t1>&close;&open;</t1></t0>\n", 743 XML_STATUS_OK, XML_ERROR_NONE, 5, 8}, 744 }; 745 746 for (size_t i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) { 747 const struct test_case testCase = cases[i]; 748 set_subtest("cases[%d]", (int)i); 749 750 const char *const doc = testCase.doc; 751 #if XML_GE == 1 752 const enum XML_Status expectedStatus = XML_STATUS_ERROR; 753 const enum XML_Error expectedError = XML_ERROR_ASYNC_ENTITY; 754 #else 755 const enum XML_Status expectedStatus = testCase.expectedStatusNoGE; 756 const enum XML_Error expectedError = testCase.expectedErrorNoGE; 757 #endif 758 759 XML_Parser parser = XML_ParserCreate(NULL); 760 assert_true(_XML_Parse_SINGLE_BYTES(parser, doc, (int)strlen(doc), 761 /*isFinal=*/XML_TRUE) 762 == expectedStatus); 763 assert_true(XML_GetErrorCode(parser) == expectedError); 764 #if XML_GE == 1 765 assert_true(XML_GetCurrentLineNumber(parser) == testCase.expectedErrorLine); 766 assert_true(XML_GetCurrentColumnNumber(parser) 767 == testCase.expectedErrorColumn); 768 #endif 769 XML_ParserFree(parser); 770 } 771 } 772 END_TEST 773 774 void 775 make_miscellaneous_test_case(Suite *s) { 776 TCase *tc_misc = tcase_create("miscellaneous tests"); 777 778 suite_add_tcase(s, tc_misc); 779 tcase_add_checked_fixture(tc_misc, NULL, basic_teardown); 780 781 tcase_add_test(tc_misc, test_misc_alloc_create_parser); 782 tcase_add_test(tc_misc, test_misc_alloc_create_parser_with_encoding); 783 tcase_add_test(tc_misc, test_misc_null_parser); 784 tcase_add_test(tc_misc, test_misc_error_string); 785 tcase_add_test(tc_misc, test_misc_version); 786 tcase_add_test(tc_misc, test_misc_features); 787 tcase_add_test(tc_misc, test_misc_attribute_leak); 788 tcase_add_test(tc_misc, test_misc_utf16le); 789 tcase_add_test(tc_misc, test_misc_stop_during_end_handler_issue_240_1); 790 tcase_add_test(tc_misc, test_misc_stop_during_end_handler_issue_240_2); 791 tcase_add_test__ifdef_xml_dtd( 792 tc_misc, test_misc_deny_internal_entity_closing_doctype_issue_317); 793 tcase_add_test(tc_misc, test_misc_tag_mismatch_reset_leak); 794 tcase_add_test(tc_misc, 795 test_misc_create_external_entity_parser_with_null_context); 796 tcase_add_test(tc_misc, test_misc_general_entities_support); 797 tcase_add_test(tc_misc, test_misc_char_handler_stop_without_leak); 798 tcase_add_test(tc_misc, test_misc_resumeparser_not_crashing); 799 tcase_add_test(tc_misc, test_misc_stopparser_rejects_unstarted_parser); 800 tcase_add_test__if_xml_ge(tc_misc, test_renter_loop_finite_content); 801 tcase_add_test(tc_misc, test_misc_expected_event_ptr_issue_980); 802 tcase_add_test(tc_misc, test_misc_sync_entity_tolerated); 803 tcase_add_test(tc_misc, test_misc_async_entity_rejected); 804 } 805