1 /*
2 __ __ _
3 ___\ \/ /_ __ __ _| |_
4 / _ \\ /| '_ \ / _` | __|
5 | __// \| |_) | (_| | |_
6 \___/_/\_\ .__/ \__,_|\__|
7 |_| XML parser
8
9 Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
10 Copyright (c) 2000 Clark Cooper <coopercc@users.sourceforge.net>
11 Copyright (c) 2002-2003 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
12 Copyright (c) 2004-2006 Karl Waclawek <karl@waclawek.net>
13 Copyright (c) 2005-2007 Steven Solie <steven@solie.ca>
14 Copyright (c) 2016-2026 Sebastian Pipping <sebastian@pipping.org>
15 Copyright (c) 2017 Rhodri James <rhodri@wildebeest.org.uk>
16 Copyright (c) 2019 David Loffredo <loffredo@steptools.com>
17 Copyright (c) 2021 Donghee Na <donghee.na@python.org>
18 Copyright (c) 2024 Hanno Böck <hanno@gentoo.org>
19 Copyright (c) 2025 Alfonso Gregory <gfunni234@gmail.com>
20 Copyright (c) 2026 Matthew Fernandez <matthew.fernandez@gmail.com>
21 Copyright (c) 2026 Kartik Kenchi <netliomax25@gmail.com>
22 Licensed under the MIT license:
23
24 Permission is hereby granted, free of charge, to any person obtaining
25 a copy of this software and associated documentation files (the
26 "Software"), to deal in the Software without restriction, including
27 without limitation the rights to use, copy, modify, merge, publish,
28 distribute, sublicense, and/or sell copies of the Software, and to permit
29 persons to whom the Software is furnished to do so, subject to the
30 following conditions:
31
32 The above copyright notice and this permission notice shall be included
33 in all copies or substantial portions of the Software.
34
35 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
36 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
37 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
38 NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
39 DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
40 OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
41 USE OR OTHER DEALINGS IN THE SOFTWARE.
42
43 SPDX-License-Identifier: MIT
44 */
45
46 #include "expat_config.h"
47
48 #include <inttypes.h> // PRIu64
49 #include <stdio.h>
50 #include <stdlib.h>
51 #include <stddef.h>
52 #include <string.h>
53 #include <fcntl.h>
54
55 #ifdef _WIN32
56 # include "winconfig.h"
57 #endif
58
59 #include "expat.h"
60 #include "internal.h" /* for UNUSED_P only */
61 #include "xmlfile.h"
62 #include "xmltchar.h"
63 #include "filemap.h"
64
65 /* Function "read": */
66 #if defined(_MSC_VER)
67 # include <io.h>
68 /* https://msdn.microsoft.com/en-us/library/wyssk1bs(v=vs.100).aspx */
69 # define EXPAT_read _read
70 # define EXPAT_read_count_t int
71 # define EXPAT_read_req_t unsigned int
72 #else /* POSIX */
73 # include <unistd.h>
74 /* https://pubs.opengroup.org/onlinepubs/009695399/functions/read.html */
75 # define EXPAT_read read
76 # define EXPAT_read_count_t ssize_t
77 # define EXPAT_read_req_t size_t
78 #endif
79
80 #ifndef O_BINARY
81 # ifdef _O_BINARY
82 # define O_BINARY _O_BINARY
83 # else
84 # define O_BINARY 0
85 # endif
86 #endif
87
88 int g_read_size_bytes = 1024 * 8;
89
90 typedef struct {
91 XML_Parser parser;
92 int *retPtr;
93 } PROCESS_ARGS;
94
95 static int processStream(const XML_Char *filename, XML_Parser parser);
96
97 static void
reportError(XML_Parser parser,const XML_Char * filename)98 reportError(XML_Parser parser, const XML_Char *filename) {
99 enum XML_Error code = XML_GetErrorCode(parser);
100 const XML_Char *message = XML_ErrorString(code);
101 if (message)
102 ftprintf(stdout, T("%s") T(":%") T(PRIu64) T(":%") T(PRIu64) T(": %s\n"),
103 filename, XML_GetCurrentLineNumber64(parser),
104 XML_GetCurrentColumnNumber64(parser), message);
105 else
106 ftprintf(stderr, T("%s: (unknown message %u)\n"), filename,
107 (unsigned int)code);
108 }
109
110 /* This implementation will give problems on files larger than INT_MAX. */
111 static void
processFile(const void * data,size_t size,const XML_Char * filename,void * args)112 processFile(const void *data, size_t size, const XML_Char *filename,
113 void *args) {
114 XML_Parser parser = ((PROCESS_ARGS *)args)->parser;
115 int *retPtr = ((PROCESS_ARGS *)args)->retPtr;
116 if (XML_Parse(parser, (const char *)data, (int)size, 1) == XML_STATUS_ERROR) {
117 reportError(parser, filename);
118 *retPtr = 0;
119 } else
120 *retPtr = 1;
121 }
122
123 #if defined(_WIN32)
124
125 static int
isAsciiLetter(XML_Char c)126 isAsciiLetter(XML_Char c) {
127 return (T('a') <= c && c <= T('z')) || (T('A') <= c && c <= T('Z'));
128 }
129
130 #endif /* _WIN32 */
131
132 static const XML_Char *
resolveSystemId(const XML_Char * base,const XML_Char * systemId,XML_Char ** toFree)133 resolveSystemId(const XML_Char *base, const XML_Char *systemId,
134 XML_Char **toFree) {
135 XML_Char *s;
136 *toFree = 0;
137 if (! base || *systemId == T('/')
138 #if defined(_WIN32)
139 || *systemId == T('\\')
140 || (isAsciiLetter(systemId[0]) && systemId[1] == T(':'))
141 #endif
142 )
143 return systemId;
144
145 const size_t baseLen = tcslen(base);
146 const size_t systemIdLen = tcslen(systemId);
147
148 /* Detect and prevent integer overflow in the addition (without risking
149 underflow) */
150 if (baseLen > SIZE_MAX - systemIdLen || baseLen > SIZE_MAX - systemIdLen - 2)
151 return systemId;
152
153 const size_t charsRequired = baseLen + systemIdLen + 2;
154
155 /* Detect and prevent integer overflow in the multiplication */
156 if (charsRequired > SIZE_MAX / sizeof(XML_Char))
157 return systemId;
158
159 *toFree = malloc(charsRequired * sizeof(XML_Char));
160 if (! *toFree)
161 return systemId;
162 tcscpy(*toFree, base);
163 s = *toFree;
164 if (tcsrchr(s, T('/')))
165 s = tcsrchr(s, T('/')) + 1;
166 #if defined(_WIN32)
167 if (tcsrchr(s, T('\\')))
168 s = tcsrchr(s, T('\\')) + 1;
169 #endif
170 tcscpy(s, systemId);
171 return *toFree;
172 }
173
174 static int
externalEntityRefFilemap(XML_Parser parser,const XML_Char * context,const XML_Char * base,const XML_Char * systemId,const XML_Char * publicId)175 externalEntityRefFilemap(XML_Parser parser, const XML_Char *context,
176 const XML_Char *base, const XML_Char *systemId,
177 const XML_Char *publicId) {
178 int result;
179 XML_Char *s;
180 const XML_Char *filename;
181 XML_Parser entParser = XML_ExternalEntityParserCreate(parser, context, 0);
182 int filemapRes;
183 PROCESS_ARGS args;
184 UNUSED_P(publicId);
185 args.retPtr = &result;
186 args.parser = entParser;
187 filename = resolveSystemId(base, systemId, &s);
188 XML_SetBase(entParser, filename);
189 filemapRes = filemap(filename, processFile, &args);
190 switch (filemapRes) {
191 case 0:
192 result = 0;
193 break;
194 case 2:
195 ftprintf(stderr,
196 T("%s: file too large for memory-mapping")
197 T(", switching to streaming\n"),
198 filename);
199 result = processStream(filename, entParser);
200 break;
201 }
202 free(s);
203 XML_ParserFree(entParser);
204 return result;
205 }
206
207 static int
processStream(const XML_Char * filename,XML_Parser parser)208 processStream(const XML_Char *filename, XML_Parser parser) {
209 /* passing NULL for filename means read input from stdin */
210 int fd = 0; /* 0 is the fileno for stdin */
211
212 if (filename != NULL) {
213 fd = topen(filename, O_BINARY | O_RDONLY);
214 if (fd < 0) {
215 tperror(filename);
216 return 0;
217 }
218 }
219 for (;;) {
220 EXPAT_read_count_t nread;
221 char *buf = (char *)XML_GetBuffer(parser, g_read_size_bytes);
222 if (! buf) {
223 if (filename != NULL)
224 close(fd);
225 ftprintf(stderr, T("%s: out of memory\n"),
226 filename != NULL ? filename : T("xmlwf"));
227 return 0;
228 }
229 nread = EXPAT_read(fd, buf, (EXPAT_read_req_t)g_read_size_bytes);
230 if (nread < 0) {
231 tperror(filename != NULL ? filename : T("STDIN"));
232 if (filename != NULL)
233 close(fd);
234 return 0;
235 }
236 if (XML_ParseBuffer(parser, (int)nread, nread == 0) == XML_STATUS_ERROR) {
237 reportError(parser, filename != NULL ? filename : T("STDIN"));
238 if (filename != NULL)
239 close(fd);
240 return 0;
241 }
242 if (nread == 0) {
243 if (filename != NULL)
244 close(fd);
245 break;
246 }
247 }
248 return 1;
249 }
250
251 static int
externalEntityRefStream(XML_Parser parser,const XML_Char * context,const XML_Char * base,const XML_Char * systemId,const XML_Char * publicId)252 externalEntityRefStream(XML_Parser parser, const XML_Char *context,
253 const XML_Char *base, const XML_Char *systemId,
254 const XML_Char *publicId) {
255 XML_Char *s;
256 const XML_Char *filename;
257 int ret;
258 XML_Parser entParser = XML_ExternalEntityParserCreate(parser, context, 0);
259 UNUSED_P(publicId);
260 filename = resolveSystemId(base, systemId, &s);
261 XML_SetBase(entParser, filename);
262 ret = processStream(filename, entParser);
263 free(s);
264 XML_ParserFree(entParser);
265 return ret;
266 }
267
268 bool
XML_ProcessFile(XML_Parser parser,const XML_Char * filename,unsigned flags)269 XML_ProcessFile(XML_Parser parser, const XML_Char *filename, unsigned flags) {
270 int result;
271
272 if (! XML_SetBase(parser, filename)) {
273 ftprintf(stderr, T("%s: out of memory"), filename);
274 exit(1);
275 }
276
277 if (flags & XML_EXTERNAL_ENTITIES)
278 XML_SetExternalEntityRefHandler(parser, (flags & XML_MAP_FILE)
279 ? externalEntityRefFilemap
280 : externalEntityRefStream);
281 if (flags & XML_MAP_FILE) {
282 int filemapRes;
283 PROCESS_ARGS args;
284 args.retPtr = &result;
285 args.parser = parser;
286 filemapRes = filemap(filename, processFile, &args);
287 switch (filemapRes) {
288 case 0:
289 result = 0;
290 break;
291 case 2:
292 ftprintf(stderr,
293 T("%s: file too large for memory-mapping")
294 T(", switching to streaming\n"),
295 filename);
296 result = processStream(filename, parser);
297 break;
298 }
299 } else
300 result = processStream(filename, parser);
301 return (bool)result;
302 }
303