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