xref: /openbsd-src/lib/libexpat/tests/misc_tests.c (revision aa071e6ed2e21e8e72a6aac46533908f2defbdef)
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 */
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 */
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  */
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 */
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
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
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 
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*aa071e6eSbluhm   if (xcstrcmp(version_text, XCS("expat_2.6.4"))) /* needs bump on releases */
212bd8f1dc3Sbluhm     fail("XML_*_VERSION in expat.h out of sync?\n");
213bd8f1dc3Sbluhm }
214bd8f1dc3Sbluhm END_TEST
215bd8f1dc3Sbluhm 
216bd8f1dc3Sbluhm /* Test feature information */
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  */
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 */
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 
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 
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 
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;";
335*aa071e6eSbluhm   const char *const inputTwo
336*aa071e6eSbluhm       = "<!DOCTYPE d [\n"
337*aa071e6eSbluhm         "<!ENTITY % e1 ']><d/>'><!ENTITY % e2 '&#37;e1;'>\n"
338bd8f1dc3Sbluhm         "\n"
339bd8f1dc3Sbluhm         "%e2;";
340bd8f1dc3Sbluhm   const char *const inputThree = "<!DOCTYPE d [\n"
341bd8f1dc3Sbluhm                                  "<!ENTITY % e ']><d'>\n"
342bd8f1dc3Sbluhm                                  "\n"
343*aa071e6eSbluhm                                  "%e;/>";
344bd8f1dc3Sbluhm   const char *const inputIssue317 = "<!DOCTYPE doc [\n"
345bd8f1dc3Sbluhm                                     "<!ENTITY % foo ']>\n"
346bd8f1dc3Sbluhm                                     "<doc>Hell<oc (#PCDATA)*>'>\n"
347bd8f1dc3Sbluhm                                     "%foo;\n"
348bd8f1dc3Sbluhm                                     "]>\n"
349bd8f1dc3Sbluhm                                     "<doc>Hello, world</dVc>";
350bd8f1dc3Sbluhm 
351bd8f1dc3Sbluhm   const char *const inputs[] = {inputOne, inputTwo, inputThree, inputIssue317};
352bd8f1dc3Sbluhm   size_t inputIndex = 0;
353bd8f1dc3Sbluhm 
354bd8f1dc3Sbluhm   for (; inputIndex < sizeof(inputs) / sizeof(inputs[0]); inputIndex++) {
355bd8f1dc3Sbluhm     set_subtest("%s", inputs[inputIndex]);
356bd8f1dc3Sbluhm     XML_Parser parser;
357bd8f1dc3Sbluhm     enum XML_Status parseResult;
358bd8f1dc3Sbluhm     int setParamEntityResult;
359bd8f1dc3Sbluhm     XML_Size lineNumber;
360bd8f1dc3Sbluhm     XML_Size columnNumber;
361bd8f1dc3Sbluhm     const char *const input = inputs[inputIndex];
362bd8f1dc3Sbluhm 
363bd8f1dc3Sbluhm     parser = XML_ParserCreate(NULL);
364bd8f1dc3Sbluhm     setParamEntityResult
365bd8f1dc3Sbluhm         = XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
366bd8f1dc3Sbluhm     if (setParamEntityResult != 1)
367bd8f1dc3Sbluhm       fail("Failed to set XML_PARAM_ENTITY_PARSING_ALWAYS.");
368bd8f1dc3Sbluhm 
369bd8f1dc3Sbluhm     parseResult = _XML_Parse_SINGLE_BYTES(parser, input, (int)strlen(input), 0);
370bd8f1dc3Sbluhm     if (parseResult != XML_STATUS_ERROR) {
371bd8f1dc3Sbluhm       parseResult = _XML_Parse_SINGLE_BYTES(parser, "", 0, 1);
372bd8f1dc3Sbluhm       if (parseResult != XML_STATUS_ERROR) {
373bd8f1dc3Sbluhm         fail("Parsing was expected to fail but succeeded.");
374bd8f1dc3Sbluhm       }
375bd8f1dc3Sbluhm     }
376bd8f1dc3Sbluhm 
377bd8f1dc3Sbluhm     if (XML_GetErrorCode(parser) != XML_ERROR_INVALID_TOKEN)
378bd8f1dc3Sbluhm       fail("Error code does not match XML_ERROR_INVALID_TOKEN");
379bd8f1dc3Sbluhm 
380bd8f1dc3Sbluhm     lineNumber = XML_GetCurrentLineNumber(parser);
381bd8f1dc3Sbluhm     if (lineNumber != 4)
382bd8f1dc3Sbluhm       fail("XML_GetCurrentLineNumber does not work as expected.");
383bd8f1dc3Sbluhm 
384bd8f1dc3Sbluhm     columnNumber = XML_GetCurrentColumnNumber(parser);
385bd8f1dc3Sbluhm     if (columnNumber != 0)
386bd8f1dc3Sbluhm       fail("XML_GetCurrentColumnNumber does not work as expected.");
387bd8f1dc3Sbluhm 
388bd8f1dc3Sbluhm     XML_ParserFree(parser);
389bd8f1dc3Sbluhm   }
390bd8f1dc3Sbluhm }
391bd8f1dc3Sbluhm END_TEST
392bd8f1dc3Sbluhm 
393bd8f1dc3Sbluhm START_TEST(test_misc_tag_mismatch_reset_leak) {
394bd8f1dc3Sbluhm #ifdef XML_NS
395bd8f1dc3Sbluhm   const char *const text = "<open xmlns='https://namespace1.test'></close>";
396bd8f1dc3Sbluhm   XML_Parser parser = XML_ParserCreateNS(NULL, XCS('\n'));
397bd8f1dc3Sbluhm 
398bd8f1dc3Sbluhm   if (_XML_Parse_SINGLE_BYTES(parser, text, (int)strlen(text), XML_TRUE)
399bd8f1dc3Sbluhm       != XML_STATUS_ERROR)
400bd8f1dc3Sbluhm     fail("Call to parse was expected to fail");
401bd8f1dc3Sbluhm   if (XML_GetErrorCode(parser) != XML_ERROR_TAG_MISMATCH)
402bd8f1dc3Sbluhm     fail("Call to parse was expected to fail from a closing tag mismatch");
403bd8f1dc3Sbluhm 
404bd8f1dc3Sbluhm   XML_ParserReset(parser, NULL);
405bd8f1dc3Sbluhm 
406bd8f1dc3Sbluhm   if (_XML_Parse_SINGLE_BYTES(parser, text, (int)strlen(text), XML_TRUE)
407bd8f1dc3Sbluhm       != XML_STATUS_ERROR)
408bd8f1dc3Sbluhm     fail("Call to parse was expected to fail");
409bd8f1dc3Sbluhm   if (XML_GetErrorCode(parser) != XML_ERROR_TAG_MISMATCH)
410bd8f1dc3Sbluhm     fail("Call to parse was expected to fail from a closing tag mismatch");
411bd8f1dc3Sbluhm 
412bd8f1dc3Sbluhm   XML_ParserFree(parser);
413bd8f1dc3Sbluhm #endif
414bd8f1dc3Sbluhm }
415bd8f1dc3Sbluhm END_TEST
416bd8f1dc3Sbluhm 
417bd8f1dc3Sbluhm START_TEST(test_misc_create_external_entity_parser_with_null_context) {
418bd8f1dc3Sbluhm   // With XML_DTD undefined, the only supported case of external entities
419bd8f1dc3Sbluhm   // is pattern "<!ENTITY entity123 SYSTEM 'filename123'>". A NULL context
420bd8f1dc3Sbluhm   // was causing a segfault through a null pointer dereference in function
421bd8f1dc3Sbluhm   // setContext, previously.
422bd8f1dc3Sbluhm   XML_Parser parser = XML_ParserCreate(NULL);
423bd8f1dc3Sbluhm   XML_Parser ext_parser = XML_ExternalEntityParserCreate(parser, NULL, NULL);
424bd8f1dc3Sbluhm #ifdef XML_DTD
425bd8f1dc3Sbluhm   assert_true(ext_parser != NULL);
426bd8f1dc3Sbluhm   XML_ParserFree(ext_parser);
427bd8f1dc3Sbluhm #else
428bd8f1dc3Sbluhm   assert_true(ext_parser == NULL);
429bd8f1dc3Sbluhm #endif /* XML_DTD */
430bd8f1dc3Sbluhm   XML_ParserFree(parser);
431bd8f1dc3Sbluhm }
432bd8f1dc3Sbluhm END_TEST
433bd8f1dc3Sbluhm 
434bd8f1dc3Sbluhm START_TEST(test_misc_general_entities_support) {
435bd8f1dc3Sbluhm   const char *const doc
436bd8f1dc3Sbluhm       = "<!DOCTYPE r [\n"
437bd8f1dc3Sbluhm         "<!ENTITY e1 'v1'>\n"
438bd8f1dc3Sbluhm         "<!ENTITY e2 SYSTEM 'v2'>\n"
439bd8f1dc3Sbluhm         "]>\n"
440bd8f1dc3Sbluhm         "<r a1='[&e1;]'>[&e1;][&e2;][&amp;&apos;&gt;&lt;&quot;]</r>";
441bd8f1dc3Sbluhm 
442bd8f1dc3Sbluhm   CharData storage;
443bd8f1dc3Sbluhm   CharData_Init(&storage);
444bd8f1dc3Sbluhm 
445bd8f1dc3Sbluhm   XML_Parser parser = XML_ParserCreate(NULL);
446bd8f1dc3Sbluhm   XML_SetUserData(parser, &storage);
447bd8f1dc3Sbluhm   XML_SetStartElementHandler(parser, accumulate_start_element);
448bd8f1dc3Sbluhm   XML_SetExternalEntityRefHandler(parser,
449bd8f1dc3Sbluhm                                   external_entity_failer__if_not_xml_ge);
450bd8f1dc3Sbluhm   XML_SetEntityDeclHandler(parser, accumulate_entity_decl);
451*aa071e6eSbluhm   XML_SetCharacterDataHandler(parser, accumulate_characters);
452bd8f1dc3Sbluhm 
453bd8f1dc3Sbluhm   if (_XML_Parse_SINGLE_BYTES(parser, doc, (int)strlen(doc), XML_TRUE)
454bd8f1dc3Sbluhm       != XML_STATUS_OK) {
455bd8f1dc3Sbluhm     xml_failure(parser);
456bd8f1dc3Sbluhm   }
457bd8f1dc3Sbluhm 
458bd8f1dc3Sbluhm   XML_ParserFree(parser);
459bd8f1dc3Sbluhm 
460bd8f1dc3Sbluhm   CharData_CheckXMLChars(&storage,
461bd8f1dc3Sbluhm   /* clang-format off */
462bd8f1dc3Sbluhm #if XML_GE == 1
463bd8f1dc3Sbluhm                          XCS("e1=v1\n")
464bd8f1dc3Sbluhm                          XCS("e2=(null)\n")
465bd8f1dc3Sbluhm                          XCS("(r(a1=[v1]))\n")
466bd8f1dc3Sbluhm                          XCS("[v1][][&'><\"]")
467bd8f1dc3Sbluhm #else
468bd8f1dc3Sbluhm                          XCS("e1=&amp;e1;\n")
469bd8f1dc3Sbluhm                          XCS("e2=(null)\n")
470bd8f1dc3Sbluhm                          XCS("(r(a1=[&e1;]))\n")
471bd8f1dc3Sbluhm                          XCS("[&e1;][&e2;][&'><\"]")
472bd8f1dc3Sbluhm #endif
473bd8f1dc3Sbluhm   );
474bd8f1dc3Sbluhm   /* clang-format on */
475bd8f1dc3Sbluhm }
476bd8f1dc3Sbluhm END_TEST
477bd8f1dc3Sbluhm 
478bd8f1dc3Sbluhm static void XMLCALL
479bd8f1dc3Sbluhm resumable_stopping_character_handler(void *userData, const XML_Char *s,
480bd8f1dc3Sbluhm                                      int len) {
481bd8f1dc3Sbluhm   UNUSED_P(s);
482bd8f1dc3Sbluhm   UNUSED_P(len);
483bd8f1dc3Sbluhm   XML_Parser parser = (XML_Parser)userData;
484bd8f1dc3Sbluhm   XML_StopParser(parser, XML_TRUE);
485bd8f1dc3Sbluhm }
486bd8f1dc3Sbluhm 
487bd8f1dc3Sbluhm // NOTE: This test needs active LeakSanitizer to be of actual use
488bd8f1dc3Sbluhm START_TEST(test_misc_char_handler_stop_without_leak) {
489bd8f1dc3Sbluhm   const char *const data
490bd8f1dc3Sbluhm       = "<!DOCTYPE t1[<!ENTITY e1 'angle<'><!ENTITY e2 '&e1;'>]><t1>&e2;";
491bd8f1dc3Sbluhm   XML_Parser parser = XML_ParserCreate(NULL);
492bd8f1dc3Sbluhm   assert_true(parser != NULL);
493bd8f1dc3Sbluhm   XML_SetUserData(parser, parser);
494bd8f1dc3Sbluhm   XML_SetCharacterDataHandler(parser, resumable_stopping_character_handler);
495bd8f1dc3Sbluhm   _XML_Parse_SINGLE_BYTES(parser, data, (int)strlen(data), XML_FALSE);
496bd8f1dc3Sbluhm   XML_ParserFree(parser);
497bd8f1dc3Sbluhm }
498bd8f1dc3Sbluhm END_TEST
499bd8f1dc3Sbluhm 
500*aa071e6eSbluhm START_TEST(test_misc_resumeparser_not_crashing) {
501*aa071e6eSbluhm   XML_Parser parser = XML_ParserCreate(NULL);
502*aa071e6eSbluhm   XML_GetBuffer(parser, 1);
503*aa071e6eSbluhm   XML_StopParser(parser, /*resumable=*/XML_TRUE);
504*aa071e6eSbluhm   XML_ResumeParser(parser); // could crash here, previously
505*aa071e6eSbluhm   XML_ParserFree(parser);
506*aa071e6eSbluhm }
507*aa071e6eSbluhm END_TEST
508*aa071e6eSbluhm 
509*aa071e6eSbluhm START_TEST(test_misc_stopparser_rejects_unstarted_parser) {
510*aa071e6eSbluhm   const XML_Bool cases[] = {XML_TRUE, XML_FALSE};
511*aa071e6eSbluhm   for (size_t i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) {
512*aa071e6eSbluhm     const XML_Bool resumable = cases[i];
513*aa071e6eSbluhm     XML_Parser parser = XML_ParserCreate(NULL);
514*aa071e6eSbluhm     assert_true(XML_GetErrorCode(parser) == XML_ERROR_NONE);
515*aa071e6eSbluhm     assert_true(XML_StopParser(parser, resumable) == XML_STATUS_ERROR);
516*aa071e6eSbluhm     assert_true(XML_GetErrorCode(parser) == XML_ERROR_NOT_STARTED);
517*aa071e6eSbluhm     XML_ParserFree(parser);
518*aa071e6eSbluhm   }
519*aa071e6eSbluhm }
520*aa071e6eSbluhm END_TEST
521*aa071e6eSbluhm 
522bd8f1dc3Sbluhm void
523bd8f1dc3Sbluhm make_miscellaneous_test_case(Suite *s) {
524bd8f1dc3Sbluhm   TCase *tc_misc = tcase_create("miscellaneous tests");
525bd8f1dc3Sbluhm 
526bd8f1dc3Sbluhm   suite_add_tcase(s, tc_misc);
527bd8f1dc3Sbluhm   tcase_add_checked_fixture(tc_misc, NULL, basic_teardown);
528bd8f1dc3Sbluhm 
529bd8f1dc3Sbluhm   tcase_add_test(tc_misc, test_misc_alloc_create_parser);
530bd8f1dc3Sbluhm   tcase_add_test(tc_misc, test_misc_alloc_create_parser_with_encoding);
531bd8f1dc3Sbluhm   tcase_add_test(tc_misc, test_misc_null_parser);
532bd8f1dc3Sbluhm   tcase_add_test(tc_misc, test_misc_error_string);
533bd8f1dc3Sbluhm   tcase_add_test(tc_misc, test_misc_version);
534bd8f1dc3Sbluhm   tcase_add_test(tc_misc, test_misc_features);
535bd8f1dc3Sbluhm   tcase_add_test(tc_misc, test_misc_attribute_leak);
536bd8f1dc3Sbluhm   tcase_add_test(tc_misc, test_misc_utf16le);
537bd8f1dc3Sbluhm   tcase_add_test(tc_misc, test_misc_stop_during_end_handler_issue_240_1);
538bd8f1dc3Sbluhm   tcase_add_test(tc_misc, test_misc_stop_during_end_handler_issue_240_2);
539bd8f1dc3Sbluhm   tcase_add_test__ifdef_xml_dtd(
540bd8f1dc3Sbluhm       tc_misc, test_misc_deny_internal_entity_closing_doctype_issue_317);
541bd8f1dc3Sbluhm   tcase_add_test(tc_misc, test_misc_tag_mismatch_reset_leak);
542bd8f1dc3Sbluhm   tcase_add_test(tc_misc,
543bd8f1dc3Sbluhm                  test_misc_create_external_entity_parser_with_null_context);
544bd8f1dc3Sbluhm   tcase_add_test(tc_misc, test_misc_general_entities_support);
545bd8f1dc3Sbluhm   tcase_add_test(tc_misc, test_misc_char_handler_stop_without_leak);
546*aa071e6eSbluhm   tcase_add_test(tc_misc, test_misc_resumeparser_not_crashing);
547*aa071e6eSbluhm   tcase_add_test(tc_misc, test_misc_stopparser_rejects_unstarted_parser);
548bd8f1dc3Sbluhm }
549