14543ef51SXin LI /* Tests in the "miscellaneous" test case for the Expat test suite
24543ef51SXin LI __ __ _
34543ef51SXin LI ___\ \/ /_ __ __ _| |_
44543ef51SXin LI / _ \\ /| '_ \ / _` | __|
54543ef51SXin LI | __// \| |_) | (_| | |_
64543ef51SXin LI \___/_/\_\ .__/ \__,_|\__|
74543ef51SXin LI |_| XML parser
84543ef51SXin LI
94543ef51SXin LI Copyright (c) 2001-2006 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
104543ef51SXin LI Copyright (c) 2003 Greg Stein <gstein@users.sourceforge.net>
114543ef51SXin LI Copyright (c) 2005-2007 Steven Solie <steven@solie.ca>
124543ef51SXin LI Copyright (c) 2005-2012 Karl Waclawek <karl@waclawek.net>
13*fe927888SPhilip Paeps Copyright (c) 2016-2025 Sebastian Pipping <sebastian@pipping.org>
144543ef51SXin LI Copyright (c) 2017-2022 Rhodri James <rhodri@wildebeest.org.uk>
154543ef51SXin LI Copyright (c) 2017 Joe Orton <jorton@redhat.com>
164543ef51SXin LI Copyright (c) 2017 José Gutiérrez de la Concha <jose@zeroc.com>
174543ef51SXin LI Copyright (c) 2018 Marco Maggi <marco.maggi-ipsu@poste.it>
184543ef51SXin LI Copyright (c) 2019 David Loffredo <loffredo@steptools.com>
194543ef51SXin LI Copyright (c) 2020 Tim Gates <tim.gates@iress.com>
204543ef51SXin LI Copyright (c) 2021 Donghee Na <donghee.na@python.org>
214543ef51SXin LI Copyright (c) 2023 Sony Corporation / Snild Dolkow <snild@sony.com>
224543ef51SXin LI Licensed under the MIT license:
234543ef51SXin LI
244543ef51SXin LI Permission is hereby granted, free of charge, to any person obtaining
254543ef51SXin LI a copy of this software and associated documentation files (the
264543ef51SXin LI "Software"), to deal in the Software without restriction, including
274543ef51SXin LI without limitation the rights to use, copy, modify, merge, publish,
284543ef51SXin LI distribute, sublicense, and/or sell copies of the Software, and to permit
294543ef51SXin LI persons to whom the Software is furnished to do so, subject to the
304543ef51SXin LI following conditions:
314543ef51SXin LI
324543ef51SXin LI The above copyright notice and this permission notice shall be included
334543ef51SXin LI in all copies or substantial portions of the Software.
344543ef51SXin LI
354543ef51SXin LI THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
364543ef51SXin LI EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
374543ef51SXin LI MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
384543ef51SXin LI NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
394543ef51SXin LI DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
404543ef51SXin LI OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
414543ef51SXin LI USE OR OTHER DEALINGS IN THE SOFTWARE.
424543ef51SXin LI */
434543ef51SXin LI
444543ef51SXin LI #if defined(NDEBUG)
454543ef51SXin LI # undef NDEBUG /* because test suite relies on assert(...) at the moment */
464543ef51SXin LI #endif
474543ef51SXin LI
484543ef51SXin LI #include <assert.h>
494543ef51SXin LI #include <string.h>
504543ef51SXin LI
514543ef51SXin LI #include "expat_config.h"
524543ef51SXin LI
534543ef51SXin LI #include "expat.h"
544543ef51SXin LI #include "internal.h"
554543ef51SXin LI #include "minicheck.h"
564543ef51SXin LI #include "memcheck.h"
574543ef51SXin LI #include "common.h"
584543ef51SXin LI #include "ascii.h" /* for ASCII_xxx */
594543ef51SXin LI #include "handlers.h"
604543ef51SXin LI #include "misc_tests.h"
614543ef51SXin LI
62*fe927888SPhilip Paeps void XMLCALL accumulate_characters_ext_handler(void *userData,
63*fe927888SPhilip Paeps const XML_Char *s, int len);
64*fe927888SPhilip Paeps
654543ef51SXin LI /* Test that a failure to allocate the parser structure fails gracefully */
START_TEST(test_misc_alloc_create_parser)664543ef51SXin LI START_TEST(test_misc_alloc_create_parser) {
674543ef51SXin LI XML_Memory_Handling_Suite memsuite = {duff_allocator, realloc, free};
684543ef51SXin LI unsigned int i;
694543ef51SXin LI const unsigned int max_alloc_count = 10;
704543ef51SXin LI
714543ef51SXin LI /* Something this simple shouldn't need more than 10 allocations */
724543ef51SXin LI for (i = 0; i < max_alloc_count; i++) {
734543ef51SXin LI g_allocation_count = i;
744543ef51SXin LI g_parser = XML_ParserCreate_MM(NULL, &memsuite, NULL);
754543ef51SXin LI if (g_parser != NULL)
764543ef51SXin LI break;
774543ef51SXin LI }
784543ef51SXin LI if (i == 0)
794543ef51SXin LI fail("Parser unexpectedly ignored failing allocator");
804543ef51SXin LI else if (i == max_alloc_count)
814543ef51SXin LI fail("Parser not created with max allocation count");
824543ef51SXin LI }
834543ef51SXin LI END_TEST
844543ef51SXin LI
854543ef51SXin LI /* Test memory allocation failures for a parser with an encoding */
START_TEST(test_misc_alloc_create_parser_with_encoding)864543ef51SXin LI START_TEST(test_misc_alloc_create_parser_with_encoding) {
874543ef51SXin LI XML_Memory_Handling_Suite memsuite = {duff_allocator, realloc, free};
884543ef51SXin LI unsigned int i;
894543ef51SXin LI const unsigned int max_alloc_count = 10;
904543ef51SXin LI
914543ef51SXin LI /* Try several levels of allocation */
924543ef51SXin LI for (i = 0; i < max_alloc_count; i++) {
934543ef51SXin LI g_allocation_count = i;
944543ef51SXin LI g_parser = XML_ParserCreate_MM(XCS("us-ascii"), &memsuite, NULL);
954543ef51SXin LI if (g_parser != NULL)
964543ef51SXin LI break;
974543ef51SXin LI }
984543ef51SXin LI if (i == 0)
994543ef51SXin LI fail("Parser ignored failing allocator");
1004543ef51SXin LI else if (i == max_alloc_count)
1014543ef51SXin LI fail("Parser not created with max allocation count");
1024543ef51SXin LI }
1034543ef51SXin LI END_TEST
1044543ef51SXin LI
1054543ef51SXin LI /* Test that freeing a NULL parser doesn't cause an explosion.
1064543ef51SXin LI * (Not actually tested anywhere else)
1074543ef51SXin LI */
START_TEST(test_misc_null_parser)1084543ef51SXin LI START_TEST(test_misc_null_parser) {
1094543ef51SXin LI XML_ParserFree(NULL);
1104543ef51SXin LI }
1114543ef51SXin LI END_TEST
1124543ef51SXin LI
1134543ef51SXin LI #if defined(__has_feature)
1144543ef51SXin LI # if __has_feature(undefined_behavior_sanitizer)
1154543ef51SXin LI # define EXPAT_TESTS_UBSAN 1
1164543ef51SXin LI # else
1174543ef51SXin LI # define EXPAT_TESTS_UBSAN 0
1184543ef51SXin LI # endif
1194543ef51SXin LI #else
1204543ef51SXin LI # define EXPAT_TESTS_UBSAN 0
1214543ef51SXin LI #endif
1224543ef51SXin LI
1234543ef51SXin LI /* Test that XML_ErrorString rejects out-of-range codes */
START_TEST(test_misc_error_string)1244543ef51SXin LI START_TEST(test_misc_error_string) {
1254543ef51SXin LI #if ! EXPAT_TESTS_UBSAN // because this would trigger UBSan
1264543ef51SXin LI union {
1274543ef51SXin LI enum XML_Error xml_error;
1284543ef51SXin LI int integer;
1294543ef51SXin LI } trickery;
1304543ef51SXin LI
1314543ef51SXin LI assert_true(sizeof(enum XML_Error) == sizeof(int)); // self-test
1324543ef51SXin LI
1334543ef51SXin LI trickery.integer = -1;
1344543ef51SXin LI if (XML_ErrorString(trickery.xml_error) != NULL)
1354543ef51SXin LI fail("Negative error code not rejected");
1364543ef51SXin LI
1374543ef51SXin LI trickery.integer = 100;
1384543ef51SXin LI if (XML_ErrorString(trickery.xml_error) != NULL)
1394543ef51SXin LI fail("Large error code not rejected");
1404543ef51SXin LI #endif
1414543ef51SXin LI }
1424543ef51SXin LI END_TEST
1434543ef51SXin LI
1444543ef51SXin LI /* Test the version information is consistent */
1454543ef51SXin LI
1464543ef51SXin LI /* Since we are working in XML_LChars (potentially 16-bits), we
1474543ef51SXin LI * can't use the standard C library functions for character
1484543ef51SXin LI * manipulation and have to roll our own.
1494543ef51SXin LI */
1504543ef51SXin LI static int
parse_version(const XML_LChar * version_text,XML_Expat_Version * version_struct)1514543ef51SXin LI parse_version(const XML_LChar *version_text,
1524543ef51SXin LI XML_Expat_Version *version_struct) {
1534543ef51SXin LI if (! version_text)
1544543ef51SXin LI return XML_FALSE;
1554543ef51SXin LI
1564543ef51SXin LI while (*version_text != 0x00) {
1574543ef51SXin LI if (*version_text >= ASCII_0 && *version_text <= ASCII_9)
1584543ef51SXin LI break;
1594543ef51SXin LI version_text++;
1604543ef51SXin LI }
1614543ef51SXin LI if (*version_text == 0x00)
1624543ef51SXin LI return XML_FALSE;
1634543ef51SXin LI
1644543ef51SXin LI /* version_struct->major = strtoul(version_text, 10, &version_text) */
1654543ef51SXin LI version_struct->major = 0;
1664543ef51SXin LI while (*version_text >= ASCII_0 && *version_text <= ASCII_9) {
1674543ef51SXin LI version_struct->major
1684543ef51SXin LI = 10 * version_struct->major + (*version_text++ - ASCII_0);
1694543ef51SXin LI }
1704543ef51SXin LI if (*version_text++ != ASCII_PERIOD)
1714543ef51SXin LI return XML_FALSE;
1724543ef51SXin LI
1734543ef51SXin LI /* Now for the minor version number */
1744543ef51SXin LI version_struct->minor = 0;
1754543ef51SXin LI while (*version_text >= ASCII_0 && *version_text <= ASCII_9) {
1764543ef51SXin LI version_struct->minor
1774543ef51SXin LI = 10 * version_struct->minor + (*version_text++ - ASCII_0);
1784543ef51SXin LI }
1794543ef51SXin LI if (*version_text++ != ASCII_PERIOD)
1804543ef51SXin LI return XML_FALSE;
1814543ef51SXin LI
1824543ef51SXin LI /* Finally the micro version number */
1834543ef51SXin LI version_struct->micro = 0;
1844543ef51SXin LI while (*version_text >= ASCII_0 && *version_text <= ASCII_9) {
1854543ef51SXin LI version_struct->micro
1864543ef51SXin LI = 10 * version_struct->micro + (*version_text++ - ASCII_0);
1874543ef51SXin LI }
1884543ef51SXin LI if (*version_text != 0x00)
1894543ef51SXin LI return XML_FALSE;
1904543ef51SXin LI return XML_TRUE;
1914543ef51SXin LI }
1924543ef51SXin LI
1934543ef51SXin LI static int
versions_equal(const XML_Expat_Version * first,const XML_Expat_Version * second)1944543ef51SXin LI versions_equal(const XML_Expat_Version *first,
1954543ef51SXin LI const XML_Expat_Version *second) {
1964543ef51SXin LI return (first->major == second->major && first->minor == second->minor
1974543ef51SXin LI && first->micro == second->micro);
1984543ef51SXin LI }
1994543ef51SXin LI
START_TEST(test_misc_version)2004543ef51SXin LI START_TEST(test_misc_version) {
2014543ef51SXin LI XML_Expat_Version read_version = XML_ExpatVersionInfo();
2024543ef51SXin LI /* Silence compiler warning with the following assignment */
2034543ef51SXin LI XML_Expat_Version parsed_version = {0, 0, 0};
2044543ef51SXin LI const XML_LChar *version_text = XML_ExpatVersion();
2054543ef51SXin LI
2064543ef51SXin LI if (version_text == NULL)
2074543ef51SXin LI fail("Could not obtain version text");
2084543ef51SXin LI assert(version_text != NULL);
2094543ef51SXin LI if (! parse_version(version_text, &parsed_version))
2104543ef51SXin LI fail("Unable to parse version text");
2114543ef51SXin LI if (! versions_equal(&read_version, &parsed_version))
2124543ef51SXin LI fail("Version mismatch");
2134543ef51SXin LI
214*fe927888SPhilip Paeps if (xcstrcmp(version_text, XCS("expat_2.7.1"))) /* needs bump on releases */
2154543ef51SXin LI fail("XML_*_VERSION in expat.h out of sync?\n");
2164543ef51SXin LI }
2174543ef51SXin LI END_TEST
2184543ef51SXin LI
2194543ef51SXin LI /* Test feature information */
START_TEST(test_misc_features)2204543ef51SXin LI START_TEST(test_misc_features) {
2214543ef51SXin LI const XML_Feature *features = XML_GetFeatureList();
2224543ef51SXin LI
2234543ef51SXin LI /* Prevent problems with double-freeing parsers */
2244543ef51SXin LI g_parser = NULL;
2254543ef51SXin LI if (features == NULL) {
2264543ef51SXin LI fail("Failed to get feature information");
2274543ef51SXin LI } else {
2284543ef51SXin LI /* Loop through the features checking what we can */
2294543ef51SXin LI while (features->feature != XML_FEATURE_END) {
2304543ef51SXin LI switch (features->feature) {
2314543ef51SXin LI case XML_FEATURE_SIZEOF_XML_CHAR:
2324543ef51SXin LI if (features->value != sizeof(XML_Char))
2334543ef51SXin LI fail("Incorrect size of XML_Char");
2344543ef51SXin LI break;
2354543ef51SXin LI case XML_FEATURE_SIZEOF_XML_LCHAR:
2364543ef51SXin LI if (features->value != sizeof(XML_LChar))
2374543ef51SXin LI fail("Incorrect size of XML_LChar");
2384543ef51SXin LI break;
2394543ef51SXin LI default:
2404543ef51SXin LI break;
2414543ef51SXin LI }
2424543ef51SXin LI features++;
2434543ef51SXin LI }
2444543ef51SXin LI }
2454543ef51SXin LI }
2464543ef51SXin LI END_TEST
2474543ef51SXin LI
2484543ef51SXin LI /* Regression test for GitHub Issue #17: memory leak parsing attribute
2494543ef51SXin LI * values with mixed bound and unbound namespaces.
2504543ef51SXin LI */
START_TEST(test_misc_attribute_leak)2514543ef51SXin LI START_TEST(test_misc_attribute_leak) {
2524543ef51SXin LI const char *text = "<D xmlns:L=\"D\" l:a='' L:a=''/>";
2534543ef51SXin LI XML_Memory_Handling_Suite memsuite
2544543ef51SXin LI = {tracking_malloc, tracking_realloc, tracking_free};
2554543ef51SXin LI
2564543ef51SXin LI g_parser = XML_ParserCreate_MM(XCS("UTF-8"), &memsuite, XCS("\n"));
2574543ef51SXin LI expect_failure(text, XML_ERROR_UNBOUND_PREFIX, "Unbound prefixes not found");
2584543ef51SXin LI XML_ParserFree(g_parser);
2594543ef51SXin LI /* Prevent the teardown trying to double free */
2604543ef51SXin LI g_parser = NULL;
2614543ef51SXin LI
2624543ef51SXin LI if (! tracking_report())
2634543ef51SXin LI fail("Memory leak found");
2644543ef51SXin LI }
2654543ef51SXin LI END_TEST
2664543ef51SXin LI
2674543ef51SXin LI /* Test parser created for UTF-16LE is successful */
START_TEST(test_misc_utf16le)2684543ef51SXin LI START_TEST(test_misc_utf16le) {
2694543ef51SXin LI const char text[] =
2704543ef51SXin LI /* <?xml version='1.0'?><q>Hi</q> */
2714543ef51SXin LI "<\0?\0x\0m\0l\0 \0"
2724543ef51SXin LI "v\0e\0r\0s\0i\0o\0n\0=\0'\0\x31\0.\0\x30\0'\0?\0>\0"
2734543ef51SXin LI "<\0q\0>\0H\0i\0<\0/\0q\0>\0";
2744543ef51SXin LI const XML_Char *expected = XCS("Hi");
2754543ef51SXin LI CharData storage;
2764543ef51SXin LI
2774543ef51SXin LI g_parser = XML_ParserCreate(XCS("UTF-16LE"));
2784543ef51SXin LI if (g_parser == NULL)
2794543ef51SXin LI fail("Parser not created");
2804543ef51SXin LI
2814543ef51SXin LI CharData_Init(&storage);
2824543ef51SXin LI XML_SetUserData(g_parser, &storage);
2834543ef51SXin LI XML_SetCharacterDataHandler(g_parser, accumulate_characters);
2844543ef51SXin LI if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)sizeof(text) - 1, XML_TRUE)
2854543ef51SXin LI == XML_STATUS_ERROR)
2864543ef51SXin LI xml_failure(g_parser);
2874543ef51SXin LI CharData_CheckXMLChars(&storage, expected);
2884543ef51SXin LI }
2894543ef51SXin LI END_TEST
2904543ef51SXin LI
START_TEST(test_misc_stop_during_end_handler_issue_240_1)2914543ef51SXin LI START_TEST(test_misc_stop_during_end_handler_issue_240_1) {
2924543ef51SXin LI XML_Parser parser;
2934543ef51SXin LI DataIssue240 *mydata;
2944543ef51SXin LI enum XML_Status result;
2954543ef51SXin LI const char *const doc1 = "<doc><e1/><e><foo/></e></doc>";
2964543ef51SXin LI
2974543ef51SXin LI parser = XML_ParserCreate(NULL);
2984543ef51SXin LI XML_SetElementHandler(parser, start_element_issue_240, end_element_issue_240);
2994543ef51SXin LI mydata = (DataIssue240 *)malloc(sizeof(DataIssue240));
300*fe927888SPhilip Paeps assert_true(mydata != NULL);
3014543ef51SXin LI mydata->parser = parser;
3024543ef51SXin LI mydata->deep = 0;
3034543ef51SXin LI XML_SetUserData(parser, mydata);
3044543ef51SXin LI
3054543ef51SXin LI result = _XML_Parse_SINGLE_BYTES(parser, doc1, (int)strlen(doc1), 1);
3064543ef51SXin LI XML_ParserFree(parser);
3074543ef51SXin LI free(mydata);
3084543ef51SXin LI if (result != XML_STATUS_ERROR)
3094543ef51SXin LI fail("Stopping the parser did not work as expected");
3104543ef51SXin LI }
3114543ef51SXin LI END_TEST
3124543ef51SXin LI
START_TEST(test_misc_stop_during_end_handler_issue_240_2)3134543ef51SXin LI START_TEST(test_misc_stop_during_end_handler_issue_240_2) {
3144543ef51SXin LI XML_Parser parser;
3154543ef51SXin LI DataIssue240 *mydata;
3164543ef51SXin LI enum XML_Status result;
3174543ef51SXin LI const char *const doc2 = "<doc><elem/></doc>";
3184543ef51SXin LI
3194543ef51SXin LI parser = XML_ParserCreate(NULL);
3204543ef51SXin LI XML_SetElementHandler(parser, start_element_issue_240, end_element_issue_240);
3214543ef51SXin LI mydata = (DataIssue240 *)malloc(sizeof(DataIssue240));
322*fe927888SPhilip Paeps assert_true(mydata != NULL);
3234543ef51SXin LI mydata->parser = parser;
3244543ef51SXin LI mydata->deep = 0;
3254543ef51SXin LI XML_SetUserData(parser, mydata);
3264543ef51SXin LI
3274543ef51SXin LI result = _XML_Parse_SINGLE_BYTES(parser, doc2, (int)strlen(doc2), 1);
3284543ef51SXin LI XML_ParserFree(parser);
3294543ef51SXin LI free(mydata);
3304543ef51SXin LI if (result != XML_STATUS_ERROR)
3314543ef51SXin LI fail("Stopping the parser did not work as expected");
3324543ef51SXin LI }
3334543ef51SXin LI END_TEST
3344543ef51SXin LI
START_TEST(test_misc_deny_internal_entity_closing_doctype_issue_317)3354543ef51SXin LI START_TEST(test_misc_deny_internal_entity_closing_doctype_issue_317) {
336*fe927888SPhilip Paeps const char *const inputOne
337*fe927888SPhilip Paeps = "<!DOCTYPE d [\n"
338*fe927888SPhilip Paeps "<!ENTITY % element_d '<!ELEMENT d (#PCDATA)*>'>\n"
339*fe927888SPhilip Paeps "%element_d;\n"
3404543ef51SXin LI "<!ENTITY % e ']><d/>'>\n"
3414543ef51SXin LI "\n"
3424543ef51SXin LI "%e;";
343908f215eSXin LI const char *const inputTwo
344908f215eSXin LI = "<!DOCTYPE d [\n"
345*fe927888SPhilip Paeps "<!ENTITY % element_d '<!ELEMENT d (#PCDATA)*>'>\n"
346*fe927888SPhilip Paeps "%element_d;\n"
347908f215eSXin LI "<!ENTITY % e1 ']><d/>'><!ENTITY % e2 '%e1;'>\n"
3484543ef51SXin LI "\n"
3494543ef51SXin LI "%e2;";
350*fe927888SPhilip Paeps const char *const inputThree
351*fe927888SPhilip Paeps = "<!DOCTYPE d [\n"
352*fe927888SPhilip Paeps "<!ENTITY % element_d '<!ELEMENT d (#PCDATA)*>'>\n"
353*fe927888SPhilip Paeps "%element_d;\n"
3544543ef51SXin LI "<!ENTITY % e ']><d'>\n"
3554543ef51SXin LI "\n"
356908f215eSXin LI "%e;/>";
357*fe927888SPhilip Paeps const char *const inputIssue317
358*fe927888SPhilip Paeps = "<!DOCTYPE doc [\n"
359*fe927888SPhilip Paeps "<!ENTITY % element_doc '<!ELEMENT doc (#PCDATA)*>'>\n"
360*fe927888SPhilip Paeps "%element_doc;\n"
3614543ef51SXin LI "<!ENTITY % foo ']>\n"
3624543ef51SXin LI "<doc>Hell<oc (#PCDATA)*>'>\n"
3634543ef51SXin LI "%foo;\n"
3644543ef51SXin LI "]>\n"
3654543ef51SXin LI "<doc>Hello, world</dVc>";
3664543ef51SXin LI
3674543ef51SXin LI const char *const inputs[] = {inputOne, inputTwo, inputThree, inputIssue317};
368*fe927888SPhilip Paeps const XML_Bool suspendOrNot[] = {XML_FALSE, XML_TRUE};
3694543ef51SXin LI size_t inputIndex = 0;
3704543ef51SXin LI
3714543ef51SXin LI for (; inputIndex < sizeof(inputs) / sizeof(inputs[0]); inputIndex++) {
372*fe927888SPhilip Paeps for (size_t suspendOrNotIndex = 0;
373*fe927888SPhilip Paeps suspendOrNotIndex < sizeof(suspendOrNot) / sizeof(suspendOrNot[0]);
374*fe927888SPhilip Paeps suspendOrNotIndex++) {
375*fe927888SPhilip Paeps const char *const input = inputs[inputIndex];
376*fe927888SPhilip Paeps const XML_Bool suspend = suspendOrNot[suspendOrNotIndex];
377*fe927888SPhilip Paeps if (suspend && (g_chunkSize > 0)) {
378*fe927888SPhilip Paeps // We cannot use _XML_Parse_SINGLE_BYTES below due to suspension, and
379*fe927888SPhilip Paeps // so chunk sizes >0 would only repeat the very same test
380*fe927888SPhilip Paeps // due to use of plain XML_Parse; we are saving upon that runtime:
381*fe927888SPhilip Paeps return;
382*fe927888SPhilip Paeps }
383*fe927888SPhilip Paeps
384*fe927888SPhilip Paeps set_subtest("[input=%d suspend=%s] %s", (int)inputIndex,
385*fe927888SPhilip Paeps suspend ? "true" : "false", input);
3864543ef51SXin LI XML_Parser parser;
3874543ef51SXin LI enum XML_Status parseResult;
3884543ef51SXin LI int setParamEntityResult;
3894543ef51SXin LI XML_Size lineNumber;
3904543ef51SXin LI XML_Size columnNumber;
3914543ef51SXin LI
3924543ef51SXin LI parser = XML_ParserCreate(NULL);
3934543ef51SXin LI setParamEntityResult
3944543ef51SXin LI = XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
3954543ef51SXin LI if (setParamEntityResult != 1)
3964543ef51SXin LI fail("Failed to set XML_PARAM_ENTITY_PARSING_ALWAYS.");
3974543ef51SXin LI
398*fe927888SPhilip Paeps if (suspend) {
399*fe927888SPhilip Paeps XML_SetUserData(parser, parser);
400*fe927888SPhilip Paeps XML_SetElementDeclHandler(parser, suspend_after_element_declaration);
401*fe927888SPhilip Paeps }
402*fe927888SPhilip Paeps
403*fe927888SPhilip Paeps if (suspend) {
404*fe927888SPhilip Paeps // can't use SINGLE_BYTES here, because it'll return early on
405*fe927888SPhilip Paeps // suspension, and we won't know exactly how much input we actually
406*fe927888SPhilip Paeps // managed to give Expat.
407*fe927888SPhilip Paeps parseResult = XML_Parse(parser, input, (int)strlen(input), 0);
408*fe927888SPhilip Paeps
409*fe927888SPhilip Paeps while (parseResult == XML_STATUS_SUSPENDED) {
410*fe927888SPhilip Paeps parseResult = XML_ResumeParser(parser);
411*fe927888SPhilip Paeps }
412*fe927888SPhilip Paeps
413*fe927888SPhilip Paeps if (parseResult != XML_STATUS_ERROR) {
414*fe927888SPhilip Paeps // can't use SINGLE_BYTES here, because it'll return early on
415*fe927888SPhilip Paeps // suspension, and we won't know exactly how much input we actually
416*fe927888SPhilip Paeps // managed to give Expat.
417*fe927888SPhilip Paeps parseResult = XML_Parse(parser, "", 0, 1);
418*fe927888SPhilip Paeps }
419*fe927888SPhilip Paeps
420*fe927888SPhilip Paeps while (parseResult == XML_STATUS_SUSPENDED) {
421*fe927888SPhilip Paeps parseResult = XML_ResumeParser(parser);
422*fe927888SPhilip Paeps }
423*fe927888SPhilip Paeps } else {
424*fe927888SPhilip Paeps parseResult
425*fe927888SPhilip Paeps = _XML_Parse_SINGLE_BYTES(parser, input, (int)strlen(input), 0);
426*fe927888SPhilip Paeps
4274543ef51SXin LI if (parseResult != XML_STATUS_ERROR) {
4284543ef51SXin LI parseResult = _XML_Parse_SINGLE_BYTES(parser, "", 0, 1);
429*fe927888SPhilip Paeps }
430*fe927888SPhilip Paeps }
431*fe927888SPhilip Paeps
4324543ef51SXin LI if (parseResult != XML_STATUS_ERROR) {
4334543ef51SXin LI fail("Parsing was expected to fail but succeeded.");
4344543ef51SXin LI }
4354543ef51SXin LI
4364543ef51SXin LI if (XML_GetErrorCode(parser) != XML_ERROR_INVALID_TOKEN)
4374543ef51SXin LI fail("Error code does not match XML_ERROR_INVALID_TOKEN");
4384543ef51SXin LI
4394543ef51SXin LI lineNumber = XML_GetCurrentLineNumber(parser);
440*fe927888SPhilip Paeps if (lineNumber != 6)
4414543ef51SXin LI fail("XML_GetCurrentLineNumber does not work as expected.");
4424543ef51SXin LI
4434543ef51SXin LI columnNumber = XML_GetCurrentColumnNumber(parser);
4444543ef51SXin LI if (columnNumber != 0)
4454543ef51SXin LI fail("XML_GetCurrentColumnNumber does not work as expected.");
4464543ef51SXin LI
4474543ef51SXin LI XML_ParserFree(parser);
4484543ef51SXin LI }
4494543ef51SXin LI }
450*fe927888SPhilip Paeps }
4514543ef51SXin LI END_TEST
4524543ef51SXin LI
START_TEST(test_misc_tag_mismatch_reset_leak)4534543ef51SXin LI START_TEST(test_misc_tag_mismatch_reset_leak) {
4544543ef51SXin LI #ifdef XML_NS
4554543ef51SXin LI const char *const text = "<open xmlns='https://namespace1.test'></close>";
4564543ef51SXin LI XML_Parser parser = XML_ParserCreateNS(NULL, XCS('\n'));
4574543ef51SXin LI
4584543ef51SXin LI if (_XML_Parse_SINGLE_BYTES(parser, text, (int)strlen(text), XML_TRUE)
4594543ef51SXin LI != XML_STATUS_ERROR)
4604543ef51SXin LI fail("Call to parse was expected to fail");
4614543ef51SXin LI if (XML_GetErrorCode(parser) != XML_ERROR_TAG_MISMATCH)
4624543ef51SXin LI fail("Call to parse was expected to fail from a closing tag mismatch");
4634543ef51SXin LI
4644543ef51SXin LI XML_ParserReset(parser, NULL);
4654543ef51SXin LI
4664543ef51SXin LI if (_XML_Parse_SINGLE_BYTES(parser, text, (int)strlen(text), XML_TRUE)
4674543ef51SXin LI != XML_STATUS_ERROR)
4684543ef51SXin LI fail("Call to parse was expected to fail");
4694543ef51SXin LI if (XML_GetErrorCode(parser) != XML_ERROR_TAG_MISMATCH)
4704543ef51SXin LI fail("Call to parse was expected to fail from a closing tag mismatch");
4714543ef51SXin LI
4724543ef51SXin LI XML_ParserFree(parser);
4734543ef51SXin LI #endif
4744543ef51SXin LI }
4754543ef51SXin LI END_TEST
4764543ef51SXin LI
START_TEST(test_misc_create_external_entity_parser_with_null_context)4774543ef51SXin LI START_TEST(test_misc_create_external_entity_parser_with_null_context) {
4784543ef51SXin LI // With XML_DTD undefined, the only supported case of external entities
4794543ef51SXin LI // is pattern "<!ENTITY entity123 SYSTEM 'filename123'>". A NULL context
4804543ef51SXin LI // was causing a segfault through a null pointer dereference in function
4814543ef51SXin LI // setContext, previously.
4824543ef51SXin LI XML_Parser parser = XML_ParserCreate(NULL);
4834543ef51SXin LI XML_Parser ext_parser = XML_ExternalEntityParserCreate(parser, NULL, NULL);
4844543ef51SXin LI #ifdef XML_DTD
4854543ef51SXin LI assert_true(ext_parser != NULL);
4864543ef51SXin LI XML_ParserFree(ext_parser);
4874543ef51SXin LI #else
4884543ef51SXin LI assert_true(ext_parser == NULL);
4894543ef51SXin LI #endif /* XML_DTD */
4904543ef51SXin LI XML_ParserFree(parser);
4914543ef51SXin LI }
4924543ef51SXin LI END_TEST
4934543ef51SXin LI
START_TEST(test_misc_general_entities_support)4944543ef51SXin LI START_TEST(test_misc_general_entities_support) {
4954543ef51SXin LI const char *const doc
4964543ef51SXin LI = "<!DOCTYPE r [\n"
4974543ef51SXin LI "<!ENTITY e1 'v1'>\n"
4984543ef51SXin LI "<!ENTITY e2 SYSTEM 'v2'>\n"
4994543ef51SXin LI "]>\n"
5004543ef51SXin LI "<r a1='[&e1;]'>[&e1;][&e2;][&'><"]</r>";
5014543ef51SXin LI
5024543ef51SXin LI CharData storage;
5034543ef51SXin LI CharData_Init(&storage);
5044543ef51SXin LI
5054543ef51SXin LI XML_Parser parser = XML_ParserCreate(NULL);
5064543ef51SXin LI XML_SetUserData(parser, &storage);
5074543ef51SXin LI XML_SetStartElementHandler(parser, accumulate_start_element);
5084543ef51SXin LI XML_SetExternalEntityRefHandler(parser,
5094543ef51SXin LI external_entity_failer__if_not_xml_ge);
5104543ef51SXin LI XML_SetEntityDeclHandler(parser, accumulate_entity_decl);
511908f215eSXin LI XML_SetCharacterDataHandler(parser, accumulate_characters);
5124543ef51SXin LI
5134543ef51SXin LI if (_XML_Parse_SINGLE_BYTES(parser, doc, (int)strlen(doc), XML_TRUE)
5144543ef51SXin LI != XML_STATUS_OK) {
5154543ef51SXin LI xml_failure(parser);
5164543ef51SXin LI }
5174543ef51SXin LI
5184543ef51SXin LI XML_ParserFree(parser);
5194543ef51SXin LI
5204543ef51SXin LI CharData_CheckXMLChars(&storage,
5214543ef51SXin LI /* clang-format off */
5224543ef51SXin LI #if XML_GE == 1
5234543ef51SXin LI XCS("e1=v1\n")
5244543ef51SXin LI XCS("e2=(null)\n")
5254543ef51SXin LI XCS("(r(a1=[v1]))\n")
5264543ef51SXin LI XCS("[v1][][&'><\"]")
5274543ef51SXin LI #else
5284543ef51SXin LI XCS("e1=&e1;\n")
5294543ef51SXin LI XCS("e2=(null)\n")
5304543ef51SXin LI XCS("(r(a1=[&e1;]))\n")
5314543ef51SXin LI XCS("[&e1;][&e2;][&'><\"]")
5324543ef51SXin LI #endif
5334543ef51SXin LI );
5344543ef51SXin LI /* clang-format on */
5354543ef51SXin LI }
5364543ef51SXin LI END_TEST
5374543ef51SXin LI
5384543ef51SXin LI static void XMLCALL
resumable_stopping_character_handler(void * userData,const XML_Char * s,int len)5394543ef51SXin LI resumable_stopping_character_handler(void *userData, const XML_Char *s,
5404543ef51SXin LI int len) {
5414543ef51SXin LI UNUSED_P(s);
5424543ef51SXin LI UNUSED_P(len);
5434543ef51SXin LI XML_Parser parser = (XML_Parser)userData;
5444543ef51SXin LI XML_StopParser(parser, XML_TRUE);
5454543ef51SXin LI }
5464543ef51SXin LI
5474543ef51SXin LI // NOTE: This test needs active LeakSanitizer to be of actual use
START_TEST(test_misc_char_handler_stop_without_leak)5484543ef51SXin LI START_TEST(test_misc_char_handler_stop_without_leak) {
5494543ef51SXin LI const char *const data
5504543ef51SXin LI = "<!DOCTYPE t1[<!ENTITY e1 'angle<'><!ENTITY e2 '&e1;'>]><t1>&e2;";
5514543ef51SXin LI XML_Parser parser = XML_ParserCreate(NULL);
5524543ef51SXin LI assert_true(parser != NULL);
5534543ef51SXin LI XML_SetUserData(parser, parser);
5544543ef51SXin LI XML_SetCharacterDataHandler(parser, resumable_stopping_character_handler);
5554543ef51SXin LI _XML_Parse_SINGLE_BYTES(parser, data, (int)strlen(data), XML_FALSE);
5564543ef51SXin LI XML_ParserFree(parser);
5574543ef51SXin LI }
5584543ef51SXin LI END_TEST
5594543ef51SXin LI
START_TEST(test_misc_resumeparser_not_crashing)560908f215eSXin LI START_TEST(test_misc_resumeparser_not_crashing) {
561908f215eSXin LI XML_Parser parser = XML_ParserCreate(NULL);
562908f215eSXin LI XML_GetBuffer(parser, 1);
563908f215eSXin LI XML_StopParser(parser, /*resumable=*/XML_TRUE);
564908f215eSXin LI XML_ResumeParser(parser); // could crash here, previously
565908f215eSXin LI XML_ParserFree(parser);
566908f215eSXin LI }
567908f215eSXin LI END_TEST
568908f215eSXin LI
START_TEST(test_misc_stopparser_rejects_unstarted_parser)569908f215eSXin LI START_TEST(test_misc_stopparser_rejects_unstarted_parser) {
570908f215eSXin LI const XML_Bool cases[] = {XML_TRUE, XML_FALSE};
571908f215eSXin LI for (size_t i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) {
572908f215eSXin LI const XML_Bool resumable = cases[i];
573908f215eSXin LI XML_Parser parser = XML_ParserCreate(NULL);
574908f215eSXin LI assert_true(XML_GetErrorCode(parser) == XML_ERROR_NONE);
575908f215eSXin LI assert_true(XML_StopParser(parser, resumable) == XML_STATUS_ERROR);
576908f215eSXin LI assert_true(XML_GetErrorCode(parser) == XML_ERROR_NOT_STARTED);
577908f215eSXin LI XML_ParserFree(parser);
578908f215eSXin LI }
579908f215eSXin LI }
580908f215eSXin LI END_TEST
581908f215eSXin LI
582*fe927888SPhilip Paeps /* Adaptation of accumulate_characters that takes ExtHdlrData input to work with
583*fe927888SPhilip Paeps * test_renter_loop_finite_content below */
584*fe927888SPhilip Paeps void XMLCALL
accumulate_characters_ext_handler(void * userData,const XML_Char * s,int len)585*fe927888SPhilip Paeps accumulate_characters_ext_handler(void *userData, const XML_Char *s, int len) {
586*fe927888SPhilip Paeps ExtHdlrData *const test_data = (ExtHdlrData *)userData;
587*fe927888SPhilip Paeps CharData_AppendXMLChars(test_data->storage, s, len);
588*fe927888SPhilip Paeps }
589*fe927888SPhilip Paeps
590*fe927888SPhilip Paeps /* Test that internalEntityProcessor does not re-enter forever;
591*fe927888SPhilip Paeps * based on files tests/xmlconf/xmltest/valid/ext-sa/012.{xml,ent} */
START_TEST(test_renter_loop_finite_content)592*fe927888SPhilip Paeps START_TEST(test_renter_loop_finite_content) {
593*fe927888SPhilip Paeps CharData storage;
594*fe927888SPhilip Paeps CharData_Init(&storage);
595*fe927888SPhilip Paeps const char *const text = "<!DOCTYPE doc [\n"
596*fe927888SPhilip Paeps "<!ENTITY e1 '&e2;'>\n"
597*fe927888SPhilip Paeps "<!ENTITY e2 '&e3;'>\n"
598*fe927888SPhilip Paeps "<!ENTITY e3 SYSTEM '012.ent'>\n"
599*fe927888SPhilip Paeps "<!ENTITY e4 '&e5;'>\n"
600*fe927888SPhilip Paeps "<!ENTITY e5 '(e5)'>\n"
601*fe927888SPhilip Paeps "<!ELEMENT doc (#PCDATA)>\n"
602*fe927888SPhilip Paeps "]>\n"
603*fe927888SPhilip Paeps "<doc>&e1;</doc>\n";
604*fe927888SPhilip Paeps ExtHdlrData test_data = {"&e4;\n", external_entity_null_loader, &storage};
605*fe927888SPhilip Paeps const XML_Char *const expected = XCS("(e5)\n");
606*fe927888SPhilip Paeps
607*fe927888SPhilip Paeps XML_Parser parser = XML_ParserCreate(NULL);
608*fe927888SPhilip Paeps assert_true(parser != NULL);
609*fe927888SPhilip Paeps XML_SetUserData(parser, &test_data);
610*fe927888SPhilip Paeps XML_SetExternalEntityRefHandler(parser, external_entity_oneshot_loader);
611*fe927888SPhilip Paeps XML_SetCharacterDataHandler(parser, accumulate_characters_ext_handler);
612*fe927888SPhilip Paeps if (_XML_Parse_SINGLE_BYTES(parser, text, (int)strlen(text), XML_TRUE)
613*fe927888SPhilip Paeps == XML_STATUS_ERROR)
614*fe927888SPhilip Paeps xml_failure(parser);
615*fe927888SPhilip Paeps
616*fe927888SPhilip Paeps CharData_CheckXMLChars(&storage, expected);
617*fe927888SPhilip Paeps XML_ParserFree(parser);
618*fe927888SPhilip Paeps }
619*fe927888SPhilip Paeps END_TEST
620*fe927888SPhilip Paeps
621*fe927888SPhilip Paeps // Inspired by function XML_OriginalString of Perl's XML::Parser
622*fe927888SPhilip Paeps static char *
dup_original_string(XML_Parser parser)623*fe927888SPhilip Paeps dup_original_string(XML_Parser parser) {
624*fe927888SPhilip Paeps const int byte_count = XML_GetCurrentByteCount(parser);
625*fe927888SPhilip Paeps
626*fe927888SPhilip Paeps assert_true(byte_count >= 0);
627*fe927888SPhilip Paeps
628*fe927888SPhilip Paeps int offset = -1;
629*fe927888SPhilip Paeps int size = -1;
630*fe927888SPhilip Paeps
631*fe927888SPhilip Paeps const char *const context = XML_GetInputContext(parser, &offset, &size);
632*fe927888SPhilip Paeps
633*fe927888SPhilip Paeps #if XML_CONTEXT_BYTES > 0
634*fe927888SPhilip Paeps assert_true(context != NULL);
635*fe927888SPhilip Paeps assert_true(offset >= 0);
636*fe927888SPhilip Paeps assert_true(size >= 0);
637*fe927888SPhilip Paeps return portable_strndup(context + offset, byte_count);
638*fe927888SPhilip Paeps #else
639*fe927888SPhilip Paeps assert_true(context == NULL);
640*fe927888SPhilip Paeps return NULL;
641*fe927888SPhilip Paeps #endif
642*fe927888SPhilip Paeps }
643*fe927888SPhilip Paeps
644*fe927888SPhilip Paeps static void
on_characters_issue_980(void * userData,const XML_Char * s,int len)645*fe927888SPhilip Paeps on_characters_issue_980(void *userData, const XML_Char *s, int len) {
646*fe927888SPhilip Paeps (void)s;
647*fe927888SPhilip Paeps (void)len;
648*fe927888SPhilip Paeps XML_Parser parser = (XML_Parser)userData;
649*fe927888SPhilip Paeps
650*fe927888SPhilip Paeps char *const original_string = dup_original_string(parser);
651*fe927888SPhilip Paeps
652*fe927888SPhilip Paeps #if XML_CONTEXT_BYTES > 0
653*fe927888SPhilip Paeps assert_true(original_string != NULL);
654*fe927888SPhilip Paeps assert_true(strcmp(original_string, "&draft.day;") == 0);
655*fe927888SPhilip Paeps free(original_string);
656*fe927888SPhilip Paeps #else
657*fe927888SPhilip Paeps assert_true(original_string == NULL);
658*fe927888SPhilip Paeps #endif
659*fe927888SPhilip Paeps }
660*fe927888SPhilip Paeps
START_TEST(test_misc_expected_event_ptr_issue_980)661*fe927888SPhilip Paeps START_TEST(test_misc_expected_event_ptr_issue_980) {
662*fe927888SPhilip Paeps // NOTE: This is a tiny subset of sample "REC-xml-19980210.xml"
663*fe927888SPhilip Paeps // from Perl's XML::Parser
664*fe927888SPhilip Paeps const char *const doc = "<!DOCTYPE day [\n"
665*fe927888SPhilip Paeps " <!ENTITY draft.day '10'>\n"
666*fe927888SPhilip Paeps "]>\n"
667*fe927888SPhilip Paeps "<day>&draft.day;</day>\n";
668*fe927888SPhilip Paeps
669*fe927888SPhilip Paeps XML_Parser parser = XML_ParserCreate(NULL);
670*fe927888SPhilip Paeps XML_SetUserData(parser, parser);
671*fe927888SPhilip Paeps XML_SetCharacterDataHandler(parser, on_characters_issue_980);
672*fe927888SPhilip Paeps
673*fe927888SPhilip Paeps assert_true(_XML_Parse_SINGLE_BYTES(parser, doc, (int)strlen(doc),
674*fe927888SPhilip Paeps /*isFinal=*/XML_TRUE)
675*fe927888SPhilip Paeps == XML_STATUS_OK);
676*fe927888SPhilip Paeps
677*fe927888SPhilip Paeps XML_ParserFree(parser);
678*fe927888SPhilip Paeps }
679*fe927888SPhilip Paeps END_TEST
680*fe927888SPhilip Paeps
6814543ef51SXin LI void
make_miscellaneous_test_case(Suite * s)6824543ef51SXin LI make_miscellaneous_test_case(Suite *s) {
6834543ef51SXin LI TCase *tc_misc = tcase_create("miscellaneous tests");
6844543ef51SXin LI
6854543ef51SXin LI suite_add_tcase(s, tc_misc);
6864543ef51SXin LI tcase_add_checked_fixture(tc_misc, NULL, basic_teardown);
6874543ef51SXin LI
6884543ef51SXin LI tcase_add_test(tc_misc, test_misc_alloc_create_parser);
6894543ef51SXin LI tcase_add_test(tc_misc, test_misc_alloc_create_parser_with_encoding);
6904543ef51SXin LI tcase_add_test(tc_misc, test_misc_null_parser);
6914543ef51SXin LI tcase_add_test(tc_misc, test_misc_error_string);
6924543ef51SXin LI tcase_add_test(tc_misc, test_misc_version);
6934543ef51SXin LI tcase_add_test(tc_misc, test_misc_features);
6944543ef51SXin LI tcase_add_test(tc_misc, test_misc_attribute_leak);
6954543ef51SXin LI tcase_add_test(tc_misc, test_misc_utf16le);
6964543ef51SXin LI tcase_add_test(tc_misc, test_misc_stop_during_end_handler_issue_240_1);
6974543ef51SXin LI tcase_add_test(tc_misc, test_misc_stop_during_end_handler_issue_240_2);
6984543ef51SXin LI tcase_add_test__ifdef_xml_dtd(
6994543ef51SXin LI tc_misc, test_misc_deny_internal_entity_closing_doctype_issue_317);
7004543ef51SXin LI tcase_add_test(tc_misc, test_misc_tag_mismatch_reset_leak);
7014543ef51SXin LI tcase_add_test(tc_misc,
7024543ef51SXin LI test_misc_create_external_entity_parser_with_null_context);
7034543ef51SXin LI tcase_add_test(tc_misc, test_misc_general_entities_support);
7044543ef51SXin LI tcase_add_test(tc_misc, test_misc_char_handler_stop_without_leak);
705908f215eSXin LI tcase_add_test(tc_misc, test_misc_resumeparser_not_crashing);
706908f215eSXin LI tcase_add_test(tc_misc, test_misc_stopparser_rejects_unstarted_parser);
707*fe927888SPhilip Paeps tcase_add_test__if_xml_ge(tc_misc, test_renter_loop_finite_content);
708*fe927888SPhilip Paeps tcase_add_test(tc_misc, test_misc_expected_event_ptr_issue_980);
7094543ef51SXin LI }
710