diff options
Diffstat (limited to '3rdparty/expat/tests')
-rw-r--r-- | 3rdparty/expat/tests/Makefile.am | 2 | ||||
-rw-r--r-- | 3rdparty/expat/tests/Makefile.in | 2 | ||||
-rw-r--r-- | 3rdparty/expat/tests/acc_tests.c | 5 | ||||
-rw-r--r-- | 3rdparty/expat/tests/alloc_tests.c | 27 | ||||
-rw-r--r-- | 3rdparty/expat/tests/basic_tests.c | 331 | ||||
-rw-r--r-- | 3rdparty/expat/tests/benchmark/benchmark.c | 57 | ||||
-rw-r--r-- | 3rdparty/expat/tests/common.c | 33 | ||||
-rw-r--r-- | 3rdparty/expat/tests/common.h | 4 | ||||
-rw-r--r-- | 3rdparty/expat/tests/handlers.c | 23 | ||||
-rw-r--r-- | 3rdparty/expat/tests/handlers.h | 9 | ||||
-rw-r--r-- | 3rdparty/expat/tests/minicheck.h | 6 | ||||
-rw-r--r-- | 3rdparty/expat/tests/misc_tests.c | 247 | ||||
-rw-r--r-- | 3rdparty/expat/tests/runtests.c | 2 | ||||
-rwxr-xr-x | 3rdparty/expat/tests/xmltest.sh | 5 |
14 files changed, 674 insertions, 79 deletions
diff --git a/3rdparty/expat/tests/Makefile.am b/3rdparty/expat/tests/Makefile.am index 3b1b15302a6..d25376be541 100644 --- a/3rdparty/expat/tests/Makefile.am +++ b/3rdparty/expat/tests/Makefile.am @@ -9,7 +9,7 @@ # Copyright (c) 2017-2024 Sebastian Pipping <sebastian@pipping.org> # Copyright (c) 2017-2022 Rhodri James <rhodri@wildebeest.org.uk> # Copyright (c) 2020 Jeffrey Walton <noloader@gmail.com> -# Copyright (c) 2024 Dag-Erling Smørgrav <des@des.dev> +# Copyright (c) 2024 Dag-Erling Smørgrav <des@des.dev> # Licensed under the MIT license: # # Permission is hereby granted, free of charge, to any person obtaining diff --git a/3rdparty/expat/tests/Makefile.in b/3rdparty/expat/tests/Makefile.in index c1afa35c334..eb00a068cbd 100644 --- a/3rdparty/expat/tests/Makefile.in +++ b/3rdparty/expat/tests/Makefile.in @@ -25,7 +25,7 @@ # Copyright (c) 2017-2024 Sebastian Pipping <sebastian@pipping.org> # Copyright (c) 2017-2022 Rhodri James <rhodri@wildebeest.org.uk> # Copyright (c) 2020 Jeffrey Walton <noloader@gmail.com> -# Copyright (c) 2024 Dag-Erling Smørgrav <des@des.dev> +# Copyright (c) 2024 Dag-Erling Smørgrav <des@des.dev> # Licensed under the MIT license: # # Permission is hereby granted, free of charge, to any person obtaining diff --git a/3rdparty/expat/tests/acc_tests.c b/3rdparty/expat/tests/acc_tests.c index f193aa58a49..b58647a2ab0 100644 --- a/3rdparty/expat/tests/acc_tests.c +++ b/3rdparty/expat/tests/acc_tests.c @@ -360,13 +360,16 @@ END_TEST START_TEST(test_helper_unsigned_char_to_printable) { // Smoke test unsigned char uc = 0; - for (; uc < (unsigned char)-1; uc++) { + for (;; uc++) { set_subtest("char %u", (unsigned)uc); const char *const printable = unsignedCharToPrintable(uc); if (printable == NULL) fail("unsignedCharToPrintable returned NULL"); else if (strlen(printable) < (size_t)1) fail("unsignedCharToPrintable returned empty string"); + if (uc == (unsigned char)-1) { + break; + } } // Two concrete samples diff --git a/3rdparty/expat/tests/alloc_tests.c b/3rdparty/expat/tests/alloc_tests.c index e5d46ebea82..12ea3b2a81d 100644 --- a/3rdparty/expat/tests/alloc_tests.c +++ b/3rdparty/expat/tests/alloc_tests.c @@ -19,6 +19,7 @@ Copyright (c) 2020 Tim Gates <tim.gates@iress.com> Copyright (c) 2021 Donghee Na <donghee.na@python.org> Copyright (c) 2023 Sony Corporation / Snild Dolkow <snild@sony.com> + Copyright (c) 2025 Berkay Eren Ürün <berkay.ueruen@siemens.com> Licensed under the MIT license: Permission is hereby granted, free of charge, to any person obtaining @@ -450,6 +451,31 @@ START_TEST(test_alloc_internal_entity) { } END_TEST +START_TEST(test_alloc_parameter_entity) { + const char *text = "<!DOCTYPE foo [" + "<!ENTITY % param1 \"<!ENTITY internal 'some_text'>\">" + "%param1;" + "]> <foo>&internal;content</foo>"; + int i; + const int alloc_test_max_repeats = 30; + + for (i = 0; i < alloc_test_max_repeats; i++) { + g_allocation_count = i; + XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS); + if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE) + != XML_STATUS_ERROR) + break; + alloc_teardown(); + alloc_setup(); + } + g_allocation_count = -1; + if (i == 0) + fail("Parameter entity processed despite duff allocator"); + if (i == alloc_test_max_repeats) + fail("Parameter entity not processed at max allocation count"); +} +END_TEST + /* Test the robustness against allocation failure of element handling * Based on test_dtd_default_handling(). */ @@ -2079,6 +2105,7 @@ make_alloc_test_case(Suite *s) { tcase_add_test__ifdef_xml_dtd(tc_alloc, test_alloc_external_entity); tcase_add_test__ifdef_xml_dtd(tc_alloc, test_alloc_ext_entity_set_encoding); tcase_add_test__ifdef_xml_dtd(tc_alloc, test_alloc_internal_entity); + tcase_add_test__ifdef_xml_dtd(tc_alloc, test_alloc_parameter_entity); tcase_add_test__ifdef_xml_dtd(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); diff --git a/3rdparty/expat/tests/basic_tests.c b/3rdparty/expat/tests/basic_tests.c index d38b8fd1841..e813df8b6fd 100644 --- a/3rdparty/expat/tests/basic_tests.c +++ b/3rdparty/expat/tests/basic_tests.c @@ -10,7 +10,7 @@ Copyright (c) 2003 Greg Stein <gstein@users.sourceforge.net> Copyright (c) 2005-2007 Steven Solie <steven@solie.ca> Copyright (c) 2005-2012 Karl Waclawek <karl@waclawek.net> - Copyright (c) 2016-2024 Sebastian Pipping <sebastian@pipping.org> + Copyright (c) 2016-2025 Sebastian Pipping <sebastian@pipping.org> Copyright (c) 2017-2022 Rhodri James <rhodri@wildebeest.org.uk> Copyright (c) 2017 Joe Orton <jorton@redhat.com> Copyright (c) 2017 José Gutiérrez de la Concha <jose@zeroc.com> @@ -19,6 +19,7 @@ Copyright (c) 2020 Tim Gates <tim.gates@iress.com> Copyright (c) 2021 Donghee Na <donghee.na@python.org> Copyright (c) 2023-2024 Sony Corporation / Snild Dolkow <snild@sony.com> + Copyright (c) 2024-2025 Berkay Eren Ürün <berkay.ueruen@siemens.com> Licensed under the MIT license: Permission is hereby granted, free of charge, to any person obtaining @@ -1191,6 +1192,22 @@ START_TEST(test_not_standalone_handler_accept) { } END_TEST +START_TEST(test_entity_start_tag_level_greater_than_one) { + const char *const text = "<!DOCTYPE t1 [\n" + " <!ENTITY e1 'hello'>\n" + "]>\n" + "<t1>\n" + " <t2>&e1;</t2>\n" + "</t1>\n"; + + XML_Parser parser = XML_ParserCreate(NULL); + assert_true(_XML_Parse_SINGLE_BYTES(parser, text, (int)strlen(text), + /*isFinal*/ XML_TRUE) + == XML_STATUS_OK); + XML_ParserFree(parser); +} +END_TEST + START_TEST(test_wfc_no_recursive_entity_refs) { const char *text = "<!DOCTYPE doc [\n" " <!ENTITY entity '&entity;'>\n" @@ -1202,6 +1219,93 @@ START_TEST(test_wfc_no_recursive_entity_refs) { } END_TEST +START_TEST(test_no_indirectly_recursive_entity_refs) { + struct TestCase { + const char *doc; + bool usesParameterEntities; + }; + + const struct TestCase cases[] = { + // general entity + character data + {"<!DOCTYPE a [\n" + " <!ENTITY e1 '&e2;'>\n" + " <!ENTITY e2 '&e1;'>\n" + "]><a>&e2;</a>\n", + false}, + + // general entity + attribute value + {"<!DOCTYPE a [\n" + " <!ENTITY e1 '&e2;'>\n" + " <!ENTITY e2 '&e1;'>\n" + "]><a k1='&e2;' />\n", + false}, + + // parameter entity + {"<!DOCTYPE doc [\n" + " <!ENTITY % p1 '%p2;'>\n" + " <!ENTITY % p2 '%p1;'>\n" + " <!ENTITY % define_g \"<!ENTITY g '%p2;'>\">\n" + " %define_g;\n" + "]>\n" + "<doc/>\n", + true}, + }; + const XML_Bool reset_or_not[] = {XML_TRUE, XML_FALSE}; + + for (size_t i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) { + for (size_t j = 0; j < sizeof(reset_or_not) / sizeof(reset_or_not[0]); + j++) { + const XML_Bool reset_wanted = reset_or_not[j]; + const char *const doc = cases[i].doc; + const bool usesParameterEntities = cases[i].usesParameterEntities; + + set_subtest("[%i,reset=%i] %s", (int)i, (int)j, doc); + +#ifdef XML_DTD // both GE and DTD + const bool rejection_expected = true; +#elif XML_GE == 1 // GE but not DTD + const bool rejection_expected = ! usesParameterEntities; +#else // neither DTD nor GE + const bool rejection_expected = false; +#endif + + XML_Parser parser = XML_ParserCreate(NULL); + +#ifdef XML_DTD + if (usesParameterEntities) { + assert_true( + XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS) + == 1); + } +#else + UNUSED_P(usesParameterEntities); +#endif // XML_DTD + + const enum XML_Status status + = _XML_Parse_SINGLE_BYTES(parser, doc, (int)strlen(doc), + /*isFinal*/ XML_TRUE); + + if (rejection_expected) { + assert_true(status == XML_STATUS_ERROR); + assert_true(XML_GetErrorCode(parser) == XML_ERROR_RECURSIVE_ENTITY_REF); + } else { + assert_true(status == XML_STATUS_OK); + } + + if (reset_wanted) { + // This covers free'ing of (eventually) all three open entity lists by + // XML_ParserReset. + XML_ParserReset(parser, NULL); + } + + // This covers free'ing of (eventually) all three open entity lists by + // XML_ParserFree (unless XML_ParserReset has already done that above). + XML_ParserFree(parser); + } + } +} +END_TEST + START_TEST(test_recursive_external_parameter_entity_2) { struct TestCase { const char *doc; @@ -1417,7 +1521,9 @@ START_TEST(test_suspend_parser_between_char_data_calls) { XML_SetCharacterDataHandler(g_parser, clearing_aborting_character_handler); g_resumable = XML_TRUE; - if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE) + // can't use SINGLE_BYTES here, because it'll return early on suspension, and + // we won't know exactly how much input we actually managed to give Expat. + if (XML_Parse(g_parser, text, (int)strlen(text), XML_TRUE) != XML_STATUS_SUSPENDED) xml_failure(g_parser); if (XML_GetErrorCode(g_parser) != XML_ERROR_NONE) @@ -1446,7 +1552,9 @@ START_TEST(test_repeated_stop_parser_between_char_data_calls) { XML_SetCharacterDataHandler(g_parser, parser_stop_character_handler); g_resumable = XML_TRUE; g_abortable = XML_FALSE; - if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE) + // can't use SINGLE_BYTES here, because it'll return early on suspension, and + // we won't know exactly how much input we actually managed to give Expat. + if (XML_Parse(g_parser, text, (int)strlen(text), XML_TRUE) != XML_STATUS_SUSPENDED) fail("Failed to double-suspend parser"); @@ -1830,12 +1938,19 @@ END_TEST /* Test suspending the parser in cdata handler */ START_TEST(test_suspend_parser_between_cdata_calls) { + if (g_chunkSize != 0) { + // this test does not use SINGLE_BYTES, because of suspension + return; + } + const char *text = long_cdata_text; enum XML_Status result; XML_SetCharacterDataHandler(g_parser, clearing_aborting_character_handler); g_resumable = XML_TRUE; - result = _XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE); + // can't use SINGLE_BYTES here, because it'll return early on suspension, and + // we won't know exactly how much input we actually managed to give Expat. + result = XML_Parse(g_parser, text, (int)strlen(text), XML_TRUE); if (result != XML_STATUS_SUSPENDED) { if (result == XML_STATUS_ERROR) xml_failure(g_parser); @@ -2378,6 +2493,11 @@ END_TEST * entity. Exercises some obscure code in XML_ParserReset(). */ START_TEST(test_reset_in_entity) { + if (g_chunkSize != 0) { + // this test does not use SINGLE_BYTES, because of suspension + return; + } + const char *text = "<!DOCTYPE doc [\n" "<!ENTITY wombat 'wom'>\n" "<!ENTITY entity 'hi &wom; there'>\n" @@ -2387,7 +2507,9 @@ START_TEST(test_reset_in_entity) { g_resumable = XML_TRUE; XML_SetCharacterDataHandler(g_parser, clearing_aborting_character_handler); - if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE) + // can't use SINGLE_BYTES here, because it'll return early on suspension, and + // we won't know exactly how much input we actually managed to give Expat. + if (XML_Parse(g_parser, text, (int)strlen(text), XML_TRUE) == XML_STATUS_ERROR) xml_failure(g_parser); XML_GetParsingStatus(g_parser, &status); @@ -3634,7 +3756,9 @@ START_TEST(test_suspend_xdecl) { XML_SetXmlDeclHandler(g_parser, entity_suspending_xdecl_handler); XML_SetUserData(g_parser, g_parser); g_resumable = XML_TRUE; - if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE) + // can't use SINGLE_BYTES here, because it'll return early on suspension, and + // we won't know exactly how much input we actually managed to give Expat. + if (XML_Parse(g_parser, text, (int)strlen(text), XML_TRUE) != XML_STATUS_SUSPENDED) xml_failure(g_parser); if (XML_GetErrorCode(g_parser) != XML_ERROR_NONE) @@ -3830,13 +3954,20 @@ END_TEST /* Test syntax error is caught at parse resumption */ START_TEST(test_resume_entity_with_syntax_error) { + if (g_chunkSize != 0) { + // this test does not use SINGLE_BYTES, because of suspension + return; + } + const char *text = "<!DOCTYPE doc [\n" "<!ENTITY foo '<suspend>Hi</wombat>'>\n" "]>\n" "<doc>&foo;</doc>\n"; XML_SetStartElementHandler(g_parser, start_element_suspender); - if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE) + // can't use SINGLE_BYTES here, because it'll return early on suspension, and + // we won't know exactly how much input we actually managed to give Expat. + if (XML_Parse(g_parser, text, (int)strlen(text), XML_TRUE) != XML_STATUS_SUSPENDED) xml_failure(g_parser); if (XML_ResumeParser(g_parser) != XML_STATUS_ERROR) @@ -3960,7 +4091,7 @@ START_TEST(test_skipped_null_loaded_ext_entity) { = {"<!ENTITY % pe1 SYSTEM 'http://example.org/two.ent'>\n" "<!ENTITY % pe2 '%pe1;'>\n" "%pe2;\n", - external_entity_null_loader}; + external_entity_null_loader, NULL}; XML_SetUserData(g_parser, &test_data); XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS); @@ -3978,7 +4109,7 @@ START_TEST(test_skipped_unloaded_ext_entity) { = {"<!ENTITY % pe1 SYSTEM 'http://example.org/two.ent'>\n" "<!ENTITY % pe2 '%pe1;'>\n" "%pe2;\n", - NULL}; + NULL, NULL}; XML_SetUserData(g_parser, &test_data); XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS); @@ -5278,6 +5409,151 @@ START_TEST(test_pool_integrity_with_unfinished_attr) { } END_TEST +/* Test a possible early return location in internalEntityProcessor */ +START_TEST(test_entity_ref_no_elements) { + const char *const text = "<!DOCTYPE foo [\n" + "<!ENTITY e1 \"test\">\n" + "]> <foo>&e1;"; // intentionally missing newline + + XML_Parser parser = XML_ParserCreate(NULL); + assert_true(_XML_Parse_SINGLE_BYTES(parser, text, (int)strlen(text), XML_TRUE) + == XML_STATUS_ERROR); + assert_true(XML_GetErrorCode(parser) == XML_ERROR_NO_ELEMENTS); + XML_ParserFree(parser); +} +END_TEST + +/* Tests if chained entity references lead to unbounded recursion */ +START_TEST(test_deep_nested_entity) { + const size_t N_LINES = 60000; + const size_t SIZE_PER_LINE = 50; + + char *const text = (char *)malloc((N_LINES + 4) * SIZE_PER_LINE); + if (text == NULL) { + fail("malloc failed"); + } + + char *textPtr = text; + + // Create the XML + textPtr += snprintf(textPtr, SIZE_PER_LINE, + "<!DOCTYPE foo [\n" + " <!ENTITY s0 'deepText'>\n"); + + for (size_t i = 1; i < N_LINES; ++i) { + textPtr += snprintf(textPtr, SIZE_PER_LINE, " <!ENTITY s%lu '&s%lu;'>\n", + (long unsigned)i, (long unsigned)(i - 1)); + } + + snprintf(textPtr, SIZE_PER_LINE, "]> <foo>&s%lu;</foo>\n", + (long unsigned)(N_LINES - 1)); + + const XML_Char *const expected = XCS("deepText"); + + CharData storage; + CharData_Init(&storage); + + XML_Parser parser = XML_ParserCreate(NULL); + + XML_SetCharacterDataHandler(parser, accumulate_characters); + XML_SetUserData(parser, &storage); + + if (_XML_Parse_SINGLE_BYTES(parser, text, (int)strlen(text), XML_TRUE) + == XML_STATUS_ERROR) + xml_failure(parser); + + CharData_CheckXMLChars(&storage, expected); + XML_ParserFree(parser); + free(text); +} +END_TEST + +/* Tests if chained entity references in attributes +lead to unbounded recursion */ +START_TEST(test_deep_nested_attribute_entity) { + const size_t N_LINES = 60000; + const size_t SIZE_PER_LINE = 100; + + char *const text = (char *)malloc((N_LINES + 4) * SIZE_PER_LINE); + if (text == NULL) { + fail("malloc failed"); + } + + char *textPtr = text; + + // Create the XML + textPtr += snprintf(textPtr, SIZE_PER_LINE, + "<!DOCTYPE foo [\n" + " <!ENTITY s0 'deepText'>\n"); + + for (size_t i = 1; i < N_LINES; ++i) { + textPtr += snprintf(textPtr, SIZE_PER_LINE, " <!ENTITY s%lu '&s%lu;'>\n", + (long unsigned)i, (long unsigned)(i - 1)); + } + + snprintf(textPtr, SIZE_PER_LINE, "]> <foo name='&s%lu;'>mainText</foo>\n", + (long unsigned)(N_LINES - 1)); + + AttrInfo doc_info[] = {{XCS("name"), XCS("deepText")}, {NULL, NULL}}; + ElementInfo info[] = {{XCS("foo"), 1, NULL, NULL}, {NULL, 0, NULL, NULL}}; + info[0].attributes = doc_info; + + XML_Parser parser = XML_ParserCreate(NULL); + ParserAndElementInfo parserPlusElemenInfo = {parser, info}; + + XML_SetStartElementHandler(parser, counting_start_element_handler); + XML_SetUserData(parser, &parserPlusElemenInfo); + + if (_XML_Parse_SINGLE_BYTES(parser, text, (int)strlen(text), XML_TRUE) + == XML_STATUS_ERROR) + xml_failure(parser); + + XML_ParserFree(parser); + free(text); +} +END_TEST + +START_TEST(test_deep_nested_entity_delayed_interpretation) { + const size_t N_LINES = 70000; + const size_t SIZE_PER_LINE = 100; + + char *const text = (char *)malloc((N_LINES + 4) * SIZE_PER_LINE); + if (text == NULL) { + fail("malloc failed"); + } + + char *textPtr = text; + + // Create the XML + textPtr += snprintf(textPtr, SIZE_PER_LINE, + "<!DOCTYPE foo [\n" + " <!ENTITY %% s0 'deepText'>\n"); + + for (size_t i = 1; i < N_LINES; ++i) { + textPtr += snprintf(textPtr, SIZE_PER_LINE, + " <!ENTITY %% s%lu '%s%lu;'>\n", (long unsigned)i, + (long unsigned)(i - 1)); + } + + snprintf(textPtr, SIZE_PER_LINE, + " <!ENTITY %% define_g \"<!ENTITY g '%s%lu;'>\">\n" + " %%define_g;\n" + "]>\n" + "<foo/>\n", + (long unsigned)(N_LINES - 1)); + + XML_Parser parser = XML_ParserCreate(NULL); + + XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS); + if (_XML_Parse_SINGLE_BYTES(parser, text, (int)strlen(text), XML_TRUE) + == XML_STATUS_ERROR) + xml_failure(parser); + + XML_ParserFree(parser); + free(text); +} +END_TEST + START_TEST(test_nested_entity_suspend) { const char *const text = "<!DOCTYPE a [\n" " <!ENTITY e1 '<!--e1-->'>\n" @@ -5308,6 +5584,35 @@ START_TEST(test_nested_entity_suspend) { } END_TEST +START_TEST(test_nested_entity_suspend_2) { + const char *const text = "<!DOCTYPE doc [\n" + " <!ENTITY ge1 'head1Ztail1'>\n" + " <!ENTITY ge2 'head2&ge1;tail2'>\n" + " <!ENTITY ge3 'head3&ge2;tail3'>\n" + "]>\n" + "<doc>&ge3;</doc>"; + const XML_Char *const expected = XCS("head3") XCS("head2") XCS("head1") + XCS("Z") XCS("tail1") XCS("tail2") XCS("tail3"); + CharData storage; + CharData_Init(&storage); + XML_Parser parser = XML_ParserCreate(NULL); + ParserPlusStorage parserPlusStorage = {parser, &storage}; + + XML_SetCharacterDataHandler(parser, accumulate_char_data_and_suspend); + XML_SetUserData(parser, &parserPlusStorage); + + enum XML_Status status = XML_Parse(parser, text, (int)strlen(text), XML_TRUE); + while (status == XML_STATUS_SUSPENDED) { + status = XML_ResumeParser(parser); + } + if (status != XML_STATUS_OK) + xml_failure(parser); + + CharData_CheckXMLChars(&storage, expected); + XML_ParserFree(parser); +} +END_TEST + /* Regression test for quadratic parsing on large tokens */ START_TEST(test_big_tokens_scale_linearly) { const struct { @@ -5968,7 +6273,9 @@ make_basic_test_case(Suite *s) { 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_entity_start_tag_level_greater_than_one); tcase_add_test__if_xml_ge(tc_basic, test_wfc_no_recursive_entity_refs); + tcase_add_test(tc_basic, test_no_indirectly_recursive_entity_refs); tcase_add_test__ifdef_xml_dtd(tc_basic, test_ext_entity_invalid_parse); tcase_add_test__if_xml_ge(tc_basic, test_dtd_default_handling); tcase_add_test(tc_basic, test_dtd_attr_handling); @@ -6147,7 +6454,13 @@ make_basic_test_case(Suite *s) { tcase_add_test(tc_basic, test_empty_element_abort); tcase_add_test__ifdef_xml_dtd(tc_basic, test_pool_integrity_with_unfinished_attr); + tcase_add_test__if_xml_ge(tc_basic, test_entity_ref_no_elements); + tcase_add_test__if_xml_ge(tc_basic, test_deep_nested_entity); + tcase_add_test__if_xml_ge(tc_basic, test_deep_nested_attribute_entity); + tcase_add_test__if_xml_ge(tc_basic, + test_deep_nested_entity_delayed_interpretation); tcase_add_test__if_xml_ge(tc_basic, test_nested_entity_suspend); + tcase_add_test__if_xml_ge(tc_basic, test_nested_entity_suspend_2); tcase_add_test(tc_basic, test_big_tokens_scale_linearly); tcase_add_test(tc_basic, test_set_reparse_deferral); tcase_add_test(tc_basic, test_reparse_deferral_is_inherited); diff --git a/3rdparty/expat/tests/benchmark/benchmark.c b/3rdparty/expat/tests/benchmark/benchmark.c index 355d83f896d..a02b84a0131 100644 --- a/3rdparty/expat/tests/benchmark/benchmark.c +++ b/3rdparty/expat/tests/benchmark/benchmark.c @@ -8,7 +8,7 @@ Copyright (c) 2003-2006 Karl Waclawek <karl@waclawek.net> Copyright (c) 2005-2007 Steven Solie <steven@solie.ca> - Copyright (c) 2017-2023 Sebastian Pipping <sebastian@pipping.org> + Copyright (c) 2017-2025 Sebastian Pipping <sebastian@pipping.org> Copyright (c) 2017 Rhodri James <rhodri@wildebeest.org.uk> Licensed under the MIT license: @@ -32,10 +32,18 @@ USE OR OTHER DEALINGS IN THE SOFTWARE. */ +#define _POSIX_C_SOURCE 1 // fdopen + +#if defined(_MSC_VER) +# include <io.h> // _open, _close +#else +# include <unistd.h> // close +#endif + +#include <fcntl.h> // open #include <sys/stat.h> #include <assert.h> #include <stddef.h> // ptrdiff_t -#include <stdlib.h> #include <stdio.h> #include <time.h> #include "expat.h" @@ -52,17 +60,18 @@ # define XML_FMT_STR "s" #endif -static void +static int usage(const char *prog, int rc) { fprintf(stderr, "usage: %s [-n] filename bufferSize nr_of_loops\n", prog); - exit(rc); + return rc; } int main(int argc, char *argv[]) { XML_Parser parser; char *XMLBuf, *XMLBufEnd, *XMLBufPtr; - FILE *fd; + int fd; + FILE *file; struct stat fileAttr; int nrOfLoops, bufferSize, i, isFinal; size_t fileSize; @@ -76,34 +85,48 @@ main(int argc, char *argv[]) { ns = 1; j = 1; } else - usage(argv[0], 1); + return usage(argv[0], 1); } } if (argc != j + 4) - usage(argv[0], 1); + return usage(argv[0], 1); - if (stat(argv[j + 1], &fileAttr) != 0) { - fprintf(stderr, "could not access file '%s'\n", argv[j + 1]); + fd = open(argv[j + 1], O_RDONLY); + if (fd == -1) { + fprintf(stderr, "could not open file '%s'\n", argv[j + 1]); return 2; } - fd = fopen(argv[j + 1], "r"); - if (! fd) { - fprintf(stderr, "could not open file '%s'\n", argv[j + 1]); - exit(2); + if (fstat(fd, &fileAttr) != 0) { + close(fd); + fprintf(stderr, "could not fstat file '%s'\n", argv[j + 1]); + return 2; + } + + file = fdopen(fd, "r"); + if (! file) { + close(fd); + fprintf(stderr, "could not fdopen file '%s'\n", argv[j + 1]); + return 2; } bufferSize = atoi(argv[j + 2]); nrOfLoops = atoi(argv[j + 3]); if (bufferSize <= 0 || nrOfLoops <= 0) { + fclose(file); // NOTE: this closes fd as well fprintf(stderr, "buffer size and nr of loops must be greater than zero.\n"); - exit(3); + return 3; } XMLBuf = malloc(fileAttr.st_size); - fileSize = fread(XMLBuf, sizeof(char), fileAttr.st_size, fd); - fclose(fd); + if (XMLBuf == NULL) { + fclose(file); // NOTE: this closes fd as well + fprintf(stderr, "ouf of memory.\n"); + return 5; + } + fileSize = fread(XMLBuf, sizeof(char), fileAttr.st_size, file); + fclose(file); // NOTE: this closes fd as well if (ns) parser = XML_ParserCreateNS(NULL, '!'); @@ -132,7 +155,7 @@ main(int argc, char *argv[]) { XML_GetCurrentColumnNumber(parser)); free(XMLBuf); XML_ParserFree(parser); - exit(4); + return 4; } XMLBufPtr += bufferSize; } while (! isFinal); diff --git a/3rdparty/expat/tests/common.c b/3rdparty/expat/tests/common.c index 3aea8d74d1e..b158385f56a 100644 --- a/3rdparty/expat/tests/common.c +++ b/3rdparty/expat/tests/common.c @@ -10,7 +10,7 @@ Copyright (c) 2003 Greg Stein <gstein@users.sourceforge.net> Copyright (c) 2005-2007 Steven Solie <steven@solie.ca> Copyright (c) 2005-2012 Karl Waclawek <karl@waclawek.net> - Copyright (c) 2016-2024 Sebastian Pipping <sebastian@pipping.org> + Copyright (c) 2016-2025 Sebastian Pipping <sebastian@pipping.org> Copyright (c) 2017-2022 Rhodri James <rhodri@wildebeest.org.uk> Copyright (c) 2017 Joe Orton <jorton@redhat.com> Copyright (c) 2017 José Gutiérrez de la Concha <jose@zeroc.com> @@ -42,6 +42,8 @@ */ #include <assert.h> +#include <errno.h> +#include <stdint.h> // for SIZE_MAX #include <stdio.h> #include <string.h> @@ -202,6 +204,12 @@ _XML_Parse_SINGLE_BYTES(XML_Parser parser, const char *s, int len, for (; len > chunksize; len -= chunksize, s += chunksize) { enum XML_Status res = XML_Parse(parser, s, chunksize, XML_FALSE); if (res != XML_STATUS_OK) { + if ((res == XML_STATUS_SUSPENDED) && (len > chunksize)) { + fail("Use of function _XML_Parse_SINGLE_BYTES with a chunk size " + "greater than 0 (from g_chunkSize) does not work well with " + "suspension. Please consider use of plain XML_Parse at this " + "place in your test, instead."); + } return res; } } @@ -294,3 +302,26 @@ duff_reallocator(void *ptr, size_t size) { g_reallocation_count--; return realloc(ptr, size); } + +// Portable remake of strndup(3) for C99; does not care about space efficiency +char * +portable_strndup(const char *s, size_t n) { + if ((s == NULL) || (n == SIZE_MAX)) { + errno = EINVAL; + return NULL; + } + + char *const buffer = (char *)malloc(n + 1); + if (buffer == NULL) { + errno = ENOMEM; + return NULL; + } + + errno = 0; + + memcpy(buffer, s, n); + + buffer[n] = '\0'; + + return buffer; +} diff --git a/3rdparty/expat/tests/common.h b/3rdparty/expat/tests/common.h index bc4c7da6807..2d1a5f207a0 100644 --- a/3rdparty/expat/tests/common.h +++ b/3rdparty/expat/tests/common.h @@ -10,7 +10,7 @@ Copyright (c) 2003 Greg Stein <gstein@users.sourceforge.net> Copyright (c) 2005-2007 Steven Solie <steven@solie.ca> Copyright (c) 2005-2012 Karl Waclawek <karl@waclawek.net> - Copyright (c) 2016-2024 Sebastian Pipping <sebastian@pipping.org> + Copyright (c) 2016-2025 Sebastian Pipping <sebastian@pipping.org> Copyright (c) 2017-2022 Rhodri James <rhodri@wildebeest.org.uk> Copyright (c) 2017 Joe Orton <jorton@redhat.com> Copyright (c) 2017 José Gutiérrez de la Concha <jose@zeroc.com> @@ -146,6 +146,8 @@ extern void *duff_allocator(size_t size); extern void *duff_reallocator(void *ptr, size_t size); +extern char *portable_strndup(const char *s, size_t n); + #endif /* XML_COMMON_H */ #ifdef __cplusplus diff --git a/3rdparty/expat/tests/handlers.c b/3rdparty/expat/tests/handlers.c index 0211985fe95..ac459507580 100644 --- a/3rdparty/expat/tests/handlers.c +++ b/3rdparty/expat/tests/handlers.c @@ -1843,6 +1843,15 @@ element_decl_suspender(void *userData, const XML_Char *name, } void XMLCALL +suspend_after_element_declaration(void *userData, const XML_Char *name, + XML_Content *model) { + UNUSED_P(name); + XML_Parser parser = (XML_Parser)userData; + assert_true(XML_StopParser(parser, /*resumable*/ XML_TRUE) == XML_STATUS_OK); + XML_FreeContentModel(parser, model); +} + +void XMLCALL accumulate_pi_characters(void *userData, const XML_Char *target, const XML_Char *data) { CharData *storage = (CharData *)userData; @@ -1883,6 +1892,20 @@ accumulate_entity_decl(void *userData, const XML_Char *entityName, } void XMLCALL +accumulate_char_data_and_suspend(void *userData, const XML_Char *s, int len) { + ParserPlusStorage *const parserPlusStorage = (ParserPlusStorage *)userData; + + CharData_AppendXMLChars(parserPlusStorage->storage, s, len); + + for (int i = 0; i < len; i++) { + if (s[i] == 'Z') { + XML_StopParser(parserPlusStorage->parser, /*resumable=*/XML_TRUE); + break; + } + } +} + +void XMLCALL accumulate_start_element(void *userData, const XML_Char *name, const XML_Char **atts) { CharData *const storage = (CharData *)userData; diff --git a/3rdparty/expat/tests/handlers.h b/3rdparty/expat/tests/handlers.h index 8850bb948da..fa6267fbbd0 100644 --- a/3rdparty/expat/tests/handlers.h +++ b/3rdparty/expat/tests/handlers.h @@ -325,6 +325,7 @@ extern int XMLCALL external_entity_devaluer(XML_Parser parser, typedef struct ext_hdlr_data { const char *parse_text; XML_ExternalEntityRefHandler handler; + CharData *storage; } ExtHdlrData; extern int XMLCALL external_entity_oneshot_loader(XML_Parser parser, @@ -557,6 +558,10 @@ extern void XMLCALL suspending_comment_handler(void *userData, extern void XMLCALL element_decl_suspender(void *userData, const XML_Char *name, XML_Content *model); +extern void XMLCALL suspend_after_element_declaration(void *userData, + const XML_Char *name, + XML_Content *model); + extern void XMLCALL accumulate_pi_characters(void *userData, const XML_Char *target, const XML_Char *data); @@ -569,6 +574,10 @@ extern void XMLCALL accumulate_entity_decl( const XML_Char *systemId, const XML_Char *publicId, const XML_Char *notationName); +extern void XMLCALL accumulate_char_data_and_suspend(void *userData, + const XML_Char *s, + int len); + extern void XMLCALL accumulate_start_element(void *userData, const XML_Char *name, const XML_Char **atts); diff --git a/3rdparty/expat/tests/minicheck.h b/3rdparty/expat/tests/minicheck.h index 3d888f8d2a3..29ae4cb2420 100644 --- a/3rdparty/expat/tests/minicheck.h +++ b/3rdparty/expat/tests/minicheck.h @@ -14,7 +14,7 @@ Copyright (c) 2004-2006 Fred L. Drake, Jr. <fdrake@users.sourceforge.net> Copyright (c) 2006-2012 Karl Waclawek <karl@waclawek.net> - Copyright (c) 2016-2024 Sebastian Pipping <sebastian@pipping.org> + Copyright (c) 2016-2025 Sebastian Pipping <sebastian@pipping.org> Copyright (c) 2022 Rhodri James <rhodri@wildebeest.org.uk> Copyright (c) 2023-2024 Sony Corporation / Snild Dolkow <snild@sony.com> Licensed under the MIT license: @@ -129,8 +129,10 @@ void _check_set_test_info(char const *function, char const *filename, * Prototypes for the actual implementation. */ -# if defined(__GNUC__) +# if defined(__has_attribute) +# if __has_attribute(noreturn) __attribute__((noreturn)) +# endif # endif void _fail(const char *file, int line, const char *msg); diff --git a/3rdparty/expat/tests/misc_tests.c b/3rdparty/expat/tests/misc_tests.c index 9afe0922d6b..fb95014b142 100644 --- a/3rdparty/expat/tests/misc_tests.c +++ b/3rdparty/expat/tests/misc_tests.c @@ -10,7 +10,7 @@ Copyright (c) 2003 Greg Stein <gstein@users.sourceforge.net> Copyright (c) 2005-2007 Steven Solie <steven@solie.ca> Copyright (c) 2005-2012 Karl Waclawek <karl@waclawek.net> - Copyright (c) 2016-2024 Sebastian Pipping <sebastian@pipping.org> + Copyright (c) 2016-2025 Sebastian Pipping <sebastian@pipping.org> Copyright (c) 2017-2022 Rhodri James <rhodri@wildebeest.org.uk> Copyright (c) 2017 Joe Orton <jorton@redhat.com> Copyright (c) 2017 José Gutiérrez de la Concha <jose@zeroc.com> @@ -59,6 +59,9 @@ #include "handlers.h" #include "misc_tests.h" +void XMLCALL accumulate_characters_ext_handler(void *userData, + const XML_Char *s, int len); + /* 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}; @@ -208,7 +211,7 @@ START_TEST(test_misc_version) { if (! versions_equal(&read_version, &parsed_version)) fail("Version mismatch"); - if (xcstrcmp(version_text, XCS("expat_2.6.4"))) /* needs bump on releases */ + if (xcstrcmp(version_text, XCS("expat_2.7.1"))) /* needs bump on releases */ fail("XML_*_VERSION in expat.h out of sync?\n"); } END_TEST @@ -294,6 +297,7 @@ START_TEST(test_misc_stop_during_end_handler_issue_240_1) { parser = XML_ParserCreate(NULL); XML_SetElementHandler(parser, start_element_issue_240, end_element_issue_240); mydata = (DataIssue240 *)malloc(sizeof(DataIssue240)); + assert_true(mydata != NULL); mydata->parser = parser; mydata->deep = 0; XML_SetUserData(parser, mydata); @@ -315,6 +319,7 @@ START_TEST(test_misc_stop_during_end_handler_issue_240_2) { parser = XML_ParserCreate(NULL); XML_SetElementHandler(parser, start_element_issue_240, end_element_issue_240); mydata = (DataIssue240 *)malloc(sizeof(DataIssue240)); + assert_true(mydata != NULL); mydata->parser = parser; mydata->deep = 0; XML_SetUserData(parser, mydata); @@ -328,64 +333,119 @@ START_TEST(test_misc_stop_during_end_handler_issue_240_2) { END_TEST START_TEST(test_misc_deny_internal_entity_closing_doctype_issue_317) { - const char *const inputOne = "<!DOCTYPE d [\n" - "<!ENTITY % e ']><d/>'>\n" - "\n" - "%e;"; + const char *const inputOne + = "<!DOCTYPE d [\n" + "<!ENTITY % element_d '<!ELEMENT d (#PCDATA)*>'>\n" + "%element_d;\n" + "<!ENTITY % e ']><d/>'>\n" + "\n" + "%e;"; const char *const inputTwo = "<!DOCTYPE d [\n" + "<!ENTITY % element_d '<!ELEMENT d (#PCDATA)*>'>\n" + "%element_d;\n" "<!ENTITY % e1 ']><d/>'><!ENTITY % e2 '%e1;'>\n" "\n" "%e2;"; - const char *const inputThree = "<!DOCTYPE d [\n" - "<!ENTITY % e ']><d'>\n" - "\n" - "%e;/>"; - const char *const inputIssue317 = "<!DOCTYPE doc [\n" - "<!ENTITY % foo ']>\n" - "<doc>Hell<oc (#PCDATA)*>'>\n" - "%foo;\n" - "]>\n" - "<doc>Hello, world</dVc>"; + const char *const inputThree + = "<!DOCTYPE d [\n" + "<!ENTITY % element_d '<!ELEMENT d (#PCDATA)*>'>\n" + "%element_d;\n" + "<!ENTITY % e ']><d'>\n" + "\n" + "%e;/>"; + const char *const inputIssue317 + = "<!DOCTYPE doc [\n" + "<!ENTITY % element_doc '<!ELEMENT doc (#PCDATA)*>'>\n" + "%element_doc;\n" + "<!ENTITY % foo ']>\n" + "<doc>Hell<oc (#PCDATA)*>'>\n" + "%foo;\n" + "]>\n" + "<doc>Hello, world</dVc>"; const char *const inputs[] = {inputOne, inputTwo, inputThree, inputIssue317}; + const XML_Bool suspendOrNot[] = {XML_FALSE, XML_TRUE}; size_t inputIndex = 0; for (; inputIndex < sizeof(inputs) / sizeof(inputs[0]); inputIndex++) { - set_subtest("%s", inputs[inputIndex]); - XML_Parser parser; - enum XML_Status parseResult; - int setParamEntityResult; - XML_Size lineNumber; - XML_Size columnNumber; - const char *const input = inputs[inputIndex]; - - parser = XML_ParserCreate(NULL); - setParamEntityResult - = XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS); - if (setParamEntityResult != 1) - fail("Failed to set XML_PARAM_ENTITY_PARSING_ALWAYS."); - - parseResult = _XML_Parse_SINGLE_BYTES(parser, input, (int)strlen(input), 0); - if (parseResult != XML_STATUS_ERROR) { - parseResult = _XML_Parse_SINGLE_BYTES(parser, "", 0, 1); + for (size_t suspendOrNotIndex = 0; + suspendOrNotIndex < sizeof(suspendOrNot) / sizeof(suspendOrNot[0]); + suspendOrNotIndex++) { + const char *const input = inputs[inputIndex]; + const XML_Bool suspend = suspendOrNot[suspendOrNotIndex]; + if (suspend && (g_chunkSize > 0)) { + // We cannot use _XML_Parse_SINGLE_BYTES below due to suspension, and + // so chunk sizes >0 would only repeat the very same test + // due to use of plain XML_Parse; we are saving upon that runtime: + return; + } + + set_subtest("[input=%d suspend=%s] %s", (int)inputIndex, + suspend ? "true" : "false", input); + XML_Parser parser; + enum XML_Status parseResult; + int setParamEntityResult; + XML_Size lineNumber; + XML_Size columnNumber; + + parser = XML_ParserCreate(NULL); + setParamEntityResult + = XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS); + if (setParamEntityResult != 1) + fail("Failed to set XML_PARAM_ENTITY_PARSING_ALWAYS."); + + if (suspend) { + XML_SetUserData(parser, parser); + XML_SetElementDeclHandler(parser, suspend_after_element_declaration); + } + + if (suspend) { + // can't use SINGLE_BYTES here, because it'll return early on + // suspension, and we won't know exactly how much input we actually + // managed to give Expat. + parseResult = XML_Parse(parser, input, (int)strlen(input), 0); + + while (parseResult == XML_STATUS_SUSPENDED) { + parseResult = XML_ResumeParser(parser); + } + + if (parseResult != XML_STATUS_ERROR) { + // can't use SINGLE_BYTES here, because it'll return early on + // suspension, and we won't know exactly how much input we actually + // managed to give Expat. + parseResult = XML_Parse(parser, "", 0, 1); + } + + while (parseResult == XML_STATUS_SUSPENDED) { + parseResult = XML_ResumeParser(parser); + } + } else { + parseResult + = _XML_Parse_SINGLE_BYTES(parser, input, (int)strlen(input), 0); + + if (parseResult != XML_STATUS_ERROR) { + parseResult = _XML_Parse_SINGLE_BYTES(parser, "", 0, 1); + } + } + if (parseResult != XML_STATUS_ERROR) { fail("Parsing was expected to fail but succeeded."); } - } - if (XML_GetErrorCode(parser) != XML_ERROR_INVALID_TOKEN) - fail("Error code does not match XML_ERROR_INVALID_TOKEN"); + if (XML_GetErrorCode(parser) != XML_ERROR_INVALID_TOKEN) + fail("Error code does not match XML_ERROR_INVALID_TOKEN"); - lineNumber = XML_GetCurrentLineNumber(parser); - if (lineNumber != 4) - fail("XML_GetCurrentLineNumber does not work as expected."); + lineNumber = XML_GetCurrentLineNumber(parser); + if (lineNumber != 6) + fail("XML_GetCurrentLineNumber does not work as expected."); - columnNumber = XML_GetCurrentColumnNumber(parser); - if (columnNumber != 0) - fail("XML_GetCurrentColumnNumber does not work as expected."); + columnNumber = XML_GetCurrentColumnNumber(parser); + if (columnNumber != 0) + fail("XML_GetCurrentColumnNumber does not work as expected."); - XML_ParserFree(parser); + XML_ParserFree(parser); + } } } END_TEST @@ -519,6 +579,105 @@ START_TEST(test_misc_stopparser_rejects_unstarted_parser) { } END_TEST +/* Adaptation of accumulate_characters that takes ExtHdlrData input to work with + * test_renter_loop_finite_content below */ +void XMLCALL +accumulate_characters_ext_handler(void *userData, const XML_Char *s, int len) { + ExtHdlrData *const test_data = (ExtHdlrData *)userData; + CharData_AppendXMLChars(test_data->storage, s, len); +} + +/* Test that internalEntityProcessor does not re-enter forever; + * based on files tests/xmlconf/xmltest/valid/ext-sa/012.{xml,ent} */ +START_TEST(test_renter_loop_finite_content) { + CharData storage; + CharData_Init(&storage); + const char *const text = "<!DOCTYPE doc [\n" + "<!ENTITY e1 '&e2;'>\n" + "<!ENTITY e2 '&e3;'>\n" + "<!ENTITY e3 SYSTEM '012.ent'>\n" + "<!ENTITY e4 '&e5;'>\n" + "<!ENTITY e5 '(e5)'>\n" + "<!ELEMENT doc (#PCDATA)>\n" + "]>\n" + "<doc>&e1;</doc>\n"; + ExtHdlrData test_data = {"&e4;\n", external_entity_null_loader, &storage}; + const XML_Char *const expected = XCS("(e5)\n"); + + XML_Parser parser = XML_ParserCreate(NULL); + assert_true(parser != NULL); + XML_SetUserData(parser, &test_data); + XML_SetExternalEntityRefHandler(parser, external_entity_oneshot_loader); + XML_SetCharacterDataHandler(parser, accumulate_characters_ext_handler); + if (_XML_Parse_SINGLE_BYTES(parser, text, (int)strlen(text), XML_TRUE) + == XML_STATUS_ERROR) + xml_failure(parser); + + CharData_CheckXMLChars(&storage, expected); + XML_ParserFree(parser); +} +END_TEST + +// Inspired by function XML_OriginalString of Perl's XML::Parser +static char * +dup_original_string(XML_Parser parser) { + const int byte_count = XML_GetCurrentByteCount(parser); + + assert_true(byte_count >= 0); + + int offset = -1; + int size = -1; + + const char *const context = XML_GetInputContext(parser, &offset, &size); + +#if XML_CONTEXT_BYTES > 0 + assert_true(context != NULL); + assert_true(offset >= 0); + assert_true(size >= 0); + return portable_strndup(context + offset, byte_count); +#else + assert_true(context == NULL); + return NULL; +#endif +} + +static void +on_characters_issue_980(void *userData, const XML_Char *s, int len) { + (void)s; + (void)len; + XML_Parser parser = (XML_Parser)userData; + + char *const original_string = dup_original_string(parser); + +#if XML_CONTEXT_BYTES > 0 + assert_true(original_string != NULL); + assert_true(strcmp(original_string, "&draft.day;") == 0); + free(original_string); +#else + assert_true(original_string == NULL); +#endif +} + +START_TEST(test_misc_expected_event_ptr_issue_980) { + // NOTE: This is a tiny subset of sample "REC-xml-19980210.xml" + // from Perl's XML::Parser + const char *const doc = "<!DOCTYPE day [\n" + " <!ENTITY draft.day '10'>\n" + "]>\n" + "<day>&draft.day;</day>\n"; + + XML_Parser parser = XML_ParserCreate(NULL); + XML_SetUserData(parser, parser); + XML_SetCharacterDataHandler(parser, on_characters_issue_980); + + assert_true(_XML_Parse_SINGLE_BYTES(parser, doc, (int)strlen(doc), + /*isFinal=*/XML_TRUE) + == XML_STATUS_OK); + + XML_ParserFree(parser); +} +END_TEST + void make_miscellaneous_test_case(Suite *s) { TCase *tc_misc = tcase_create("miscellaneous tests"); @@ -545,4 +704,6 @@ make_miscellaneous_test_case(Suite *s) { tcase_add_test(tc_misc, test_misc_char_handler_stop_without_leak); tcase_add_test(tc_misc, test_misc_resumeparser_not_crashing); tcase_add_test(tc_misc, test_misc_stopparser_rejects_unstarted_parser); + tcase_add_test__if_xml_ge(tc_misc, test_renter_loop_finite_content); + tcase_add_test(tc_misc, test_misc_expected_event_ptr_issue_980); } diff --git a/3rdparty/expat/tests/runtests.c b/3rdparty/expat/tests/runtests.c index 5feaaabe226..ecb1c36be58 100644 --- a/3rdparty/expat/tests/runtests.c +++ b/3rdparty/expat/tests/runtests.c @@ -13,7 +13,7 @@ Copyright (c) 2016-2023 Sebastian Pipping <sebastian@pipping.org> Copyright (c) 2017-2022 Rhodri James <rhodri@wildebeest.org.uk> Copyright (c) 2017 Joe Orton <jorton@redhat.com> - Copyright (c) 2017 José Gutiérrez de la Concha <jose@zeroc.com> + Copyright (c) 2017 José Gutiérrez de la Concha <jose@zeroc.com> Copyright (c) 2018 Marco Maggi <marco.maggi-ipsu@poste.it> Copyright (c) 2019 David Loffredo <loffredo@steptools.com> Copyright (c) 2020 Tim Gates <tim.gates@iress.com> diff --git a/3rdparty/expat/tests/xmltest.sh b/3rdparty/expat/tests/xmltest.sh index dc409d01e45..56e66c56f6e 100755 --- a/3rdparty/expat/tests/xmltest.sh +++ b/3rdparty/expat/tests/xmltest.sh @@ -2,8 +2,8 @@ # EXPAT TEST SCRIPT FOR W3C XML TEST SUITE # # This script can be used to exercise Expat against the -# w3c.org xml test suite, available from -# http://www.w3.org/XML/Test/xmlts20020606.zip. +# w3c.org xml test suite, available from: +# https://www.w3.org/XML/Test/xmlts20020606.zip # # To run this script, first set XMLWF below so that xmlwf can be # found, then set the output directory with OUTPUT. @@ -30,6 +30,7 @@ # Copyright (c) 2002 Karl Waclawek <karl@waclawek.net> # Copyright (c) 2008-2019 Sebastian Pipping <sebastian@pipping.org> # Copyright (c) 2017 Rhodri James <rhodri@wildebeest.org.uk> +# Copyright (c) 2025 Hanno Böck <hanno@gentoo.org> # Licensed under the MIT license: # # Permission is hereby granted, free of charge, to any person obtaining |