1 /* Commonly used functions for the Expat test suite
2 __ __ _
3 ___\ \/ /_ __ __ _| |_
4 / _ \\ /| '_ \ / _` | __|
5 | __// \| |_) | (_| | |_
6 \___/_/\_\ .__/ \__,_|\__|
7 |_| XML parser
8
9 Copyright (c) 2001-2006 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
10 Copyright (c) 2003 Greg Stein <gstein@users.sourceforge.net>
11 Copyright (c) 2005-2007 Steven Solie <steven@solie.ca>
12 Copyright (c) 2005-2012 Karl Waclawek <karl@waclawek.net>
13 Copyright (c) 2016-2026 Sebastian Pipping <sebastian@pipping.org>
14 Copyright (c) 2017-2022 Rhodri James <rhodri@wildebeest.org.uk>
15 Copyright (c) 2017 Joe Orton <jorton@redhat.com>
16 Copyright (c) 2017 José Gutiérrez de la Concha <jose@zeroc.com>
17 Copyright (c) 2018 Marco Maggi <marco.maggi-ipsu@poste.it>
18 Copyright (c) 2019 David Loffredo <loffredo@steptools.com>
19 Copyright (c) 2020 Tim Gates <tim.gates@iress.com>
20 Copyright (c) 2021 Donghee Na <donghee.na@python.org>
21 Copyright (c) 2023-2024 Sony Corporation / Snild Dolkow <snild@sony.com>
22 Copyright (c) 2026 Matthew Fernandez <matthew.fernandez@gmail.com>
23 Licensed under the MIT license:
24
25 Permission is hereby granted, free of charge, to any person obtaining
26 a copy of this software and associated documentation files (the
27 "Software"), to deal in the Software without restriction, including
28 without limitation the rights to use, copy, modify, merge, publish,
29 distribute, sublicense, and/or sell copies of the Software, and to permit
30 persons to whom the Software is furnished to do so, subject to the
31 following conditions:
32
33 The above copyright notice and this permission notice shall be included
34 in all copies or substantial portions of the Software.
35
36 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
37 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
38 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
39 NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
40 DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
41 OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
42 USE OR OTHER DEALINGS IN THE SOFTWARE.
43
44 SPDX-License-Identifier: MIT
45 */
46
47 #include "expat_config.h"
48
49 #include <assert.h>
50 #include <errno.h>
51 #include <stdint.h> // for SIZE_MAX
52 #include <stdio.h>
53 #include <string.h>
54
55 #include "expat.h"
56 #include "internal.h"
57 #include "chardata.h"
58 #include "minicheck.h"
59 #include "common.h"
60 #include "handlers.h"
61
62 /* Common test data */
63
64 const char *long_character_data_text
65 = "<?xml version='1.0' encoding='iso-8859-1'?><s>"
66 "012345678901234567890123456789012345678901234567890123456789"
67 "012345678901234567890123456789012345678901234567890123456789"
68 "012345678901234567890123456789012345678901234567890123456789"
69 "012345678901234567890123456789012345678901234567890123456789"
70 "012345678901234567890123456789012345678901234567890123456789"
71 "012345678901234567890123456789012345678901234567890123456789"
72 "012345678901234567890123456789012345678901234567890123456789"
73 "012345678901234567890123456789012345678901234567890123456789"
74 "012345678901234567890123456789012345678901234567890123456789"
75 "012345678901234567890123456789012345678901234567890123456789"
76 "012345678901234567890123456789012345678901234567890123456789"
77 "012345678901234567890123456789012345678901234567890123456789"
78 "012345678901234567890123456789012345678901234567890123456789"
79 "012345678901234567890123456789012345678901234567890123456789"
80 "012345678901234567890123456789012345678901234567890123456789"
81 "012345678901234567890123456789012345678901234567890123456789"
82 "012345678901234567890123456789012345678901234567890123456789"
83 "012345678901234567890123456789012345678901234567890123456789"
84 "012345678901234567890123456789012345678901234567890123456789"
85 "012345678901234567890123456789012345678901234567890123456789"
86 "</s>";
87
88 const char *long_cdata_text
89 = "<s><![CDATA["
90 "012345678901234567890123456789012345678901234567890123456789"
91 "012345678901234567890123456789012345678901234567890123456789"
92 "012345678901234567890123456789012345678901234567890123456789"
93 "012345678901234567890123456789012345678901234567890123456789"
94 "012345678901234567890123456789012345678901234567890123456789"
95 "012345678901234567890123456789012345678901234567890123456789"
96 "012345678901234567890123456789012345678901234567890123456789"
97 "012345678901234567890123456789012345678901234567890123456789"
98 "012345678901234567890123456789012345678901234567890123456789"
99 "012345678901234567890123456789012345678901234567890123456789"
100 "012345678901234567890123456789012345678901234567890123456789"
101 "012345678901234567890123456789012345678901234567890123456789"
102 "012345678901234567890123456789012345678901234567890123456789"
103 "012345678901234567890123456789012345678901234567890123456789"
104 "012345678901234567890123456789012345678901234567890123456789"
105 "012345678901234567890123456789012345678901234567890123456789"
106 "012345678901234567890123456789012345678901234567890123456789"
107 "012345678901234567890123456789012345678901234567890123456789"
108 "012345678901234567890123456789012345678901234567890123456789"
109 "012345678901234567890123456789012345678901234567890123456789"
110 "]]></s>";
111
112 /* Having an element name longer than 1024 characters exercises some
113 * of the pool allocation code in the parser that otherwise does not
114 * get executed. The count at the end of the line is the number of
115 * characters (bytes) in the element name by that point.x
116 */
117 const char *get_buffer_test_text
118 = "<documentwitharidiculouslylongelementnametotease" /* 0x030 */
119 "aparticularcorneroftheallocationinXML_GetBuffers" /* 0x060 */
120 "othatwecanimprovethecoverageyetagain012345678901" /* 0x090 */
121 "123456789abcdef0123456789abcdef0123456789abcdef0" /* 0x0c0 */
122 "123456789abcdef0123456789abcdef0123456789abcdef0" /* 0x0f0 */
123 "123456789abcdef0123456789abcdef0123456789abcdef0" /* 0x120 */
124 "123456789abcdef0123456789abcdef0123456789abcdef0" /* 0x150 */
125 "123456789abcdef0123456789abcdef0123456789abcdef0" /* 0x180 */
126 "123456789abcdef0123456789abcdef0123456789abcdef0" /* 0x1b0 */
127 "123456789abcdef0123456789abcdef0123456789abcdef0" /* 0x1e0 */
128 "123456789abcdef0123456789abcdef0123456789abcdef0" /* 0x210 */
129 "123456789abcdef0123456789abcdef0123456789abcdef0" /* 0x240 */
130 "123456789abcdef0123456789abcdef0123456789abcdef0" /* 0x270 */
131 "123456789abcdef0123456789abcdef0123456789abcdef0" /* 0x2a0 */
132 "123456789abcdef0123456789abcdef0123456789abcdef0" /* 0x2d0 */
133 "123456789abcdef0123456789abcdef0123456789abcdef0" /* 0x300 */
134 "123456789abcdef0123456789abcdef0123456789abcdef0" /* 0x330 */
135 "123456789abcdef0123456789abcdef0123456789abcdef0" /* 0x360 */
136 "123456789abcdef0123456789abcdef0123456789abcdef0" /* 0x390 */
137 "123456789abcdef0123456789abcdef0123456789abcdef0" /* 0x3c0 */
138 "123456789abcdef0123456789abcdef0123456789abcdef0" /* 0x3f0 */
139 "123456789abcdef0123456789abcdef0123456789>\n<ef0"; /* 0x420 */
140
141 /* Test control globals */
142
143 /* Used as the "resumable" parameter to XML_StopParser by some tests */
144 XML_Bool g_resumable = XML_FALSE;
145
146 /* Used to control abort checks in some tests */
147 XML_Bool g_abortable = XML_FALSE;
148
149 /* Used to control _XML_Parse_SINGLE_BYTES() chunk size */
150 int g_chunkSize = 1;
151
152 /* Common test functions */
153
154 void
tcase_add_test__ifdef_xml_dtd(TCase * tc,tcase_test_function test)155 tcase_add_test__ifdef_xml_dtd(TCase *tc, tcase_test_function test) {
156 #ifdef XML_DTD
157 tcase_add_test(tc, test);
158 #else
159 UNUSED_P(tc);
160 UNUSED_P(test);
161 #endif
162 }
163
164 void
tcase_add_test__if_xml_ge(TCase * tc,tcase_test_function test)165 tcase_add_test__if_xml_ge(TCase *tc, tcase_test_function test) {
166 #if XML_GE == 1
167 tcase_add_test(tc, test);
168 #else
169 UNUSED_P(tc);
170 UNUSED_P(test);
171 #endif
172 }
173
174 void
basic_teardown(void)175 basic_teardown(void) {
176 if (g_parser != NULL) {
177 XML_ParserFree(g_parser);
178 g_parser = NULL;
179 }
180 }
181
182 /* Generate a failure using the parser state to create an error message;
183 this should be used when the parser reports an error we weren't
184 expecting.
185 */
186 void
_xml_failure(XML_Parser parser,const char * file,int line)187 _xml_failure(XML_Parser parser, const char *file, int line) {
188 char buffer[1024];
189 enum XML_Error err = XML_GetErrorCode(parser);
190 snprintf(buffer, sizeof(buffer),
191 " %d: %" XML_FMT_STR " (line %" XML_FMT_INT_MOD
192 "u, offset %" XML_FMT_INT_MOD "u)\n reported from %s, line %d\n",
193 err, XML_ErrorString(err), XML_GetCurrentLineNumber(parser),
194 XML_GetCurrentColumnNumber(parser), file, line);
195 _fail(file, line, buffer);
196 }
197
198 enum XML_Status
_XML_Parse_SINGLE_BYTES(XML_Parser parser,const char * s,int len,int isFinal)199 _XML_Parse_SINGLE_BYTES(XML_Parser parser, const char *s, int len,
200 int isFinal) {
201 // This ensures that tests have to run pathological parse cases
202 // (e.g. when `s` is NULL) against plain XML_Parse rather than
203 // chunking _XML_Parse_SINGLE_BYTES.
204 assert((parser != NULL) && (s != NULL) && (len >= 0));
205 const int chunksize = g_chunkSize;
206 if (chunksize > 0) {
207 // parse in chunks of `chunksize` bytes as long as not exhausting
208 for (; len > chunksize; len -= chunksize, s += chunksize) {
209 enum XML_Status res = XML_Parse(parser, s, chunksize, XML_FALSE);
210 if (res != XML_STATUS_OK) {
211 if ((res == XML_STATUS_SUSPENDED) && (len > chunksize)) {
212 fail("Use of function _XML_Parse_SINGLE_BYTES with a chunk size "
213 "greater than 0 (from g_chunkSize) does not work well with "
214 "suspension. Please consider use of plain XML_Parse at this "
215 "place in your test, instead.");
216 }
217 return res;
218 }
219 }
220 }
221 // parse the final chunk, the size of which will be <= chunksize
222 return XML_Parse(parser, s, len, isFinal);
223 }
224
225 void
_expect_failure(const char * text,enum XML_Error errorCode,const char * errorMessage,const char * file,int lineno)226 _expect_failure(const char *text, enum XML_Error errorCode,
227 const char *errorMessage, const char *file, int lineno) {
228 if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
229 == XML_STATUS_OK)
230 /* Hackish use of _fail() macro, but lets us report
231 the right filename and line number. */
232 _fail(file, lineno, errorMessage);
233 if (XML_GetErrorCode(g_parser) != errorCode)
234 _xml_failure(g_parser, file, lineno);
235 }
236
237 void
_run_character_check(const char * text,const XML_Char * expected,const char * file,int line)238 _run_character_check(const char *text, const XML_Char *expected,
239 const char *file, int line) {
240 CharData storage;
241
242 CharData_Init(&storage);
243 XML_SetUserData(g_parser, &storage);
244 XML_SetCharacterDataHandler(g_parser, accumulate_characters);
245 if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
246 == XML_STATUS_ERROR)
247 _xml_failure(g_parser, file, line);
248 CharData_CheckXMLChars(&storage, expected);
249 }
250
251 void
_run_attribute_check(const char * text,const XML_Char * expected,const char * file,int line)252 _run_attribute_check(const char *text, const XML_Char *expected,
253 const char *file, int line) {
254 CharData storage;
255
256 CharData_Init(&storage);
257 XML_SetUserData(g_parser, &storage);
258 XML_SetStartElementHandler(g_parser, accumulate_attribute);
259 if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
260 == XML_STATUS_ERROR)
261 _xml_failure(g_parser, file, line);
262 CharData_CheckXMLChars(&storage, expected);
263 }
264
265 void
_run_ext_character_check(const char * text,ExtTest * test_data,const XML_Char * expected,const char * file,int line)266 _run_ext_character_check(const char *text, ExtTest *test_data,
267 const XML_Char *expected, const char *file, int line) {
268 CharData *const storage = malloc(sizeof(CharData));
269
270 CharData_Init(storage);
271 test_data->storage = storage;
272 XML_SetUserData(g_parser, test_data);
273 XML_SetCharacterDataHandler(g_parser, ext_accumulate_characters);
274 if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
275 == XML_STATUS_ERROR)
276 _xml_failure(g_parser, file, line);
277 CharData_CheckXMLChars(storage, expected);
278
279 free(storage);
280 }
281
282 /* Control variable; the number of times duff_allocator() will successfully
283 * allocate */
284 #define ALLOC_ALWAYS_SUCCEED (-1)
285 #define REALLOC_ALWAYS_SUCCEED (-1)
286
287 int g_allocation_count = ALLOC_ALWAYS_SUCCEED;
288 int g_reallocation_count = REALLOC_ALWAYS_SUCCEED;
289
290 /* Crocked allocator for allocation failure tests */
291 void *
duff_allocator(size_t size)292 duff_allocator(size_t size) {
293 if (g_allocation_count == 0)
294 return NULL;
295 if (g_allocation_count != ALLOC_ALWAYS_SUCCEED)
296 g_allocation_count--;
297 return malloc(size);
298 }
299
300 /* Crocked reallocator for allocation failure tests */
301 void *
duff_reallocator(void * ptr,size_t size)302 duff_reallocator(void *ptr, size_t size) {
303 if (g_reallocation_count == 0)
304 return NULL;
305 if (g_reallocation_count != REALLOC_ALWAYS_SUCCEED)
306 g_reallocation_count--;
307 return realloc(ptr, size);
308 }
309
310 // Portable remake of strnlen(3) for C99
311 static size_t
portable_strnlen(const char * s,size_t maxlen)312 portable_strnlen(const char *s, size_t maxlen) {
313 const char *const end = (const char *)memchr(s, '\0', maxlen);
314 return (end == NULL) ? maxlen : (size_t)(end - s);
315 }
316
317 // Portable remake of strndup(3) for C99
318 char *
portable_strndup(const char * s,size_t n)319 portable_strndup(const char *s, size_t n) {
320 if ((s == NULL) || (n == SIZE_MAX)) {
321 errno = EINVAL;
322 return NULL;
323 }
324
325 n = portable_strnlen(s, n);
326
327 char *const buffer = malloc(n + 1);
328 if (buffer == NULL) {
329 errno = ENOMEM;
330 return NULL;
331 }
332
333 errno = 0;
334
335 memcpy(buffer, s, n);
336
337 buffer[n] = '\0';
338
339 return buffer;
340 }
341