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 = 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 = 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.1"))) /* needs bump on releases */ 215 fail("XML_*_VERSION in expat.h out of sync?\n"); 216 } 217 END_TEST 218 219 /* Test feature information */ 220 START_TEST(test_misc_features) { 221 const XML_Feature *features = XML_GetFeatureList(); 222 223 /* Prevent problems with double-freeing parsers */ 224 g_parser = NULL; 225 if (features == NULL) { 226 fail("Failed to get feature information"); 227 } else { 228 /* Loop through the features checking what we can */ 229 while (features->feature != XML_FEATURE_END) { 230 switch (features->feature) { 231 case XML_FEATURE_SIZEOF_XML_CHAR: 232 if (features->value != sizeof(XML_Char)) 233 fail("Incorrect size of XML_Char"); 234 break; 235 case XML_FEATURE_SIZEOF_XML_LCHAR: 236 if (features->value != sizeof(XML_LChar)) 237 fail("Incorrect size of XML_LChar"); 238 break; 239 default: 240 break; 241 } 242 features++; 243 } 244 } 245 } 246 END_TEST 247 248 /* Regression test for GitHub Issue #17: memory leak parsing attribute 249 * values with mixed bound and unbound namespaces. 250 */ 251 START_TEST(test_misc_attribute_leak) { 252 const char *text = "<D xmlns:L=\"D\" l:a='' L:a=''/>"; 253 XML_Memory_Handling_Suite memsuite 254 = {tracking_malloc, tracking_realloc, tracking_free}; 255 256 g_parser = XML_ParserCreate_MM(XCS("UTF-8"), &memsuite, XCS("\n")); 257 expect_failure(text, XML_ERROR_UNBOUND_PREFIX, "Unbound prefixes not found"); 258 XML_ParserFree(g_parser); 259 /* Prevent the teardown trying to double free */ 260 g_parser = NULL; 261 262 if (! tracking_report()) 263 fail("Memory leak found"); 264 } 265 END_TEST 266 267 /* Test parser created for UTF-16LE is successful */ 268 START_TEST(test_misc_utf16le) { 269 const char text[] = 270 /* <?xml version='1.0'?><q>Hi</q> */ 271 "<\0?\0x\0m\0l\0 \0" 272 "v\0e\0r\0s\0i\0o\0n\0=\0'\0\x31\0.\0\x30\0'\0?\0>\0" 273 "<\0q\0>\0H\0i\0<\0/\0q\0>\0"; 274 const XML_Char *expected = XCS("Hi"); 275 CharData storage; 276 277 g_parser = XML_ParserCreate(XCS("UTF-16LE")); 278 if (g_parser == NULL) 279 fail("Parser not created"); 280 281 CharData_Init(&storage); 282 XML_SetUserData(g_parser, &storage); 283 XML_SetCharacterDataHandler(g_parser, accumulate_characters); 284 if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)sizeof(text) - 1, XML_TRUE) 285 == XML_STATUS_ERROR) 286 xml_failure(g_parser); 287 CharData_CheckXMLChars(&storage, expected); 288 } 289 END_TEST 290 291 START_TEST(test_misc_stop_during_end_handler_issue_240_1) { 292 XML_Parser parser; 293 DataIssue240 *mydata; 294 enum XML_Status result; 295 const char *const doc1 = "<doc><e1/><e><foo/></e></doc>"; 296 297 parser = XML_ParserCreate(NULL); 298 XML_SetElementHandler(parser, start_element_issue_240, end_element_issue_240); 299 mydata = (DataIssue240 *)malloc(sizeof(DataIssue240)); 300 assert_true(mydata != NULL); 301 mydata->parser = parser; 302 mydata->deep = 0; 303 XML_SetUserData(parser, mydata); 304 305 result = _XML_Parse_SINGLE_BYTES(parser, doc1, (int)strlen(doc1), 1); 306 XML_ParserFree(parser); 307 free(mydata); 308 if (result != XML_STATUS_ERROR) 309 fail("Stopping the parser did not work as expected"); 310 } 311 END_TEST 312 313 START_TEST(test_misc_stop_during_end_handler_issue_240_2) { 314 XML_Parser parser; 315 DataIssue240 *mydata; 316 enum XML_Status result; 317 const char *const doc2 = "<doc><elem/></doc>"; 318 319 parser = XML_ParserCreate(NULL); 320 XML_SetElementHandler(parser, start_element_issue_240, end_element_issue_240); 321 mydata = (DataIssue240 *)malloc(sizeof(DataIssue240)); 322 assert_true(mydata != NULL); 323 mydata->parser = parser; 324 mydata->deep = 0; 325 XML_SetUserData(parser, mydata); 326 327 result = _XML_Parse_SINGLE_BYTES(parser, doc2, (int)strlen(doc2), 1); 328 XML_ParserFree(parser); 329 free(mydata); 330 if (result != XML_STATUS_ERROR) 331 fail("Stopping the parser did not work as expected"); 332 } 333 END_TEST 334 335 START_TEST(test_misc_deny_internal_entity_closing_doctype_issue_317) { 336 const char *const inputOne 337 = "<!DOCTYPE d [\n" 338 "<!ENTITY % element_d '<!ELEMENT d (#PCDATA)*>'>\n" 339 "%element_d;\n" 340 "<!ENTITY % e ']><d/>'>\n" 341 "\n" 342 "%e;"; 343 const char *const inputTwo 344 = "<!DOCTYPE d [\n" 345 "<!ENTITY % element_d '<!ELEMENT d (#PCDATA)*>'>\n" 346 "%element_d;\n" 347 "<!ENTITY % e1 ']><d/>'><!ENTITY % e2 '%e1;'>\n" 348 "\n" 349 "%e2;"; 350 const char *const inputThree 351 = "<!DOCTYPE d [\n" 352 "<!ENTITY % element_d '<!ELEMENT d (#PCDATA)*>'>\n" 353 "%element_d;\n" 354 "<!ENTITY % e ']><d'>\n" 355 "\n" 356 "%e;/>"; 357 const char *const inputIssue317 358 = "<!DOCTYPE doc [\n" 359 "<!ENTITY % element_doc '<!ELEMENT doc (#PCDATA)*>'>\n" 360 "%element_doc;\n" 361 "<!ENTITY % foo ']>\n" 362 "<doc>Hell<oc (#PCDATA)*>'>\n" 363 "%foo;\n" 364 "]>\n" 365 "<doc>Hello, world</dVc>"; 366 367 const char *const inputs[] = {inputOne, inputTwo, inputThree, inputIssue317}; 368 const XML_Bool suspendOrNot[] = {XML_FALSE, XML_TRUE}; 369 size_t inputIndex = 0; 370 371 for (; inputIndex < sizeof(inputs) / sizeof(inputs[0]); inputIndex++) { 372 for (size_t suspendOrNotIndex = 0; 373 suspendOrNotIndex < sizeof(suspendOrNot) / sizeof(suspendOrNot[0]); 374 suspendOrNotIndex++) { 375 const char *const input = inputs[inputIndex]; 376 const XML_Bool suspend = suspendOrNot[suspendOrNotIndex]; 377 if (suspend && (g_chunkSize > 0)) { 378 // We cannot use _XML_Parse_SINGLE_BYTES below due to suspension, and 379 // so chunk sizes >0 would only repeat the very same test 380 // due to use of plain XML_Parse; we are saving upon that runtime: 381 return; 382 } 383 384 set_subtest("[input=%d suspend=%s] %s", (int)inputIndex, 385 suspend ? "true" : "false", input); 386 XML_Parser parser; 387 enum XML_Status parseResult; 388 int setParamEntityResult; 389 XML_Size lineNumber; 390 XML_Size columnNumber; 391 392 parser = XML_ParserCreate(NULL); 393 setParamEntityResult 394 = XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS); 395 if (setParamEntityResult != 1) 396 fail("Failed to set XML_PARAM_ENTITY_PARSING_ALWAYS."); 397 398 if (suspend) { 399 XML_SetUserData(parser, parser); 400 XML_SetElementDeclHandler(parser, suspend_after_element_declaration); 401 } 402 403 if (suspend) { 404 // can't use SINGLE_BYTES here, because it'll return early on 405 // suspension, and we won't know exactly how much input we actually 406 // managed to give Expat. 407 parseResult = XML_Parse(parser, input, (int)strlen(input), 0); 408 409 while (parseResult == XML_STATUS_SUSPENDED) { 410 parseResult = XML_ResumeParser(parser); 411 } 412 413 if (parseResult != XML_STATUS_ERROR) { 414 // can't use SINGLE_BYTES here, because it'll return early on 415 // suspension, and we won't know exactly how much input we actually 416 // managed to give Expat. 417 parseResult = XML_Parse(parser, "", 0, 1); 418 } 419 420 while (parseResult == XML_STATUS_SUSPENDED) { 421 parseResult = XML_ResumeParser(parser); 422 } 423 } else { 424 parseResult 425 = _XML_Parse_SINGLE_BYTES(parser, input, (int)strlen(input), 0); 426 427 if (parseResult != XML_STATUS_ERROR) { 428 parseResult = _XML_Parse_SINGLE_BYTES(parser, "", 0, 1); 429 } 430 } 431 432 if (parseResult != XML_STATUS_ERROR) { 433 fail("Parsing was expected to fail but succeeded."); 434 } 435 436 if (XML_GetErrorCode(parser) != XML_ERROR_INVALID_TOKEN) 437 fail("Error code does not match XML_ERROR_INVALID_TOKEN"); 438 439 lineNumber = XML_GetCurrentLineNumber(parser); 440 if (lineNumber != 6) 441 fail("XML_GetCurrentLineNumber does not work as expected."); 442 443 columnNumber = XML_GetCurrentColumnNumber(parser); 444 if (columnNumber != 0) 445 fail("XML_GetCurrentColumnNumber does not work as expected."); 446 447 XML_ParserFree(parser); 448 } 449 } 450 } 451 END_TEST 452 453 START_TEST(test_misc_tag_mismatch_reset_leak) { 454 #ifdef XML_NS 455 const char *const text = "<open xmlns='https://namespace1.test'></close>"; 456 XML_Parser parser = XML_ParserCreateNS(NULL, XCS('\n')); 457 458 if (_XML_Parse_SINGLE_BYTES(parser, text, (int)strlen(text), XML_TRUE) 459 != XML_STATUS_ERROR) 460 fail("Call to parse was expected to fail"); 461 if (XML_GetErrorCode(parser) != XML_ERROR_TAG_MISMATCH) 462 fail("Call to parse was expected to fail from a closing tag mismatch"); 463 464 XML_ParserReset(parser, NULL); 465 466 if (_XML_Parse_SINGLE_BYTES(parser, text, (int)strlen(text), XML_TRUE) 467 != XML_STATUS_ERROR) 468 fail("Call to parse was expected to fail"); 469 if (XML_GetErrorCode(parser) != XML_ERROR_TAG_MISMATCH) 470 fail("Call to parse was expected to fail from a closing tag mismatch"); 471 472 XML_ParserFree(parser); 473 #endif 474 } 475 END_TEST 476 477 START_TEST(test_misc_create_external_entity_parser_with_null_context) { 478 // With XML_DTD undefined, the only supported case of external entities 479 // is pattern "<!ENTITY entity123 SYSTEM 'filename123'>". A NULL context 480 // was causing a segfault through a null pointer dereference in function 481 // setContext, previously. 482 XML_Parser parser = XML_ParserCreate(NULL); 483 XML_Parser ext_parser = XML_ExternalEntityParserCreate(parser, NULL, NULL); 484 #ifdef XML_DTD 485 assert_true(ext_parser != NULL); 486 XML_ParserFree(ext_parser); 487 #else 488 assert_true(ext_parser == NULL); 489 #endif /* XML_DTD */ 490 XML_ParserFree(parser); 491 } 492 END_TEST 493 494 START_TEST(test_misc_general_entities_support) { 495 const char *const doc 496 = "<!DOCTYPE r [\n" 497 "<!ENTITY e1 'v1'>\n" 498 "<!ENTITY e2 SYSTEM 'v2'>\n" 499 "]>\n" 500 "<r a1='[&e1;]'>[&e1;][&e2;][&'><"]</r>"; 501 502 CharData storage; 503 CharData_Init(&storage); 504 505 XML_Parser parser = XML_ParserCreate(NULL); 506 XML_SetUserData(parser, &storage); 507 XML_SetStartElementHandler(parser, accumulate_start_element); 508 XML_SetExternalEntityRefHandler(parser, 509 external_entity_failer__if_not_xml_ge); 510 XML_SetEntityDeclHandler(parser, accumulate_entity_decl); 511 XML_SetCharacterDataHandler(parser, accumulate_characters); 512 513 if (_XML_Parse_SINGLE_BYTES(parser, doc, (int)strlen(doc), XML_TRUE) 514 != XML_STATUS_OK) { 515 xml_failure(parser); 516 } 517 518 XML_ParserFree(parser); 519 520 CharData_CheckXMLChars(&storage, 521 /* clang-format off */ 522 #if XML_GE == 1 523 XCS("e1=v1\n") 524 XCS("e2=(null)\n") 525 XCS("(r(a1=[v1]))\n") 526 XCS("[v1][][&'><\"]") 527 #else 528 XCS("e1=&e1;\n") 529 XCS("e2=(null)\n") 530 XCS("(r(a1=[&e1;]))\n") 531 XCS("[&e1;][&e2;][&'><\"]") 532 #endif 533 ); 534 /* clang-format on */ 535 } 536 END_TEST 537 538 static void XMLCALL 539 resumable_stopping_character_handler(void *userData, const XML_Char *s, 540 int len) { 541 UNUSED_P(s); 542 UNUSED_P(len); 543 XML_Parser parser = (XML_Parser)userData; 544 XML_StopParser(parser, XML_TRUE); 545 } 546 547 // NOTE: This test needs active LeakSanitizer to be of actual use 548 START_TEST(test_misc_char_handler_stop_without_leak) { 549 const char *const data 550 = "<!DOCTYPE t1[<!ENTITY e1 'angle<'><!ENTITY e2 '&e1;'>]><t1>&e2;"; 551 XML_Parser parser = XML_ParserCreate(NULL); 552 assert_true(parser != NULL); 553 XML_SetUserData(parser, parser); 554 XML_SetCharacterDataHandler(parser, resumable_stopping_character_handler); 555 _XML_Parse_SINGLE_BYTES(parser, data, (int)strlen(data), XML_FALSE); 556 XML_ParserFree(parser); 557 } 558 END_TEST 559 560 START_TEST(test_misc_resumeparser_not_crashing) { 561 XML_Parser parser = XML_ParserCreate(NULL); 562 XML_GetBuffer(parser, 1); 563 XML_StopParser(parser, /*resumable=*/XML_TRUE); 564 XML_ResumeParser(parser); // could crash here, previously 565 XML_ParserFree(parser); 566 } 567 END_TEST 568 569 START_TEST(test_misc_stopparser_rejects_unstarted_parser) { 570 const XML_Bool cases[] = {XML_TRUE, XML_FALSE}; 571 for (size_t i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) { 572 const XML_Bool resumable = cases[i]; 573 XML_Parser parser = XML_ParserCreate(NULL); 574 assert_true(XML_GetErrorCode(parser) == XML_ERROR_NONE); 575 assert_true(XML_StopParser(parser, resumable) == XML_STATUS_ERROR); 576 assert_true(XML_GetErrorCode(parser) == XML_ERROR_NOT_STARTED); 577 XML_ParserFree(parser); 578 } 579 } 580 END_TEST 581 582 /* Adaptation of accumulate_characters that takes ExtHdlrData input to work with 583 * test_renter_loop_finite_content below */ 584 void XMLCALL 585 accumulate_characters_ext_handler(void *userData, const XML_Char *s, int len) { 586 ExtHdlrData *const test_data = (ExtHdlrData *)userData; 587 CharData_AppendXMLChars(test_data->storage, s, len); 588 } 589 590 /* Test that internalEntityProcessor does not re-enter forever; 591 * based on files tests/xmlconf/xmltest/valid/ext-sa/012.{xml,ent} */ 592 START_TEST(test_renter_loop_finite_content) { 593 CharData storage; 594 CharData_Init(&storage); 595 const char *const text = "<!DOCTYPE doc [\n" 596 "<!ENTITY e1 '&e2;'>\n" 597 "<!ENTITY e2 '&e3;'>\n" 598 "<!ENTITY e3 SYSTEM '012.ent'>\n" 599 "<!ENTITY e4 '&e5;'>\n" 600 "<!ENTITY e5 '(e5)'>\n" 601 "<!ELEMENT doc (#PCDATA)>\n" 602 "]>\n" 603 "<doc>&e1;</doc>\n"; 604 ExtHdlrData test_data = {"&e4;\n", external_entity_null_loader, &storage}; 605 const XML_Char *const expected = XCS("(e5)\n"); 606 607 XML_Parser parser = XML_ParserCreate(NULL); 608 assert_true(parser != NULL); 609 XML_SetUserData(parser, &test_data); 610 XML_SetExternalEntityRefHandler(parser, external_entity_oneshot_loader); 611 XML_SetCharacterDataHandler(parser, accumulate_characters_ext_handler); 612 if (_XML_Parse_SINGLE_BYTES(parser, text, (int)strlen(text), XML_TRUE) 613 == XML_STATUS_ERROR) 614 xml_failure(parser); 615 616 CharData_CheckXMLChars(&storage, expected); 617 XML_ParserFree(parser); 618 } 619 END_TEST 620 621 // Inspired by function XML_OriginalString of Perl's XML::Parser 622 static char * 623 dup_original_string(XML_Parser parser) { 624 const int byte_count = XML_GetCurrentByteCount(parser); 625 626 assert_true(byte_count >= 0); 627 628 int offset = -1; 629 int size = -1; 630 631 const char *const context = XML_GetInputContext(parser, &offset, &size); 632 633 #if XML_CONTEXT_BYTES > 0 634 assert_true(context != NULL); 635 assert_true(offset >= 0); 636 assert_true(size >= 0); 637 return portable_strndup(context + offset, byte_count); 638 #else 639 assert_true(context == NULL); 640 return NULL; 641 #endif 642 } 643 644 static void 645 on_characters_issue_980(void *userData, const XML_Char *s, int len) { 646 (void)s; 647 (void)len; 648 XML_Parser parser = (XML_Parser)userData; 649 650 char *const original_string = dup_original_string(parser); 651 652 #if XML_CONTEXT_BYTES > 0 653 assert_true(original_string != NULL); 654 assert_true(strcmp(original_string, "&draft.day;") == 0); 655 free(original_string); 656 #else 657 assert_true(original_string == NULL); 658 #endif 659 } 660 661 START_TEST(test_misc_expected_event_ptr_issue_980) { 662 // NOTE: This is a tiny subset of sample "REC-xml-19980210.xml" 663 // from Perl's XML::Parser 664 const char *const doc = "<!DOCTYPE day [\n" 665 " <!ENTITY draft.day '10'>\n" 666 "]>\n" 667 "<day>&draft.day;</day>\n"; 668 669 XML_Parser parser = XML_ParserCreate(NULL); 670 XML_SetUserData(parser, parser); 671 XML_SetCharacterDataHandler(parser, on_characters_issue_980); 672 673 assert_true(_XML_Parse_SINGLE_BYTES(parser, doc, (int)strlen(doc), 674 /*isFinal=*/XML_TRUE) 675 == XML_STATUS_OK); 676 677 XML_ParserFree(parser); 678 } 679 END_TEST 680 681 void 682 make_miscellaneous_test_case(Suite *s) { 683 TCase *tc_misc = tcase_create("miscellaneous tests"); 684 685 suite_add_tcase(s, tc_misc); 686 tcase_add_checked_fixture(tc_misc, NULL, basic_teardown); 687 688 tcase_add_test(tc_misc, test_misc_alloc_create_parser); 689 tcase_add_test(tc_misc, test_misc_alloc_create_parser_with_encoding); 690 tcase_add_test(tc_misc, test_misc_null_parser); 691 tcase_add_test(tc_misc, test_misc_error_string); 692 tcase_add_test(tc_misc, test_misc_version); 693 tcase_add_test(tc_misc, test_misc_features); 694 tcase_add_test(tc_misc, test_misc_attribute_leak); 695 tcase_add_test(tc_misc, test_misc_utf16le); 696 tcase_add_test(tc_misc, test_misc_stop_during_end_handler_issue_240_1); 697 tcase_add_test(tc_misc, test_misc_stop_during_end_handler_issue_240_2); 698 tcase_add_test__ifdef_xml_dtd( 699 tc_misc, test_misc_deny_internal_entity_closing_doctype_issue_317); 700 tcase_add_test(tc_misc, test_misc_tag_mismatch_reset_leak); 701 tcase_add_test(tc_misc, 702 test_misc_create_external_entity_parser_with_null_context); 703 tcase_add_test(tc_misc, test_misc_general_entities_support); 704 tcase_add_test(tc_misc, test_misc_char_handler_stop_without_leak); 705 tcase_add_test(tc_misc, test_misc_resumeparser_not_crashing); 706 tcase_add_test(tc_misc, test_misc_stopparser_rejects_unstarted_parser); 707 tcase_add_test__if_xml_ge(tc_misc, test_renter_loop_finite_content); 708 tcase_add_test(tc_misc, test_misc_expected_event_ptr_issue_980); 709 } 710