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 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 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 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 * 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 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 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 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 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