aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPhilip Paeps <philip@FreeBSD.org>2026-06-25 14:32:40 +0000
committerPhilip Paeps <philip@FreeBSD.org>2026-06-25 14:32:40 +0000
commit28bb687ea813d17f5dc8370ba4bdd11a397a749c (patch)
tree1a0de4db214a6c55c5c15065c6b0ba8663a50aa1
parente3c0f2f39909f86f1707b375a63fff5ed1f31b97 (diff)
-rw-r--r--Changes92
-rw-r--r--README.md5
-rw-r--r--configure.ac23
-rw-r--r--doc/reference.html6
-rw-r--r--doc/xmlwf.12
-rw-r--r--doc/xmlwf.xml2
-rw-r--r--lib/Makefile.am5
-rw-r--r--lib/Makefile.in5
-rw-r--r--lib/expat.h2
-rw-r--r--lib/fallthrough.h49
-rw-r--r--lib/memory_sanitizer.h51
-rw-r--r--lib/random_arc4random_buf.c4
-rw-r--r--lib/random_dev_urandom.c4
-rw-r--r--lib/random_getentropy.c8
-rw-r--r--lib/random_getrandom.c11
-rw-r--r--lib/siphash.h15
-rw-r--r--lib/xcsinc.c48
-rw-r--r--lib/xmlparse.c1206
-rw-r--r--lib/xmltok.c6
-rw-r--r--lib/xmltok_impl.c21
-rw-r--r--tests/acc_tests.c6
-rw-r--r--tests/alloc_tests.c6
-rw-r--r--tests/basic_tests.c48
-rw-r--r--tests/chardata.c14
-rw-r--r--tests/common.c5
-rw-r--r--tests/handlers.c53
-rw-r--r--tests/handlers.h14
-rw-r--r--tests/misc_tests.c42
-rw-r--r--xmlwf/filemap.h3
-rw-r--r--xmlwf/win32filemap.c2
-rw-r--r--xmlwf/xmlfile.c18
-rw-r--r--xmlwf/xmlwf.c107
-rwxr-xr-xxmlwf/xmlwf_helpgen.py2
33 files changed, 1228 insertions, 657 deletions
diff --git a/Changes b/Changes
index 313b3dc60cd0..6a1b89160fb9 100644
--- a/Changes
+++ b/Changes
@@ -29,6 +29,94 @@
!! THANK YOU! Sebastian Pipping -- Berlin, 2026-03-17 !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+Release 2.8.2 Thu June 25 2026
+ Security fixes:
+ #1246 CVE-2026-50219 -- Disallow calls to functions
+ `XML_GetBuffer`, `XML_Parse`, `XML_ParseBuffer`,
+ `XML_ParserFree`, `XML_ParserReset` to guard e.g.
+ Expat bindings from memory corruption;
+ this CPython issue is related:
+ https://github.com/python/cpython/issues/146169
+ #1267 CVE-2026-56131 -- Protect XML_ResumeParser from being called
+ from a handler, plugging a hole in the fix
+ to CVE-2026-50219
+ #1272 CVE-2026-56132 -- Fix out-of-bound scaffolding index store
+ in `doProlog`
+ #1229 #1232 CVE-2026-56403 -- Integer overflow in `storeAtts`
+ #1249 CVE-2026-56404 -- Integer overflow in `addBinding`
+ #1251 CVE-2026-56405 -- Integer overflow in `getAttributeId`
+ #1255 CVE-2026-56406 -- Integer overflow in `XML_ParseBuffer`
+ #1262 CVE-2026-56407 -- Integer overflow in `textLen` handling
+ #565 CVE-2026-56408 -- Integer overflow in `copyString`
+ (commit 16e2efd867ea8567ffa012210b52ef5918e20817)
+ #1259 CVE-2026-56409 -- xmlwf: Integer overflow in output path join
+ #1252 CVE-2026-56410 -- xmlwf: Integer overflow in
+ `resolveSystemId`
+ #1263 CVE-2026-56411 -- xmlwf: Integer overflow in notation list
+ allocation
+ #1278 CVE-2026-56412 -- Guard XML_TOK_DATA_CHARS handler calls in
+ `doCdataSection`, plugging a hole in the fix to
+ CVE-2026-50219
+
+ Bug fixes:
+ #1260 xmlwf: Escape names and base URI in meta output
+ #1266 xmlwf: Pick a safe quote for notation system and public IDs
+
+ Other changes:
+ #1257 CMake|Autotools: Stop using /dev/urandom by default
+ #1244 #1254 CMake: Fix guard for Unix sources of entropy
+ #1183 #1270 CMake|Windows: Add missing export for symbol
+ `XML_SetHashSalt16Bytes`
+ #1236 CMake: Mark option EXPAT_OSSFUZZ_BUILD as advanced
+ #1283 Limit output indentation for EXPAT_ENTITY_DEBUG=1 and
+ allow unlimited indentation via EXPAT_ENTITY_DEBUG=2
+ #565 Replace some loops by use of `memcpy`, `strlen`, `wcslen`
+ #1220 lib: Use a size_t for group sizes
+ #1221 lib: Fix too-conservative integer overflow check when
+ appending raw name
+ #1222 lib: Simplify attribute allocation/management logic
+ #1224 Update fallthrough annotations to satisfy Clang and GCC
+ #1226 lib: Remove unnecessary void * casts in random code
+ #1228 lib: Reduce scope of locals in storeAtts
+ #1230 lib: Count attributes with size_t variables
+ #1238 Minor get-buffer improvements
+ #1239 #1240 lib|tests: Include header expat_config.h first
+ #1241 lib: Shrink size of XML_GetBuffer
+ #1242 lib: Remove a legacy comment
+ #1243 lib: XML_ParserReset: Extract repeated linked-list move logic
+ #1243 lib: Unify entity free lists
+ #1247 lib: Fix use of '0' as boolean literal
+ #1248 lib: Make XML_Index overflow check more intuitive
+ #1256 lib: Use size_t for counting string/URI lengths
+ #1258 lib: XML_GetInputContext: Remove use of 0 for NULL
+ #1261 Comment typo fixes
+ #1275 Teach Memory Sanitizer semantics of randomization functions
+ #1276 #1281 Version info bumped from 13:1:12 (libexpat*.so.1.12.1)
+ to 13:2:12 (libexpat*.so.1.12.2); see https://verbump.de/
+ for what these numbers do
+
+ Infrastructure:
+ #1231 perl-integration.yml: Bump to XML::Parser 2.59
+ #1237 emscripten.yml: Bump from Ubuntu 22.04 to 24.04
+ #1183 #1271 windows-build.yml: Cover completeness of file
+ libexpat.def.cmake
+ #1274 linux.yml: Make llvm-symbolizer available in CI
+
+ Special thanks to:
+ Alessandro Gario
+ Asher Darden
+ Christoph Reiter
+ Haris Hussain
+ Matthew Fernandez
+ Kartik Kenchi
+ Nick Begg
+ Sajin S
+ Yousef Shanableh
+ and
+ Anthropic
+ Astra Security
+ Trail of Bits
+
Release 2.8.1 Sun May 10 2026
Security fixes:
#1216 CVE-2026-45186 -- Fix quadratic runtime from attribute name
@@ -1820,7 +1908,7 @@ Release 2.1.0 Sat March 24 2012
- Bug Fixes:
#1742315: Harmful XML_ParserCreateNS suggestion.
#1785430: Expat build fails on linux-amd64 with gcc version>=4.1 -O3.
- #1983953, 2517952, 2517962, 2649838:
+ #1983953, 2517952, 2517962, 2649838:
Build modifications using autoreconf instead of buildconf.sh.
#2815947, #2884086: OBJEXT and EXEEXT support while building.
#2517938: xmlwf should return non-zero exit status if not well-formed.
@@ -1846,7 +1934,7 @@ Release 2.1.0 Sat March 24 2012
See bug #2990652 and patch #3312568.
Added run-benchmark target to Makefile.in - relies on testdata module
present in the same relative location as in the repository.
-
+
Release 2.0.1 Tue June 5 2007
- Fixed bugs #1515266, #1515600: The character data handler's calling
of XML_StopParser() was not handled properly; if the parser was
diff --git a/README.md b/README.md
index 7a5ed463b6d5..5ad5e1e85afa 100644
--- a/README.md
+++ b/README.md
@@ -11,7 +11,7 @@
> at the top of the `Changes` file.
-# Expat, Release 2.8.1
+# Expat, Release 2.8.2
This is Expat, a C99 library for parsing
[XML 1.0 Fourth Edition](https://www.w3.org/TR/2006/REC-xml-20060816/), started by
@@ -286,9 +286,6 @@ EXPAT_ENABLE_INSTALL:BOOL=ON
// Use /MT flag (static CRT) when compiling in MSVC
EXPAT_MSVC_STATIC_CRT:BOOL=OFF
-// Build fuzzers via OSS-Fuzz for the expat library
-EXPAT_OSSFUZZ_BUILD:BOOL=OFF
-
// Build a shared expat library
EXPAT_SHARED_LIBS:BOOL=ON
diff --git a/configure.ac b/configure.ac
index fb492dfd79f7..f14c0308e1c4 100644
--- a/configure.ac
+++ b/configure.ac
@@ -90,7 +90,7 @@ dnl If the API changes incompatibly set LIBAGE back to 0
dnl
LIBCURRENT=13 # sync
-LIBREVISION=1 # with
+LIBREVISION=2 # with
LIBAGE=12 # CMakeLists.txt!
AC_CONFIG_HEADERS([expat_config.h])
@@ -395,8 +395,23 @@ AC_DEFINE([XML_GE], 1,
[Define as 1/0 to enable/disable support for general entities.])
AC_DEFINE([XML_DTD], 1,
[Define to make parameter entity parsing functionality available.])
-AC_DEFINE([XML_DEV_URANDOM], 1,
- [Define to include code reading entropy from `/dev/urandom'.])
+
+
+have_dev_urandom=false
+
+AC_ARG_WITH([dev-urandom],
+ [AS_HELP_STRING([--with-dev-urandom],
+ [enforce the use of /dev/urandom in the system @<:@default=no@:>@])
+AS_HELP_STRING([--without-dev-urandom],
+ [enforce not using /dev/urandom in the system @<:@default=no@:>@])],
+ [with_dev_urandom=${withval}],
+ [])
+
+AS_IF([test "x${with_dev_urandom}" = "xyes"],
+ [have_dev_urandom=true
+ AC_DEFINE([XML_DEV_URANDOM], 1,
+ [Define to include code reading entropy from `/dev/urandom'.])])
+
AC_ARG_ENABLE([xml-attr-info],
[AS_HELP_STRING([--enable-xml-attr-info],
@@ -580,7 +595,7 @@ Entropy sources:
getentropy: ${have_getentropy}
getrandom: ${have_getrandom_function}
syscall SYS_getrandom: ${have_getrandom_syscall}
- /dev/urandom: true
+ /dev/urandom: ${have_dev_urandom}
Continue with e.g.:
make -j2
diff --git a/doc/reference.html b/doc/reference.html
index 07d331ed7b56..5c4b1c76ca78 100644
--- a/doc/reference.html
+++ b/doc/reference.html
@@ -53,7 +53,7 @@
<body>
<div>
<h1>
- The Expat XML Parser <small>Release 2.8.1</small>
+ The Expat XML Parser <small>Release 2.8.2</small>
</h1>
</div>
@@ -1602,7 +1602,9 @@ XML_ParserReset(XML_Parser p,
To state the obvious: the three parsing functions <code><a href=
"#XML_Parse">XML_Parse</a></code>, <code><a href=
"#XML_ParseBuffer">XML_ParseBuffer</a></code> and <code><a href=
- "#XML_GetBuffer">XML_GetBuffer</a></code> must not be called from within a
+ "#XML_GetBuffer">XML_GetBuffer</a></code> as well as the two cleanup functions
+ <code><a href="#XML_ParserFree">XML_ParserFree</a></code> and <code><a href=
+ "#XML_ParserReset">XML_ParserReset</a></code> must not be called from within a
handler unless they operate on a separate parser instance, that is, one that did
not call the handler. For example, it is OK to call the parsing functions from
within an <code>XML_ExternalEntityRefHandler</code>, if they apply to the parser
diff --git a/doc/xmlwf.1 b/doc/xmlwf.1
index 12760944a5da..3de559ec1bb3 100644
--- a/doc/xmlwf.1
+++ b/doc/xmlwf.1
@@ -5,7 +5,7 @@
\\$2 \(la\\$1\(ra\\$3
..
.if \n(.g .mso www.tmac
-.TH XMLWF 1 "May 10, 2026" "" ""
+.TH XMLWF 1 "June 25, 2026" "" ""
.SH NAME
xmlwf \- Determines if an XML document is well-formed
.SH SYNOPSIS
diff --git a/doc/xmlwf.xml b/doc/xmlwf.xml
index 529dbba16722..7e57cf1346b9 100644
--- a/doc/xmlwf.xml
+++ b/doc/xmlwf.xml
@@ -21,7 +21,7 @@
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd" [
<!ENTITY dhfirstname "<firstname>Scott</firstname>">
<!ENTITY dhsurname "<surname>Bronson</surname>">
- <!ENTITY dhdate "<date>May 10, 2026</date>">
+ <!ENTITY dhdate "<date>June 25, 2026</date>">
<!-- Please adjust this^^ date whenever cutting a new release. -->
<!ENTITY dhsection "<manvolnum>1</manvolnum>">
<!ENTITY dhemail "<email>bronson@rinspin.com</email>">
diff --git a/lib/Makefile.am b/lib/Makefile.am
index 27bf2ab64769..2b6aec2e6bdc 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -10,6 +10,8 @@
# Copyright (c) 2017 Tomasz Kłoczko <kloczek@fedoraproject.org>
# Copyright (c) 2019 David Loffredo <loffredo@steptools.com>
# Copyright (c) 2026 Gordon Messmer <gordon.messmer@gmail.com>
+# Copyright (c) 2026 Nick Begg <nick@stunttruck.net>
+# Copyright (c) 2026 Matthew Fernandez <matthew.fernandez@gmail.com>
# Licensed under the MIT license:
#
# Permission is hereby granted, free of charge, to any person obtaining
@@ -112,14 +114,17 @@ EXTRA_DIST = \
asciitab.h \
expat_external.h \
expat.h \
+ fallthrough.h \
iasciitab.h \
internal.h \
latin1tab.h \
libexpat.def.cmake \
+ memory_sanitizer.h \
nametab.h \
siphash.h \
utf8tab.h \
winconfig.h \
+ xcsinc.c \
xmlrole.h \
xmltok.h \
xmltok_impl.c \
diff --git a/lib/Makefile.in b/lib/Makefile.in
index 4cd6fcfc6088..73db0e584705 100644
--- a/lib/Makefile.in
+++ b/lib/Makefile.in
@@ -26,6 +26,8 @@
# Copyright (c) 2017 Tomasz Kłoczko <kloczek@fedoraproject.org>
# Copyright (c) 2019 David Loffredo <loffredo@steptools.com>
# Copyright (c) 2026 Gordon Messmer <gordon.messmer@gmail.com>
+# Copyright (c) 2026 Nick Begg <nick@stunttruck.net>
+# Copyright (c) 2026 Matthew Fernandez <matthew.fernandez@gmail.com>
# Licensed under the MIT license:
#
# Permission is hereby granted, free of charge, to any person obtaining
@@ -508,14 +510,17 @@ EXTRA_DIST = \
asciitab.h \
expat_external.h \
expat.h \
+ fallthrough.h \
iasciitab.h \
internal.h \
latin1tab.h \
libexpat.def.cmake \
+ memory_sanitizer.h \
nametab.h \
siphash.h \
utf8tab.h \
winconfig.h \
+ xcsinc.c \
xmlrole.h \
xmltok.h \
xmltok_impl.c \
diff --git a/lib/expat.h b/lib/expat.h
index ec3f58544cb0..c493c70441c4 100644
--- a/lib/expat.h
+++ b/lib/expat.h
@@ -1094,7 +1094,7 @@ XML_SetReparseDeferralEnabled(XML_Parser parser, XML_Bool enabled);
*/
# define XML_MAJOR_VERSION 2
# define XML_MINOR_VERSION 8
-# define XML_MICRO_VERSION 1
+# define XML_MICRO_VERSION 2
# ifdef __cplusplus
}
diff --git a/lib/fallthrough.h b/lib/fallthrough.h
new file mode 100644
index 000000000000..707dbdd44bfe
--- /dev/null
+++ b/lib/fallthrough.h
@@ -0,0 +1,49 @@
+/*
+ __ __ _
+ ___\ \/ /_ __ __ _| |_
+ / _ \\ /| '_ \ / _` | __|
+ | __// \| |_) | (_| | |_
+ \___/_/\_\ .__/ \__,_|\__|
+ |_| XML parser
+
+ Copyright (c) 2026 Nick Begg <nick@stunttruck.net>
+ Licensed under the MIT license:
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to permit
+ persons to whom the Software is furnished to do so, subject to the
+ following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+ NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef FALLTHROUGH_H
+# define FALLTHROUGH_H 1
+
+// Explicit fallthrough in switch case to avoid warnings
+// with compiler flag -Wimplicit-fallthrough.
+
+# define EXPAT_FALLTHROUGH \
+ do { \
+ } while (0)
+
+# if defined(__has_attribute)
+# if __has_attribute(fallthrough)
+# undef EXPAT_FALLTHROUGH
+# define EXPAT_FALLTHROUGH __attribute__((fallthrough))
+# endif
+# endif
+
+#endif // FALLTHROUGH_H
diff --git a/lib/memory_sanitizer.h b/lib/memory_sanitizer.h
new file mode 100644
index 000000000000..a8a8006ccded
--- /dev/null
+++ b/lib/memory_sanitizer.h
@@ -0,0 +1,51 @@
+/*
+ __ __ _
+ ___\ \/ /_ __ __ _| |_
+ / _ \\ /| '_ \ / _` | __|
+ | __// \| |_) | (_| | |_
+ \___/_/\_\ .__/ \__,_|\__|
+ |_| XML parser
+
+ Copyright (c) 2026 Matthew Fernandez <matthew.fernandez@gmail.com>
+ Licensed under the MIT license:
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to permit
+ persons to whom the Software is furnished to do so, subject to the
+ following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+ NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#if ! defined(MEMORY_SANITIZER_H)
+# define MEMORY_SANITIZER_H 1
+
+# if defined(__has_feature)
+# if __has_feature(memory_sanitizer)
+# include <sanitizer/msan_interface.h>
+
+// inform Memory Sanitizer that [base, base + extent) is now initialized
+# define MSAN_UNPOISON(base, extent) __msan_unpoison((base), (extent))
+
+# endif
+# endif
+
+# if ! defined(MSAN_UNPOISON)
+# define MSAN_UNPOISON(base, extent) \
+ do { \
+ } while (0)
+# endif
+
+#endif // ! defined(MEMORY_SANITIZER_H)
diff --git a/lib/random_arc4random_buf.c b/lib/random_arc4random_buf.c
index 1476f07ba1a2..5212b9f192ce 100644
--- a/lib/random_arc4random_buf.c
+++ b/lib/random_arc4random_buf.c
@@ -7,6 +7,7 @@
|_| XML parser
Copyright (c) 2026 Sebastian Pipping <sebastian@pipping.org>
+ Copyright (c) 2026 Matthew Fernandez <matthew.fernandez@gmail.com>
Licensed under the MIT license:
Permission is hereby granted, free of charge, to any person obtaining
@@ -35,9 +36,12 @@
# define _DEFAULT_SOURCE 1 /* for glibc */
#endif
+#include "memory_sanitizer.h"
#include <stdlib.h> // for arc4random_buf
void
writeRandomBytes_arc4random_buf(void *target, size_t count) {
arc4random_buf(target, count);
+ // MSan does not understand `arc4random_buf`, so explain its effects
+ MSAN_UNPOISON(target, count);
}
diff --git a/lib/random_dev_urandom.c b/lib/random_dev_urandom.c
index e508fe9f3e3c..acae6ba4b047 100644
--- a/lib/random_dev_urandom.c
+++ b/lib/random_dev_urandom.c
@@ -49,11 +49,11 @@ writeRandomBytes_dev_urandom(void *target, size_t count) {
const int fd = open("/dev/urandom", O_RDONLY | O_CLOEXEC);
if (fd < 0) {
- return 0;
+ return false;
}
do {
- void *const currentTarget = (void *)((char *)target + bytesWrittenTotal);
+ void *const currentTarget = (char *)target + bytesWrittenTotal;
const size_t bytesToWrite = count - bytesWrittenTotal;
errno = 0;
diff --git a/lib/random_getentropy.c b/lib/random_getentropy.c
index b4be70ed73c1..5cea2d8838b9 100644
--- a/lib/random_getentropy.c
+++ b/lib/random_getentropy.c
@@ -7,6 +7,7 @@
|_| XML parser
Copyright (c) 2026 Sebastian Pipping <sebastian@pipping.org>
+ Copyright (c) 2026 Matthew Fernandez <matthew.fernandez@gmail.com>
Licensed under the MIT license:
Permission is hereby granted, free of charge, to any person obtaining
@@ -45,10 +46,15 @@
# include <unistd.h>
#endif // ! defined(__APPLE__)
+#include "memory_sanitizer.h"
#include <errno.h>
bool
writeRandomBytes_getentropy(void *target, size_t count) {
errno = 0;
- return getentropy(target, count) == 0;
+ const bool success = getentropy(target, count);
+ // MSan does not understand `getentropy`, so explain its effects
+ if (success)
+ MSAN_UNPOISON(target, count);
+ return success;
}
diff --git a/lib/random_getrandom.c b/lib/random_getrandom.c
index e5724a211cc6..72e2cc7093be 100644
--- a/lib/random_getrandom.c
+++ b/lib/random_getrandom.c
@@ -32,10 +32,10 @@
USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
-#include "random_getrandom.h"
-
#include "expat_config.h" // for HAVE_GETRANDOM, HAVE_SYSCALL_GETRANDOM
+#include "random_getrandom.h"
+
#if defined(HAVE_GETRANDOM)
# include <sys/random.h> /* getrandom */
#endif
@@ -52,6 +52,7 @@
# define GRND_NONBLOCK 0x0001
#endif /* defined(GRND_NONBLOCK) */
+#include "memory_sanitizer.h"
#include <assert.h>
#include <errno.h>
#include <limits.h> // for INT_MAX
@@ -64,7 +65,7 @@ writeRandomBytes_getrandom_nonblock(void *target, size_t count) {
const unsigned int getrandomFlags = GRND_NONBLOCK;
do {
- void *const currentTarget = (void *)((char *)target + bytesWrittenTotal);
+ void *const currentTarget = (char *)target + bytesWrittenTotal;
const size_t bytesToWrite = count - bytesWrittenTotal;
assert(bytesToWrite <= INT_MAX);
@@ -74,9 +75,13 @@ writeRandomBytes_getrandom_nonblock(void *target, size_t count) {
const int bytesWrittenMore =
#if defined(HAVE_GETRANDOM)
(int)getrandom(currentTarget, bytesToWrite, getrandomFlags);
+ // MSan understands `getrandom`, so does not need extra guidance
#else
(int)syscall(SYS_getrandom, currentTarget, bytesToWrite,
getrandomFlags);
+ // MSan does not understand `syscall`, so explain its effects
+ if (bytesWrittenMore > 0)
+ MSAN_UNPOISON(currentTarget, bytesWrittenMore);
#endif
if (bytesWrittenMore > 0) {
diff --git a/lib/siphash.h b/lib/siphash.h
index 04f6f74585b5..be216b4006dc 100644
--- a/lib/siphash.h
+++ b/lib/siphash.h
@@ -100,6 +100,7 @@
#include <stddef.h> /* size_t */
#include <stdint.h> /* uint64_t uint32_t uint8_t */
+#include "fallthrough.h"
/*
* Workaround to not require a C++11 compiler for using ULL suffix
@@ -234,25 +235,25 @@ sip24_final(struct siphash *H) {
switch (left) {
case 7:
b |= (uint64_t)H->buf[6] << 48;
- /* fall through */
+ EXPAT_FALLTHROUGH;
case 6:
b |= (uint64_t)H->buf[5] << 40;
- /* fall through */
+ EXPAT_FALLTHROUGH;
case 5:
b |= (uint64_t)H->buf[4] << 32;
- /* fall through */
+ EXPAT_FALLTHROUGH;
case 4:
b |= (uint64_t)H->buf[3] << 24;
- /* fall through */
+ EXPAT_FALLTHROUGH;
case 3:
b |= (uint64_t)H->buf[2] << 16;
- /* fall through */
+ EXPAT_FALLTHROUGH;
case 2:
b |= (uint64_t)H->buf[1] << 8;
- /* fall through */
+ EXPAT_FALLTHROUGH;
case 1:
b |= (uint64_t)H->buf[0] << 0;
- /* fall through */
+ EXPAT_FALLTHROUGH;
case 0:
break;
}
diff --git a/lib/xcsinc.c b/lib/xcsinc.c
new file mode 100644
index 000000000000..3597c2480bc9
--- /dev/null
+++ b/lib/xcsinc.c
@@ -0,0 +1,48 @@
+/* This file is included from other .c files!
+ __ __ _
+ ___\ \/ /_ __ __ _| |_
+ / _ \\ /| '_ \ / _` | __|
+ | __// \| |_) | (_| | |_
+ \___/_/\_\ .__/ \__,_|\__|
+ |_| XML parser
+
+ Copyright (c) 2022 Sebastian Pipping <sebastian@pipping.org>
+ Licensed under the MIT license:
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to permit
+ persons to whom the Software is furnished to do so, subject to the
+ following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+ NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+static size_t
+xcslen(const XML_Char *s) {
+#ifdef XML_UNICODE
+# ifdef XML_UNICODE_WCHAR_T
+ return wcslen(s);
+# else
+ // XML_Char is unsigned short
+ size_t len = 0;
+ while (s[len]) {
+ len++;
+ }
+ return len;
+# endif
+#else
+ return strlen(s);
+#endif
+}
diff --git a/lib/xmlparse.c b/lib/xmlparse.c
index 890a9389f15d..60f6cc700548 100644
--- a/lib/xmlparse.c
+++ b/lib/xmlparse.c
@@ -1,4 +1,4 @@
-/* 75ef4224f81c052e9e5aeea2ac7de75357d2169ff9908e39edc08b9dc3052513 (2.8.1+)
+/* 5de44e6750c6cc78818f06ed552f522a1241df0299395250e1792cb339389daf (2.8.2+)
__ __ _
___\ \/ /_ __ __ _| |_
/ _ \\ /| '_ \ / _` | __|
@@ -47,6 +47,9 @@
Copyright (c) 2026 Rosen Penev <rosenp@gmail.com>
Copyright (c) 2026 Francesco Bertolaccini
Copyright (c) 2026 Christian Ng <christianrng@berkeley.edu>
+ Copyright (c) 2026 Nick Begg <nick@stunttruck.net>
+ Copyright (c) 2026 Kartik Kenchi <netliomax25@gmail.com>
+ Copyright (c) 2026 Haris Hussain <hextheshadow0x@gmail.com>
Licensed under the MIT license:
Permission is hereby granted, free of charge, to any person obtaining
@@ -90,7 +93,7 @@
#include <stddef.h>
#include <string.h> /* memset(), memcpy() */
#include <assert.h>
-#include <limits.h> /* INT_MAX, UINT_MAX */
+#include <limits.h> /* INT_MAX, LLONG_MAX, LONG_MAX, UINT_MAX */
#include <stdio.h> /* fprintf */
#include <stdlib.h> /* getenv */
#include <stdint.h> /* SIZE_MAX, uintptr_t */
@@ -114,6 +117,7 @@
#include "ascii.h"
#include "expat.h"
#include "siphash.h"
+#include "xcsinc.c"
#if defined(HAVE_ARC4RANDOM)
# include "random_arc4random.h"
@@ -156,7 +160,7 @@
* Windows >=Vista (rand_s): _WIN32. \
\
If you insist on not using any of these, bypass this error by defining \
- XML_POOR_ENTROPY; you have been warned. \
+ XML_POOR_ENTROPY and be vulnerable to hash flooding; you have been warned. \
\
If you have reasons to patch this detection code away or need changes \
to the build system, please open a bug. Thank you!
@@ -207,6 +211,12 @@ typedef char ICHAR;
#endif
+#ifdef XML_LARGE_SIZE
+# define XML_INDEX_MAX LLONG_MAX
+#else
+# define XML_INDEX_MAX LONG_MAX
+#endif
+
/* Round up n to be a multiple of sz, where sz is a power of 2. */
#define ROUND_UP(n, sz) (((n) + ((sz) - 1)) & ~((sz) - 1))
@@ -270,8 +280,8 @@ typedef struct binding {
struct binding *prevPrefixBinding;
const struct attribute_id *attId;
XML_Char *uri;
- int uriLen;
- int uriAlloc;
+ size_t uriLen;
+ size_t uriAlloc;
} BINDING;
typedef struct prefix {
@@ -283,9 +293,9 @@ typedef struct {
const XML_Char *str;
const XML_Char *localPart;
const XML_Char *prefix;
- int strLen;
- int uriLen;
- int prefixLen;
+ size_t strLen;
+ size_t uriLen;
+ size_t prefixLen;
} TAG_NAME;
/* TAG represents an open element.
@@ -384,8 +394,8 @@ typedef struct {
const XML_Char *name;
PREFIX *prefix;
const ATTRIBUTE_ID *idAtt;
- int nDefaultAtts;
- int allocDefaultAtts;
+ size_t nDefaultAtts;
+ size_t allocDefaultAtts;
DEFAULT_ATTRIBUTE *defaultAtts;
HASH_TABLE defaultAttsNames;
} ELEMENT_TYPE;
@@ -417,6 +427,7 @@ typedef struct {
unsigned scaffCount;
int scaffLevel;
int *scaffIndex;
+ size_t scaffIndexSize;
} DTD;
enum EntityType {
@@ -588,6 +599,7 @@ static XML_Char *poolAppend(STRING_POOL *pool, const ENCODING *enc,
static XML_Char *poolStoreString(STRING_POOL *pool, const ENCODING *enc,
const char *ptr, const char *end);
static XML_Bool FASTCALL poolGrow(STRING_POOL *pool);
+static bool FASTCALL poolGrowUntil(STRING_POOL *pool, size_t needed);
static const XML_Char *FASTCALL poolCopyString(STRING_POOL *pool,
const XML_Char *s);
static const XML_Char *FASTCALL poolCopyStringNoFinish(STRING_POOL *pool,
@@ -642,16 +654,40 @@ static XML_Parser getRootParserOf(XML_Parser parser,
static unsigned long getDebugLevel(const char *variableName,
unsigned long defaultDebugLevel);
+static bool poolAppendChar(STRING_POOL *pool, XML_Char c);
+
+static bool poolAppendChars(STRING_POOL *pool, const XML_Char *s, size_t len);
+
#define poolStart(pool) ((pool)->start)
#define poolLength(pool) ((pool)->ptr - (pool)->start)
#define poolChop(pool) ((void)--(pool->ptr))
#define poolLastChar(pool) (((pool)->ptr)[-1])
#define poolDiscard(pool) ((pool)->ptr = (pool)->start)
#define poolFinish(pool) ((pool)->start = (pool)->ptr)
-#define poolAppendChar(pool, c) \
- (((pool)->ptr == (pool)->end && ! poolGrow(pool)) \
- ? 0 \
- : ((*((pool)->ptr)++ = c), 1))
+
+bool
+poolAppendChar(STRING_POOL *pool, XML_Char c) {
+ if (pool->ptr == pool->end && ! poolGrow(pool))
+ return false;
+
+ *(pool->ptr)++ = c;
+ return true;
+}
+
+bool
+poolAppendChars(STRING_POOL *pool, const XML_Char *s, size_t len) {
+ // Detect and prevent integer overflow
+ if (len > SIZE_MAX / sizeof(XML_Char))
+ return false;
+
+ if (! poolGrowUntil(pool, len))
+ return false;
+
+ memcpy(pool->ptr, s, len * sizeof(XML_Char));
+ pool->ptr += len;
+
+ return true;
+}
#if ! defined(XML_TESTING)
const
@@ -730,11 +766,9 @@ struct XML_ParserStruct {
const char *m_eventEndPtr;
const char *m_positionPtr;
OPEN_INTERNAL_ENTITY *m_openInternalEntities;
- OPEN_INTERNAL_ENTITY *m_freeInternalEntities;
OPEN_INTERNAL_ENTITY *m_openAttributeEntities;
- OPEN_INTERNAL_ENTITY *m_freeAttributeEntities;
OPEN_INTERNAL_ENTITY *m_openValueEntities;
- OPEN_INTERNAL_ENTITY *m_freeValueEntities;
+ OPEN_INTERNAL_ENTITY *m_freeEntities;
XML_Bool m_defaultExpandInternalEntities;
int m_tagLevel;
ENTITY *m_declEntity;
@@ -754,7 +788,7 @@ struct XML_ParserStruct {
TAG *m_freeTagList;
BINDING *m_inheritedBindings;
BINDING *m_freeBindingList;
- int m_attsSize;
+ size_t m_attsSize;
int m_nSpecifiedAtts;
int m_idAttIndex;
ATTRIBUTE *m_atts;
@@ -768,7 +802,7 @@ struct XML_ParserStruct {
STRING_POOL m_tempPool;
STRING_POOL m_temp2Pool;
char *m_groupConnector;
- unsigned int m_groupSize;
+ size_t m_groupSize;
XML_Char m_namespaceSeparator;
XML_Parser m_parentParser;
XML_ParsingStatus m_parsingStatus;
@@ -785,6 +819,7 @@ struct XML_ParserStruct {
ENTITY_STATS m_entity_stats;
#endif
XML_Bool m_reenter;
+ unsigned m_handlerCallDepth;
};
#if XML_GE == 1
@@ -1128,6 +1163,23 @@ generate_hash_secret_salt(void) {
#endif
}
+static void
+beforeHandler(XML_Parser parser) {
+ assert(parser->m_handlerCallDepth < UINT_MAX);
+ parser->m_handlerCallDepth++;
+}
+
+static void
+afterHandler(XML_Parser parser) {
+ assert(parser->m_handlerCallDepth > 0);
+ parser->m_handlerCallDepth--;
+}
+
+static bool
+isCalledFromInsideHandler(XML_Parser parser) {
+ return parser->m_handlerCallDepth > 0;
+}
+
static enum XML_Error
callProcessor(XML_Parser parser, const char *start, const char *end,
const char **endPtr) {
@@ -1364,9 +1416,7 @@ parserCreate(const XML_Char *encodingName,
parser->m_freeBindingList = NULL;
parser->m_freeTagList = NULL;
- parser->m_freeInternalEntities = NULL;
- parser->m_freeAttributeEntities = NULL;
- parser->m_freeValueEntities = NULL;
+ parser->m_freeEntities = NULL;
parser->m_groupSize = 0;
parser->m_groupConnector = NULL;
@@ -1482,6 +1532,7 @@ parserInit(XML_Parser parser, const XML_Char *encodingName) {
parser->m_parsingStatus.parsing = XML_INITIALIZED;
// Reentry can only be triggered inside m_processor calls
parser->m_reenter = XML_FALSE;
+ parser->m_handlerCallDepth = 0;
#ifdef XML_DTD
parser->m_isParamEntity = XML_FALSE;
parser->m_useForeignDTD = XML_FALSE;
@@ -1515,12 +1566,22 @@ moveToFreeBindingList(XML_Parser parser, BINDING *bindings) {
}
}
+/* Moves a list of entities onto the start of another list. */
+static void
+moveEntityList(OPEN_INTERNAL_ENTITY **dst, OPEN_INTERNAL_ENTITY **src) {
+ for (OPEN_INTERNAL_ENTITY *head = *src; head != NULL;) {
+ OPEN_INTERNAL_ENTITY *const openEntity = head;
+ head = head->next;
+ openEntity->next = *dst;
+ *dst = openEntity;
+ }
+}
+
XML_Bool XMLCALL
XML_ParserReset(XML_Parser parser, const XML_Char *encodingName) {
TAG *tStk;
- OPEN_INTERNAL_ENTITY *openEntityList;
- if (parser == NULL)
+ if ((parser == NULL) || isCalledFromInsideHandler(parser))
return XML_FALSE;
if (parser->m_parentParser)
@@ -1535,32 +1596,14 @@ XML_ParserReset(XML_Parser parser, const XML_Char *encodingName) {
tag->bindings = NULL;
parser->m_freeTagList = tag;
}
- /* move m_openInternalEntities to m_freeInternalEntities */
- openEntityList = parser->m_openInternalEntities;
- while (openEntityList) {
- OPEN_INTERNAL_ENTITY *openEntity = openEntityList;
- openEntityList = openEntity->next;
- openEntity->next = parser->m_freeInternalEntities;
- parser->m_freeInternalEntities = openEntity;
- }
- /* move m_openAttributeEntities to m_freeAttributeEntities (i.e. same task but
- * for attributes) */
- openEntityList = parser->m_openAttributeEntities;
- while (openEntityList) {
- OPEN_INTERNAL_ENTITY *openEntity = openEntityList;
- openEntityList = openEntity->next;
- openEntity->next = parser->m_freeAttributeEntities;
- parser->m_freeAttributeEntities = openEntity;
- }
- /* move m_openValueEntities to m_freeValueEntities (i.e. same task but
- * for value entities) */
- openEntityList = parser->m_openValueEntities;
- while (openEntityList) {
- OPEN_INTERNAL_ENTITY *openEntity = openEntityList;
- openEntityList = openEntity->next;
- openEntity->next = parser->m_freeValueEntities;
- parser->m_freeValueEntities = openEntity;
- }
+ /* move m_openInternalEntities to m_freeEntities */
+ moveEntityList(&parser->m_freeEntities, &parser->m_openInternalEntities);
+ /* move m_openAttributeEntities to m_freeEntities (i.e. same task but for
+ * attributes) */
+ moveEntityList(&parser->m_freeEntities, &parser->m_openAttributeEntities);
+ /* move m_openValueEntities to m_freeEntities (i.e. same task but for value
+ * entities) */
+ moveEntityList(&parser->m_freeEntities, &parser->m_openValueEntities);
moveToFreeBindingList(parser, parser->m_inheritedBindings);
FREE(parser, parser->m_unknownEncodingMem);
if (parser->m_unknownEncodingRelease)
@@ -1712,11 +1755,6 @@ XML_ExternalEntityParserCreate(XML_Parser oldParser, const XML_Char *context,
newDtd = oldDtd;
#endif /* XML_DTD */
- /* Note that the magical uses of the pre-processor to make field
- access look more like C++ require that `parser' be overwritten
- here. This makes this function more painful to follow than it
- would be otherwise.
- */
if (parser->m_ns) {
XML_Char tmp[2] = {parser->m_namespaceSeparator, 0};
parser = parserCreate(encodingName, &parser->m_mem, tmp, newDtd, oldParser);
@@ -1806,8 +1844,7 @@ destroyBindings(BINDING *bindings, XML_Parser parser) {
void XMLCALL
XML_ParserFree(XML_Parser parser) {
TAG *tagList;
- OPEN_INTERNAL_ENTITY *entityList;
- if (parser == NULL)
+ if ((parser == NULL) || isCalledFromInsideHandler(parser))
return;
/* free m_tagStack and m_freeTagList */
tagList = parser->m_tagStack;
@@ -1825,48 +1862,35 @@ XML_ParserFree(XML_Parser parser) {
destroyBindings(p->bindings, parser);
FREE(parser, p);
}
- /* free m_openInternalEntities and m_freeInternalEntities */
- entityList = parser->m_openInternalEntities;
- for (;;) {
- OPEN_INTERNAL_ENTITY *openEntity;
- if (entityList == NULL) {
- if (parser->m_freeInternalEntities == NULL)
- break;
- entityList = parser->m_freeInternalEntities;
- parser->m_freeInternalEntities = NULL;
- }
- openEntity = entityList;
+ /* free m_openInternalEntities */
+ for (OPEN_INTERNAL_ENTITY *entityList = parser->m_openInternalEntities;
+ entityList != NULL;) {
+ OPEN_INTERNAL_ENTITY *const openEntity = entityList;
entityList = entityList->next;
FREE(parser, openEntity);
}
- /* free m_openAttributeEntities and m_freeAttributeEntities */
- entityList = parser->m_openAttributeEntities;
- for (;;) {
- OPEN_INTERNAL_ENTITY *openEntity;
- if (entityList == NULL) {
- if (parser->m_freeAttributeEntities == NULL)
- break;
- entityList = parser->m_freeAttributeEntities;
- parser->m_freeAttributeEntities = NULL;
- }
- openEntity = entityList;
+ /* free m_openAttributeEntities */
+ for (OPEN_INTERNAL_ENTITY *entityList = parser->m_openAttributeEntities;
+ entityList != NULL;) {
+ OPEN_INTERNAL_ENTITY *const openEntity = entityList;
entityList = entityList->next;
FREE(parser, openEntity);
}
- /* free m_openValueEntities and m_freeValueEntities */
- entityList = parser->m_openValueEntities;
- for (;;) {
- OPEN_INTERNAL_ENTITY *openEntity;
- if (entityList == NULL) {
- if (parser->m_freeValueEntities == NULL)
- break;
- entityList = parser->m_freeValueEntities;
- parser->m_freeValueEntities = NULL;
- }
- openEntity = entityList;
+ /* free m_openValueEntities */
+ for (OPEN_INTERNAL_ENTITY *entityList = parser->m_openValueEntities;
+ entityList != NULL;) {
+ OPEN_INTERNAL_ENTITY *const openEntity = entityList;
entityList = entityList->next;
FREE(parser, openEntity);
}
+ /* free m_freeEntities */
+ for (OPEN_INTERNAL_ENTITY *entityList = parser->m_freeEntities;
+ entityList != NULL;) {
+ OPEN_INTERNAL_ENTITY *const openEntity = entityList;
+ entityList = entityList->next;
+ FREE(parser, openEntity);
+ }
+ parser->m_freeEntities = NULL;
destroyBindings(parser->m_freeBindingList, parser);
destroyBindings(parser->m_inheritedBindings, parser);
poolDestroy(&parser->m_tempPool);
@@ -2263,6 +2287,8 @@ XML_Parse(XML_Parser parser, const char *s, int len, int isFinal) {
parser->m_errorCode = XML_ERROR_INVALID_ARGUMENT;
return XML_STATUS_ERROR;
}
+ if (isCalledFromInsideHandler(parser))
+ return XML_STATUS_ERROR;
switch (parser->m_parsingStatus.parsing) {
case XML_SUSPENDED:
parser->m_errorCode = XML_ERROR_SUSPENDED;
@@ -2275,7 +2301,7 @@ XML_Parse(XML_Parser parser, const char *s, int len, int isFinal) {
parser->m_errorCode = XML_ERROR_NO_MEMORY;
return XML_STATUS_ERROR;
}
- /* fall through */
+ EXPAT_FALLTHROUGH;
default:
parser->m_parsingStatus.parsing = XML_PARSING;
}
@@ -2286,7 +2312,7 @@ XML_Parse(XML_Parser parser, const char *s, int len, int isFinal) {
int nLeftOver;
enum XML_Status result;
/* Detect overflow (a+b > MAX <==> b > MAX-a) */
- if ((XML_Size)len > ((XML_Size)-1) / 2 - parser->m_parseEndByteIndex) {
+ if (len > XML_INDEX_MAX - parser->m_parseEndByteIndex) {
parser->m_errorCode = XML_ERROR_NO_MEMORY;
parser->m_eventPtr = parser->m_eventEndPtr = NULL;
parser->m_processor = errorProcessor;
@@ -2317,7 +2343,7 @@ XML_Parse(XML_Parser parser, const char *s, int len, int isFinal) {
parser->m_parsingStatus.parsing = XML_FINISHED;
return XML_STATUS_OK;
}
- /* fall through */
+ EXPAT_FALLTHROUGH;
default:
result = XML_STATUS_OK;
}
@@ -2372,7 +2398,7 @@ XML_ParseBuffer(XML_Parser parser, int len, int isFinal) {
const char *start;
enum XML_Status result = XML_STATUS_OK;
- if (parser == NULL)
+ if ((parser == NULL) || isCalledFromInsideHandler(parser))
return XML_STATUS_ERROR;
if (len < 0) {
@@ -2398,11 +2424,19 @@ XML_ParseBuffer(XML_Parser parser, int len, int isFinal) {
parser->m_errorCode = XML_ERROR_NO_MEMORY;
return XML_STATUS_ERROR;
}
- /* fall through */
+ EXPAT_FALLTHROUGH;
default:
parser->m_parsingStatus.parsing = XML_PARSING;
}
+ // Detect and avoid integer overflow
+ if (len > XML_INDEX_MAX - parser->m_parseEndByteIndex) {
+ parser->m_errorCode = XML_ERROR_NO_MEMORY;
+ parser->m_eventPtr = parser->m_eventEndPtr = NULL;
+ parser->m_processor = errorProcessor;
+ return XML_STATUS_ERROR;
+ }
+
start = parser->m_bufferPtr;
parser->m_positionPtr = start;
parser->m_bufferEnd += len;
@@ -2428,6 +2462,7 @@ XML_ParseBuffer(XML_Parser parser, int len, int isFinal) {
parser->m_parsingStatus.parsing = XML_FINISHED;
return result;
}
+ break;
default:; /* should not happen */
}
}
@@ -2438,9 +2473,32 @@ XML_ParseBuffer(XML_Parser parser, int len, int isFinal) {
return result;
}
+/* Modifies `parser`’s buffer to be backed by `newBuf`. */
+static void
+setParserBuffer(XML_Parser parser, char *newBuf, int newBufSize, int keep) {
+ parser->m_bufferLim = newBuf + newBufSize;
+ if (parser->m_bufferPtr) {
+ const int parsing
+ = (int)EXPAT_SAFE_PTR_DIFF(parser->m_bufferEnd, parser->m_bufferPtr);
+ memcpy(newBuf, parser->m_bufferPtr - keep, parsing + keep);
+ // NOTE: We are avoiding FREE(..) here because parser->m_buffer
+ // is not being allocated with MALLOC(..) but with plain
+ // .malloc_fcn(..).
+ parser->m_mem.free_fcn(parser->m_buffer);
+ parser->m_buffer = newBuf;
+ parser->m_bufferEnd = newBuf + parsing + keep;
+ parser->m_bufferPtr = newBuf + keep;
+ } else {
+ /* This must be a brand new buffer with no data in it yet */
+ parser->m_buffer = newBuf;
+ parser->m_bufferEnd = newBuf;
+ parser->m_bufferPtr = newBuf;
+ }
+}
+
void *XMLCALL
XML_GetBuffer(XML_Parser parser, int len) {
- if (parser == NULL)
+ if ((parser == NULL) || isCalledFromInsideHandler(parser))
return NULL;
if (len < 0) {
parser->m_errorCode = XML_ERROR_NO_MEMORY;
@@ -2461,9 +2519,6 @@ XML_GetBuffer(XML_Parser parser, int len) {
parser->m_lastBufferRequestSize = len;
if (len > EXPAT_SAFE_PTR_DIFF(parser->m_bufferLim, parser->m_bufferEnd)
|| parser->m_buffer == NULL) {
-#if XML_CONTEXT_BYTES > 0
- int keep;
-#endif /* XML_CONTEXT_BYTES > 0 */
/* Do not invoke signed arithmetic overflow: */
int neededSize = (int)((unsigned)len
+ (unsigned)EXPAT_SAFE_PTR_DIFF(
@@ -2473,7 +2528,9 @@ XML_GetBuffer(XML_Parser parser, int len) {
return NULL;
}
#if XML_CONTEXT_BYTES > 0
- keep = (int)EXPAT_SAFE_PTR_DIFF(parser->m_bufferPtr, parser->m_buffer);
+ const int parsed
+ = (int)EXPAT_SAFE_PTR_DIFF(parser->m_bufferPtr, parser->m_buffer);
+ int keep = parsed;
if (keep > XML_CONTEXT_BYTES)
keep = XML_CONTEXT_BYTES;
/* Detect and prevent integer overflow */
@@ -2481,16 +2538,16 @@ XML_GetBuffer(XML_Parser parser, int len) {
parser->m_errorCode = XML_ERROR_NO_MEMORY;
return NULL;
}
- neededSize += keep;
+#else
+ int keep = 0;
#endif /* XML_CONTEXT_BYTES > 0 */
+ neededSize += keep;
if (parser->m_buffer && parser->m_bufferPtr
&& neededSize
<= EXPAT_SAFE_PTR_DIFF(parser->m_bufferLim, parser->m_buffer)) {
#if XML_CONTEXT_BYTES > 0
- if (keep < EXPAT_SAFE_PTR_DIFF(parser->m_bufferPtr, parser->m_buffer)) {
- int offset
- = (int)EXPAT_SAFE_PTR_DIFF(parser->m_bufferPtr, parser->m_buffer)
- - keep;
+ if (keep < parsed) {
+ int offset = parsed - keep;
/* The buffer pointers cannot be NULL here; we have at least some bytes
* in the buffer */
memmove(parser->m_buffer, &parser->m_buffer[offset],
@@ -2507,7 +2564,6 @@ XML_GetBuffer(XML_Parser parser, int len) {
parser->m_bufferPtr = parser->m_buffer;
#endif /* XML_CONTEXT_BYTES > 0 */
} else {
- char *newBuf;
int bufferSize
= (int)EXPAT_SAFE_PTR_DIFF(parser->m_bufferLim, parser->m_buffer);
if (bufferSize == 0)
@@ -2522,49 +2578,12 @@ XML_GetBuffer(XML_Parser parser, int len) {
}
// NOTE: We are avoiding MALLOC(..) here to leave limiting
// the input size to the application using Expat.
- newBuf = parser->m_mem.malloc_fcn(bufferSize);
+ char *const newBuf = parser->m_mem.malloc_fcn(bufferSize);
if (newBuf == NULL) {
parser->m_errorCode = XML_ERROR_NO_MEMORY;
return NULL;
}
- parser->m_bufferLim = newBuf + bufferSize;
-#if XML_CONTEXT_BYTES > 0
- if (parser->m_bufferPtr) {
- memcpy(newBuf, &parser->m_bufferPtr[-keep],
- EXPAT_SAFE_PTR_DIFF(parser->m_bufferEnd, parser->m_bufferPtr)
- + keep);
- // NOTE: We are avoiding FREE(..) here because parser->m_buffer
- // is not being allocated with MALLOC(..) but with plain
- // .malloc_fcn(..).
- parser->m_mem.free_fcn(parser->m_buffer);
- parser->m_buffer = newBuf;
- parser->m_bufferEnd
- = parser->m_buffer
- + EXPAT_SAFE_PTR_DIFF(parser->m_bufferEnd, parser->m_bufferPtr)
- + keep;
- parser->m_bufferPtr = parser->m_buffer + keep;
- } else {
- /* This must be a brand new buffer with no data in it yet */
- parser->m_bufferEnd = newBuf;
- parser->m_bufferPtr = parser->m_buffer = newBuf;
- }
-#else
- if (parser->m_bufferPtr) {
- memcpy(newBuf, parser->m_bufferPtr,
- EXPAT_SAFE_PTR_DIFF(parser->m_bufferEnd, parser->m_bufferPtr));
- // NOTE: We are avoiding FREE(..) here because parser->m_buffer
- // is not being allocated with MALLOC(..) but with plain
- // .malloc_fcn(..).
- parser->m_mem.free_fcn(parser->m_buffer);
- parser->m_bufferEnd
- = newBuf
- + EXPAT_SAFE_PTR_DIFF(parser->m_bufferEnd, parser->m_bufferPtr);
- } else {
- /* This must be a brand new buffer with no data in it yet */
- parser->m_bufferEnd = newBuf;
- }
- parser->m_bufferPtr = parser->m_buffer = newBuf;
-#endif /* XML_CONTEXT_BYTES > 0 */
+ setParserBuffer(parser, newBuf, bufferSize, keep);
}
parser->m_eventPtr = parser->m_eventEndPtr = NULL;
parser->m_positionPtr = NULL;
@@ -2617,7 +2636,7 @@ enum XML_Status XMLCALL
XML_ResumeParser(XML_Parser parser) {
enum XML_Status result = XML_STATUS_OK;
- if (parser == NULL)
+ if ((parser == NULL) || isCalledFromInsideHandler(parser))
return XML_STATUS_ERROR;
if (parser->m_parsingStatus.parsing != XML_SUSPENDED) {
parser->m_errorCode = XML_ERROR_NOT_SUSPENDED;
@@ -2643,6 +2662,7 @@ XML_ResumeParser(XML_Parser parser) {
parser->m_parsingStatus.parsing = XML_FINISHED;
return result;
}
+ break;
default:;
}
}
@@ -2704,7 +2724,7 @@ XML_GetInputContext(XML_Parser parser, int *offset, int *size) {
(void)offset;
(void)size;
#endif /* XML_CONTEXT_BYTES > 0 */
- return (const char *)0;
+ return NULL;
}
XML_Size XMLCALL
@@ -3063,7 +3083,7 @@ storeRawNames(XML_Parser parser) {
*/
rawNameLen = ROUND_UP(tag->rawNameLength, sizeof(XML_Char));
/* Detect and prevent integer overflow. */
- if (rawNameLen > (size_t)INT_MAX - nameLen)
+ if (rawNameLen > SIZE_MAX - nameLen)
return XML_FALSE;
bufSize = nameLen + rawNameLen;
if (bufSize > (size_t)(tag->bufEnd - tag->buf.raw)) {
@@ -3190,7 +3210,7 @@ externalEntityInitProcessor3(XML_Parser parser, const char *start,
if (parser->m_reenter) {
return XML_ERROR_UNEXPECTED_STATE; // LCOV_EXCL_LINE
}
- /* Fall through */
+ EXPAT_FALLTHROUGH;
default:
start = next;
}
@@ -3269,7 +3289,9 @@ doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc,
*eventEndPP = end;
if (parser->m_characterDataHandler) {
XML_Char c = 0xA;
+ beforeHandler(parser);
parser->m_characterDataHandler(parser->m_handlerArg, &c, 1);
+ afterHandler(parser);
} else if (parser->m_defaultHandler)
reportDefault(parser, enc, s, end);
/* We are at the end of the final buffer, should we check for
@@ -3322,9 +3344,11 @@ doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc,
((char *)&ch) + sizeof(XML_Char), __LINE__,
XML_ACCOUNT_ENTITY_EXPANSION);
#endif /* XML_GE == 1 */
- if (parser->m_characterDataHandler)
+ if (parser->m_characterDataHandler) {
+ beforeHandler(parser);
parser->m_characterDataHandler(parser->m_handlerArg, &ch, 1);
- else if (parser->m_defaultHandler)
+ afterHandler(parser);
+ } else if (parser->m_defaultHandler)
reportDefault(parser, enc, s, next);
break;
}
@@ -3344,9 +3368,11 @@ doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc,
else if (! entity->is_internal)
return XML_ERROR_ENTITY_DECLARED_IN_PE;
} else if (! entity) {
- if (parser->m_skippedEntityHandler)
+ if (parser->m_skippedEntityHandler) {
+ beforeHandler(parser);
parser->m_skippedEntityHandler(parser->m_handlerArg, name, 0);
- else if (parser->m_defaultHandler)
+ afterHandler(parser);
+ } else if (parser->m_defaultHandler)
reportDefault(parser, enc, s, next);
break;
}
@@ -3357,10 +3383,12 @@ doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc,
if (entity->textPtr) {
enum XML_Error result;
if (! parser->m_defaultExpandInternalEntities) {
- if (parser->m_skippedEntityHandler)
+ if (parser->m_skippedEntityHandler) {
+ beforeHandler(parser);
parser->m_skippedEntityHandler(parser->m_handlerArg, entity->name,
0);
- else if (parser->m_defaultHandler)
+ afterHandler(parser);
+ } else if (parser->m_defaultHandler)
reportDefault(parser, enc, s, next);
break;
}
@@ -3374,9 +3402,12 @@ doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc,
entity->open = XML_FALSE;
if (! context)
return XML_ERROR_NO_MEMORY;
- if (! parser->m_externalEntityRefHandler(
- parser->m_externalEntityRefHandlerArg, context, entity->base,
- entity->systemId, entity->publicId))
+ beforeHandler(parser);
+ const int status = parser->m_externalEntityRefHandler(
+ parser->m_externalEntityRefHandlerArg, context, entity->base,
+ entity->systemId, entity->publicId);
+ afterHandler(parser);
+ if (! status)
return XML_ERROR_EXTERNAL_ENTITY_HANDLING;
poolDiscard(&parser->m_tempPool);
} else if (parser->m_defaultHandler)
@@ -3384,7 +3415,6 @@ doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc,
break;
}
case XML_TOK_START_TAG_NO_ATTS:
- /* fall through */
case XML_TOK_START_TAG_WITH_ATTS: {
TAG *tag;
enum XML_Error result;
@@ -3416,11 +3446,10 @@ doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc,
const char *fromPtr = tag->rawName;
toPtr = tag->buf.str;
for (;;) {
- int convLen;
const enum XML_Convert_Result convert_res
= XmlConvert(enc, &fromPtr, rawNameEnd, (ICHAR **)&toPtr,
(ICHAR *)tag->bufEnd - 1);
- convLen = (int)(toPtr - tag->buf.str);
+ const size_t convLen = (size_t)(toPtr - tag->buf.str);
if ((fromPtr >= rawNameEnd)
|| (convert_res == XML_CONVERT_INPUT_INCOMPLETE)) {
tag->name.strLen = convLen;
@@ -3445,16 +3474,17 @@ doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc,
= storeAtts(parser, enc, s, &(tag->name), &(tag->bindings), account);
if (result)
return result;
- if (parser->m_startElementHandler)
+ if (parser->m_startElementHandler) {
+ beforeHandler(parser);
parser->m_startElementHandler(parser->m_handlerArg, tag->name.str,
(const XML_Char **)parser->m_atts);
- else if (parser->m_defaultHandler)
+ afterHandler(parser);
+ } else if (parser->m_defaultHandler)
reportDefault(parser, enc, s, next);
poolClear(&parser->m_tempPool);
break;
}
case XML_TOK_EMPTY_ELEMENT_NO_ATTS:
- /* fall through */
case XML_TOK_EMPTY_ELEMENT_WITH_ATTS: {
const char *rawName = s + enc->minBytesPerChar;
enum XML_Error result;
@@ -3474,14 +3504,18 @@ doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc,
}
poolFinish(&parser->m_tempPool);
if (parser->m_startElementHandler) {
+ beforeHandler(parser);
parser->m_startElementHandler(parser->m_handlerArg, name.str,
(const XML_Char **)parser->m_atts);
+ afterHandler(parser);
noElmHandlers = XML_FALSE;
}
if (parser->m_endElementHandler) {
if (parser->m_startElementHandler)
*eventPP = *eventEndPP;
+ beforeHandler(parser);
parser->m_endElementHandler(parser->m_handlerArg, name.str);
+ afterHandler(parser);
noElmHandlers = XML_FALSE;
}
if (noElmHandlers && parser->m_defaultHandler)
@@ -3539,14 +3573,19 @@ doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc,
}
*uri = XML_T('\0');
}
+ beforeHandler(parser);
parser->m_endElementHandler(parser->m_handlerArg, tag->name.str);
+ afterHandler(parser);
} else if (parser->m_defaultHandler)
reportDefault(parser, enc, s, next);
while (tag->bindings) {
BINDING *b = tag->bindings;
- if (parser->m_endNamespaceDeclHandler)
+ if (parser->m_endNamespaceDeclHandler) {
+ beforeHandler(parser);
parser->m_endNamespaceDeclHandler(parser->m_handlerArg,
b->prefix->name);
+ afterHandler(parser);
+ }
tag->bindings = tag->bindings->nextTagBinding;
b->nextTagBinding = parser->m_freeBindingList;
parser->m_freeBindingList = b;
@@ -3569,8 +3608,10 @@ doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc,
return XML_ERROR_BAD_CHAR_REF;
if (parser->m_characterDataHandler) {
XML_Char buf[XML_ENCODE_MAX];
+ beforeHandler(parser);
parser->m_characterDataHandler(parser->m_handlerArg, buf,
XmlEncode(n, (ICHAR *)buf));
+ afterHandler(parser);
} else if (parser->m_defaultHandler)
reportDefault(parser, enc, s, next);
} break;
@@ -3579,32 +3620,38 @@ doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc,
case XML_TOK_DATA_NEWLINE:
if (parser->m_characterDataHandler) {
XML_Char c = 0xA;
+ beforeHandler(parser);
parser->m_characterDataHandler(parser->m_handlerArg, &c, 1);
+ afterHandler(parser);
} else if (parser->m_defaultHandler)
reportDefault(parser, enc, s, next);
break;
case XML_TOK_CDATA_SECT_OPEN: {
enum XML_Error result;
- if (parser->m_startCdataSectionHandler)
+ if (parser->m_startCdataSectionHandler) {
+ beforeHandler(parser);
parser->m_startCdataSectionHandler(parser->m_handlerArg);
- /* BEGIN disabled code */
- /* Suppose you doing a transformation on a document that involves
- changing only the character data. You set up a defaultHandler
- and a characterDataHandler. The defaultHandler simply copies
- characters through. The characterDataHandler does the
- transformation and writes the characters out escaping them as
- necessary. This case will fail to work if we leave out the
- following two lines (because & and < inside CDATA sections will
- be incorrectly escaped).
+ afterHandler(parser);
+ /* BEGIN disabled code */
+ /* Suppose you doing a transformation on a document that involves
+ changing only the character data. You set up a defaultHandler
+ and a characterDataHandler. The defaultHandler simply copies
+ characters through. The characterDataHandler does the
+ transformation and writes the characters out escaping them as
+ necessary. This case will fail to work if we leave out the
+ following two lines (because & and < inside CDATA sections will
+ be incorrectly escaped).
- However, now we have a start/endCdataSectionHandler, so it seems
- easier to let the user deal with this.
- */
- else if ((0) && parser->m_characterDataHandler)
+ However, now we have a start/endCdataSectionHandler, so it seems
+ easier to let the user deal with this.
+ */
+ } else if ((0) && parser->m_characterDataHandler) {
+ beforeHandler(parser);
parser->m_characterDataHandler(parser->m_handlerArg, parser->m_dataBuf,
0);
- /* END disabled code */
- else if (parser->m_defaultHandler)
+ afterHandler(parser);
+ /* END disabled code */
+ } else if (parser->m_defaultHandler)
reportDefault(parser, enc, s, next);
result
= doCdataSection(parser, enc, &next, end, nextPtr, haveMore, account);
@@ -3624,13 +3671,18 @@ doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc,
if (MUST_CONVERT(enc, s)) {
ICHAR *dataPtr = (ICHAR *)parser->m_dataBuf;
XmlConvert(enc, &s, end, &dataPtr, (ICHAR *)parser->m_dataBufEnd);
+ beforeHandler(parser);
parser->m_characterDataHandler(
parser->m_handlerArg, parser->m_dataBuf,
(int)(dataPtr - (ICHAR *)parser->m_dataBuf));
- } else
+ afterHandler(parser);
+ } else {
+ beforeHandler(parser);
parser->m_characterDataHandler(
parser->m_handlerArg, (const XML_Char *)s,
(int)((const XML_Char *)end - (const XML_Char *)s));
+ afterHandler(parser);
+ }
} else if (parser->m_defaultHandler)
reportDefault(parser, enc, s, end);
/* We are at the end of the final buffer, should we check for
@@ -3655,16 +3707,21 @@ doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc,
const enum XML_Convert_Result convert_res = XmlConvert(
enc, &s, next, &dataPtr, (ICHAR *)parser->m_dataBufEnd);
*eventEndPP = s;
+ beforeHandler(parser);
charDataHandler(parser->m_handlerArg, parser->m_dataBuf,
(int)(dataPtr - (ICHAR *)parser->m_dataBuf));
+ afterHandler(parser);
if ((convert_res == XML_CONVERT_COMPLETED)
|| (convert_res == XML_CONVERT_INPUT_INCOMPLETE))
break;
*eventPP = s;
}
- } else
+ } else {
+ beforeHandler(parser);
charDataHandler(parser->m_handlerArg, (const XML_Char *)s,
(int)((const XML_Char *)next - (const XML_Char *)s));
+ afterHandler(parser);
+ }
} else if (parser->m_defaultHandler)
reportDefault(parser, enc, s, next);
} break;
@@ -3702,7 +3759,7 @@ doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc,
*nextPtr = next;
return XML_ERROR_NONE;
}
- /* Fall through */
+ EXPAT_FALLTHROUGH;
default:;
*eventPP = s = next;
}
@@ -3722,8 +3779,11 @@ freeBindings(XML_Parser parser, BINDING *bindings) {
/* m_startNamespaceDeclHandler will have been called for this
* binding in addBindings(), so call the end handler now.
*/
- if (parser->m_endNamespaceDeclHandler)
+ if (parser->m_endNamespaceDeclHandler) {
+ beforeHandler(parser);
parser->m_endNamespaceDeclHandler(parser->m_handlerArg, b->prefix->name);
+ afterHandler(parser);
+ }
bindings = bindings->nextTagBinding;
b->nextTagBinding = parser->m_freeBindingList;
@@ -3747,20 +3807,14 @@ storeAtts(XML_Parser parser, const ENCODING *enc, const char *attStr,
TAG_NAME *tagNamePtr, BINDING **bindingsPtr,
enum XML_Account account) {
DTD *const dtd = parser->m_dtd; /* save one level of indirection */
- ELEMENT_TYPE *elementType;
- int nDefaultAtts;
- const XML_Char **appAtts; /* the attribute list for the application */
int attIndex = 0;
- int prefixLen;
- int i;
- int n;
XML_Char *uri;
int nPrefixes = 0;
BINDING *binding;
const XML_Char *localPart;
/* lookup the element type name */
- elementType
+ ELEMENT_TYPE *elementType
= (ELEMENT_TYPE *)lookup(parser, &dtd->elementTypes, tagNamePtr->str, 0);
if (! elementType) {
const XML_Char *name = poolCopyString(&dtd->pool, tagNamePtr->str);
@@ -3775,75 +3829,71 @@ storeAtts(XML_Parser parser, const ENCODING *enc, const char *attStr,
if (parser->m_ns && ! setElementTypePrefix(parser, elementType))
return XML_ERROR_NO_MEMORY;
}
- nDefaultAtts = elementType->nDefaultAtts;
+ const size_t nDefaultAtts = elementType->nDefaultAtts;
+
+ /* Detect and prevent integer overflow. */
+ if (parser->m_attsSize > (size_t)INT_MAX)
+ return XML_ERROR_NO_MEMORY;
/* get the attributes from the tokenizer */
- n = XmlGetAttributes(enc, attStr, parser->m_attsSize, parser->m_atts);
+ size_t n = (size_t)XmlGetAttributes(enc, attStr, (int)parser->m_attsSize,
+ parser->m_atts);
/* Detect and prevent integer overflow */
- if (n > INT_MAX - nDefaultAtts) {
+ if (n > SIZE_MAX - nDefaultAtts) {
return XML_ERROR_NO_MEMORY;
}
if (n + nDefaultAtts > parser->m_attsSize) {
- int oldAttsSize = parser->m_attsSize;
- ATTRIBUTE *temp;
-#ifdef XML_ATTR_INFO
- XML_AttrInfo *temp2;
-#endif
+ size_t oldAttsSize = parser->m_attsSize;
/* Detect and prevent integer overflow */
- if ((nDefaultAtts > INT_MAX - INIT_ATTS_SIZE)
- || (n > INT_MAX - (nDefaultAtts + INIT_ATTS_SIZE))) {
+ if ((nDefaultAtts > SIZE_MAX - INIT_ATTS_SIZE)
+ || (n > SIZE_MAX - (nDefaultAtts + INIT_ATTS_SIZE))) {
return XML_ERROR_NO_MEMORY;
}
parser->m_attsSize = n + nDefaultAtts + INIT_ATTS_SIZE;
- /* Detect and prevent integer overflow.
- * The preprocessor guard addresses the "always false" warning
- * from -Wtype-limits on platforms where
- * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */
-#if UINT_MAX >= SIZE_MAX
- if ((unsigned)parser->m_attsSize > SIZE_MAX / sizeof(ATTRIBUTE)) {
+ /* Detect and prevent integer overflow. */
+ if (parser->m_attsSize > SIZE_MAX / sizeof(ATTRIBUTE)) {
parser->m_attsSize = oldAttsSize;
return XML_ERROR_NO_MEMORY;
}
-#endif
- temp = REALLOC(parser, parser->m_atts,
- parser->m_attsSize * sizeof(ATTRIBUTE));
+ ATTRIBUTE *const temp = REALLOC(parser, parser->m_atts,
+ parser->m_attsSize * sizeof(ATTRIBUTE));
if (temp == NULL) {
parser->m_attsSize = oldAttsSize;
return XML_ERROR_NO_MEMORY;
}
parser->m_atts = temp;
#ifdef XML_ATTR_INFO
- /* Detect and prevent integer overflow.
- * The preprocessor guard addresses the "always false" warning
- * from -Wtype-limits on platforms where
- * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */
-# if UINT_MAX >= SIZE_MAX
- if ((unsigned)parser->m_attsSize > SIZE_MAX / sizeof(XML_AttrInfo)) {
+ /* Detect and prevent integer overflow. */
+ if (parser->m_attsSize > SIZE_MAX / sizeof(XML_AttrInfo)) {
parser->m_attsSize = oldAttsSize;
return XML_ERROR_NO_MEMORY;
}
-# endif
- temp2 = REALLOC(parser, parser->m_attInfo,
- parser->m_attsSize * sizeof(XML_AttrInfo));
+ XML_AttrInfo *const temp2 = REALLOC(
+ parser, parser->m_attInfo, parser->m_attsSize * sizeof(XML_AttrInfo));
if (temp2 == NULL) {
parser->m_attsSize = oldAttsSize;
return XML_ERROR_NO_MEMORY;
}
parser->m_attInfo = temp2;
#endif
- if (n > oldAttsSize)
- XmlGetAttributes(enc, attStr, n, parser->m_atts);
+ if (n > oldAttsSize) {
+ /* Detect and prevent integer overflow. */
+ if (n > (size_t)INT_MAX)
+ return XML_ERROR_NO_MEMORY;
+ XmlGetAttributes(enc, attStr, (int)n, parser->m_atts);
+ }
}
- appAtts = (const XML_Char **)parser->m_atts;
- for (i = 0; i < n; i++) {
+ /* the attribute list for the application */
+ const XML_Char **const appAtts = (const XML_Char **)parser->m_atts;
+ for (size_t i = 0; i < n; i++) {
ATTRIBUTE *currAtt = &parser->m_atts[i];
#ifdef XML_ATTR_INFO
XML_AttrInfo *currAttInfo = &parser->m_attInfo[i];
@@ -3876,13 +3926,11 @@ storeAtts(XML_Parser parser, const ENCODING *enc, const char *attStr,
(attId->name)[-1] = 1;
appAtts[attIndex++] = attId->name;
if (! parser->m_atts[i].normalized) {
- enum XML_Error result;
XML_Bool isCdata = XML_TRUE;
/* figure out whether declared as other than CDATA */
if (attId->maybeTokenized) {
- int j;
- for (j = 0; j < nDefaultAtts; j++) {
+ for (size_t j = 0; j < nDefaultAtts; j++) {
if (attId == elementType->defaultAtts[j].id) {
isCdata = elementType->defaultAtts[j].isCdata;
break;
@@ -3891,7 +3939,7 @@ storeAtts(XML_Parser parser, const ENCODING *enc, const char *attStr,
}
/* normalize the attribute value */
- result = storeAttributeValue(
+ const enum XML_Error result = storeAttributeValue(
parser, enc, isCdata, parser->m_atts[i].valuePtr,
parser->m_atts[i].valueEnd, &parser->m_tempPool, account);
if (result)
@@ -3929,7 +3977,7 @@ storeAtts(XML_Parser parser, const ENCODING *enc, const char *attStr,
/* set-up for XML_GetSpecifiedAttributeCount and XML_GetIdAttributeIndex */
parser->m_nSpecifiedAtts = attIndex;
if (elementType->idAtt && (elementType->idAtt->name)[-1]) {
- for (i = 0; i < attIndex; i += 2)
+ for (int i = 0; i < attIndex; i += 2)
if (appAtts[i] == elementType->idAtt->name) {
parser->m_idAttIndex = i;
break;
@@ -3938,7 +3986,7 @@ storeAtts(XML_Parser parser, const ENCODING *enc, const char *attStr,
parser->m_idAttIndex = -1;
/* do attribute defaulting */
- for (i = 0; i < nDefaultAtts; i++) {
+ for (size_t i = 0; i < nDefaultAtts; i++) {
const DEFAULT_ATTRIBUTE *da = elementType->defaultAtts + i;
if (! (da->id->name)[-1] && da->value) {
if (da->id->prefix) {
@@ -3964,7 +4012,7 @@ storeAtts(XML_Parser parser, const ENCODING *enc, const char *attStr,
/* expand prefixed attribute names, check for duplicates,
and clear flags that say whether attributes were specified */
- i = 0;
+ int i = 0;
if (nPrefixes) {
unsigned int j; /* hash table index */
unsigned long version = parser->m_nsAttsVersion;
@@ -3979,7 +4027,6 @@ storeAtts(XML_Parser parser, const ENCODING *enc, const char *attStr,
/* size of hash table must be at least 2 * (# of prefixed attributes) */
if ((nPrefixes << 1)
>> parser->m_nsAttsPower) { /* true for m_nsAttsPower = 0 */
- NS_ATT *temp;
/* hash table size must also be a power of 2 and >= 8 */
while (nPrefixes >> parser->m_nsAttsPower++)
;
@@ -4007,7 +4054,8 @@ storeAtts(XML_Parser parser, const ENCODING *enc, const char *attStr,
}
#endif
- temp = REALLOC(parser, parser->m_nsAtts, nsAttsSize * sizeof(NS_ATT));
+ NS_ATT *const temp
+ = REALLOC(parser, parser->m_nsAtts, nsAttsSize * sizeof(NS_ATT));
if (! temp) {
/* Restore actual size of memory in m_nsAtts */
parser->m_nsAttsPower = oldNsAttsPower;
@@ -4028,9 +4076,6 @@ storeAtts(XML_Parser parser, const ENCODING *enc, const char *attStr,
for (; i < attIndex; i += 2) {
const XML_Char *s = appAtts[i];
if (s[-1] == 2) { /* prefixed */
- ATTRIBUTE_ID *id;
- const BINDING *b;
- unsigned long uriHash;
struct siphash sip_state;
struct sipkey sip_key;
@@ -4038,7 +4083,8 @@ storeAtts(XML_Parser parser, const ENCODING *enc, const char *attStr,
sip24_init(&sip_state, &sip_key);
((XML_Char *)s)[-1] = 0; /* clear flag */
- id = (ATTRIBUTE_ID *)lookup(parser, &dtd->attributeIds, s, 0);
+ ATTRIBUTE_ID *const id
+ = (ATTRIBUTE_ID *)lookup(parser, &dtd->attributeIds, s, 0);
if (! id || ! id->prefix) {
/* This code is walking through the appAtts array, dealing
* with (in this case) a prefixed attribute name. To be in
@@ -4046,7 +4092,7 @@ storeAtts(XML_Parser parser, const ENCODING *enc, const char *attStr,
* has to have passed through the hash table lookup once
* already. That implies that an entry for it already
* exists, so the lookup above will return a pointer to
- * already allocated memory. There is no opportunaity for
+ * already allocated memory. There is no opportunity for
* the allocator to fail, so the condition above cannot be
* fulfilled.
*
@@ -4056,15 +4102,12 @@ storeAtts(XML_Parser parser, const ENCODING *enc, const char *attStr,
*/
return XML_ERROR_NO_MEMORY; /* LCOV_EXCL_LINE */
}
- b = id->prefix->binding;
+ const BINDING *const b = id->prefix->binding;
if (! b)
return XML_ERROR_UNBOUND_PREFIX;
- for (j = 0; j < (unsigned int)b->uriLen; j++) {
- const XML_Char c = b->uri[j];
- if (! poolAppendChar(&parser->m_tempPool, c))
- return XML_ERROR_NO_MEMORY;
- }
+ if (! poolAppendChars(&parser->m_tempPool, b->uri, b->uriLen))
+ return XML_ERROR_NO_MEMORY;
sip24_update(&sip_state, b->uri, b->uriLen * sizeof(XML_Char));
@@ -4073,12 +4116,13 @@ storeAtts(XML_Parser parser, const ENCODING *enc, const char *attStr,
sip24_update(&sip_state, s, keylen(s) * sizeof(XML_Char));
- do { /* copies null terminator */
- if (! poolAppendChar(&parser->m_tempPool, *s))
+ {
+ const size_t len = xcslen(s) + /*null terminator*/ 1;
+ if (! poolAppendChars(&parser->m_tempPool, s, len))
return XML_ERROR_NO_MEMORY;
- } while (*s++);
+ }
- uriHash = (unsigned long)sip24_final(&sip_state);
+ const unsigned long uriHash = (unsigned long)sip24_final(&sip_state);
{ /* Check hash table for duplicate of expanded name (uriName).
Derived from code in lookup(parser, HASH_TABLE *table, ...).
@@ -4106,10 +4150,9 @@ storeAtts(XML_Parser parser, const ENCODING *enc, const char *attStr,
if (parser->m_ns_triplets) { /* append namespace separator and prefix */
parser->m_tempPool.ptr[-1] = parser->m_namespaceSeparator;
s = b->prefix->name;
- do {
- if (! poolAppendChar(&parser->m_tempPool, *s))
- return XML_ERROR_NO_MEMORY;
- } while (*s++);
+ const size_t len = xcslen(s) + /*null terminator*/ 1;
+ if (! poolAppendChars(&parser->m_tempPool, s, len))
+ return XML_ERROR_NO_MEMORY;
}
/* store expanded name in attribute list */
@@ -4152,48 +4195,36 @@ storeAtts(XML_Parser parser, const ENCODING *enc, const char *attStr,
localPart = tagNamePtr->str;
} else
return XML_ERROR_NONE;
- prefixLen = 0;
- if (parser->m_ns_triplets && binding->prefix->name) {
- while (binding->prefix->name[prefixLen++])
- ; /* prefixLen includes null terminator */
- }
+ size_t prefixLen = 0;
+ if (parser->m_ns_triplets && binding->prefix->name)
+ prefixLen = xcslen(binding->prefix->name) + /*null terminator*/ 1;
tagNamePtr->localPart = localPart;
tagNamePtr->uriLen = binding->uriLen;
tagNamePtr->prefix = binding->prefix->name;
tagNamePtr->prefixLen = prefixLen;
- for (i = 0; localPart[i++];)
- ; /* i includes null terminator */
+
+ const size_t localPartLen = xcslen(localPart) + /*null terminator*/ 1;
/* Detect and prevent integer overflow */
- if (binding->uriLen > INT_MAX - prefixLen
- || i > INT_MAX - (binding->uriLen + prefixLen)) {
+ if (binding->uriLen > SIZE_MAX - prefixLen
+ || localPartLen > SIZE_MAX - (binding->uriLen + prefixLen)) {
return XML_ERROR_NO_MEMORY;
}
- n = i + binding->uriLen + prefixLen;
- if (n > binding->uriAlloc) {
- TAG *p;
-
+ const size_t totalLen = localPartLen + binding->uriLen + prefixLen;
+ if (totalLen > binding->uriAlloc) {
/* Detect and prevent integer overflow */
- if (n > INT_MAX - EXPAND_SPARE) {
- return XML_ERROR_NO_MEMORY;
- }
- /* Detect and prevent integer overflow.
- * The preprocessor guard addresses the "always false" warning
- * from -Wtype-limits on platforms where
- * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */
-#if UINT_MAX >= SIZE_MAX
- if ((unsigned)(n + EXPAND_SPARE) > SIZE_MAX / sizeof(XML_Char)) {
+ if (totalLen > SIZE_MAX - EXPAND_SPARE
+ || totalLen + EXPAND_SPARE > SIZE_MAX / sizeof(XML_Char)) {
return XML_ERROR_NO_MEMORY;
}
-#endif
- uri = MALLOC(parser, (n + EXPAND_SPARE) * sizeof(XML_Char));
+ uri = MALLOC(parser, (totalLen + EXPAND_SPARE) * sizeof(XML_Char));
if (! uri)
return XML_ERROR_NO_MEMORY;
- binding->uriAlloc = n + EXPAND_SPARE;
+ binding->uriAlloc = totalLen + EXPAND_SPARE;
memcpy(uri, binding->uri, binding->uriLen * sizeof(XML_Char));
- for (p = parser->m_tagStack; p; p = p->parent)
+ for (TAG *p = parser->m_tagStack; p; p = p->parent)
if (p->name.str == binding->uri)
p->name.str = uri;
FREE(parser, binding->uri);
@@ -4201,10 +4232,14 @@ storeAtts(XML_Parser parser, const ENCODING *enc, const char *attStr,
}
/* if m_namespaceSeparator != '\0' then uri includes it already */
uri = binding->uri + binding->uriLen;
- memcpy(uri, localPart, i * sizeof(XML_Char));
+ /* Detect and prevent integer overflow */
+ if (localPartLen > SIZE_MAX / sizeof(XML_Char)) {
+ return XML_ERROR_NO_MEMORY;
+ }
+ memcpy(uri, localPart, localPartLen * sizeof(XML_Char));
/* we always have a namespace separator between localPart and prefix */
if (prefixLen) {
- uri += i - 1;
+ uri += localPartLen - 1;
*uri = parser->m_namespaceSeparator; /* replace null terminator */
memcpy(uri + 1, binding->prefix->name, prefixLen * sizeof(XML_Char));
}
@@ -4339,7 +4374,7 @@ addBinding(XML_Parser parser, PREFIX *prefix, const ATTRIBUTE_ID *attId,
ASCII_8, ASCII_SLASH, ASCII_n, ASCII_a, ASCII_m,
ASCII_e, ASCII_s, ASCII_p, ASCII_a, ASCII_c,
ASCII_e, '\0'};
- static const int xmlLen = (int)sizeof(xmlNamespace) / sizeof(XML_Char) - 1;
+ static const size_t xmlLen = sizeof(xmlNamespace) / sizeof(XML_Char) - 1;
// "http://www.w3.org/2000/xmlns/"
static const XML_Char xmlnsNamespace[]
= {ASCII_h, ASCII_t, ASCII_t, ASCII_p, ASCII_COLON, ASCII_SLASH,
@@ -4347,15 +4382,14 @@ addBinding(XML_Parser parser, PREFIX *prefix, const ATTRIBUTE_ID *attId,
ASCII_3, ASCII_PERIOD, ASCII_o, ASCII_r, ASCII_g, ASCII_SLASH,
ASCII_2, ASCII_0, ASCII_0, ASCII_0, ASCII_SLASH, ASCII_x,
ASCII_m, ASCII_l, ASCII_n, ASCII_s, ASCII_SLASH, '\0'};
- static const int xmlnsLen
- = (int)sizeof(xmlnsNamespace) / sizeof(XML_Char) - 1;
+ static const size_t xmlnsLen = sizeof(xmlnsNamespace) / sizeof(XML_Char) - 1;
XML_Bool mustBeXML = XML_FALSE;
XML_Bool isXML = XML_TRUE;
XML_Bool isXMLNS = XML_TRUE;
BINDING *b;
- int len;
+ size_t len;
/* empty URI is only valid for default namespace per XML NS 1.0 (not 1.1) */
if (*uri == XML_T('\0') && prefix->name)
@@ -4374,6 +4408,10 @@ addBinding(XML_Parser parser, PREFIX *prefix, const ATTRIBUTE_ID *attId,
}
for (len = 0; uri[len]; len++) {
+ /* Detect and prevent integer overflow */
+ if (len == SIZE_MAX) {
+ return XML_ERROR_NO_MEMORY;
+ }
if (isXML && (len > xmlLen || uri[len] != xmlNamespace[len]))
isXML = XML_FALSE;
@@ -4414,25 +4452,21 @@ addBinding(XML_Parser parser, PREFIX *prefix, const ATTRIBUTE_ID *attId,
if (isXMLNS)
return XML_ERROR_RESERVED_NAMESPACE_URI;
- if (parser->m_namespaceSeparator)
+ if (parser->m_namespaceSeparator) {
+ /* Detect and prevent integer overflow */
+ if (len == SIZE_MAX) {
+ return XML_ERROR_NO_MEMORY;
+ }
len++;
+ }
if (parser->m_freeBindingList) {
b = parser->m_freeBindingList;
if (len > b->uriAlloc) {
/* Detect and prevent integer overflow */
- if (len > INT_MAX - EXPAND_SPARE) {
- return XML_ERROR_NO_MEMORY;
- }
-
- /* Detect and prevent integer overflow.
- * The preprocessor guard addresses the "always false" warning
- * from -Wtype-limits on platforms where
- * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */
-#if UINT_MAX >= SIZE_MAX
- if ((unsigned)(len + EXPAND_SPARE) > SIZE_MAX / sizeof(XML_Char)) {
+ if (len > SIZE_MAX - EXPAND_SPARE
+ || len + EXPAND_SPARE > SIZE_MAX / sizeof(XML_Char)) {
return XML_ERROR_NO_MEMORY;
}
-#endif
XML_Char *temp
= REALLOC(parser, b->uri, sizeof(XML_Char) * (len + EXPAND_SPARE));
@@ -4448,18 +4482,10 @@ addBinding(XML_Parser parser, PREFIX *prefix, const ATTRIBUTE_ID *attId,
return XML_ERROR_NO_MEMORY;
/* Detect and prevent integer overflow */
- if (len > INT_MAX - EXPAND_SPARE) {
+ if (len > SIZE_MAX - EXPAND_SPARE
+ || len + EXPAND_SPARE > SIZE_MAX / sizeof(XML_Char)) {
return XML_ERROR_NO_MEMORY;
}
- /* Detect and prevent integer overflow.
- * The preprocessor guard addresses the "always false" warning
- * from -Wtype-limits on platforms where
- * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */
-#if UINT_MAX >= SIZE_MAX
- if ((unsigned)(len + EXPAND_SPARE) > SIZE_MAX / sizeof(XML_Char)) {
- return XML_ERROR_NO_MEMORY;
- }
-#endif
b->uri = MALLOC(parser, sizeof(XML_Char) * (len + EXPAND_SPARE));
if (! b->uri) {
@@ -4483,9 +4509,12 @@ addBinding(XML_Parser parser, PREFIX *prefix, const ATTRIBUTE_ID *attId,
b->nextTagBinding = *bindingsPtr;
*bindingsPtr = b;
/* if attId == NULL then we are not starting a namespace scope */
- if (attId && parser->m_startNamespaceDeclHandler)
+ if (attId && parser->m_startNamespaceDeclHandler) {
+ beforeHandler(parser);
parser->m_startNamespaceDeclHandler(parser->m_handlerArg, prefix->name,
prefix->binding ? uri : 0);
+ afterHandler(parser);
+ }
return XML_ERROR_NONE;
}
@@ -4547,15 +4576,20 @@ doCdataSection(XML_Parser parser, const ENCODING *enc, const char **startPtr,
*eventEndPP = next;
switch (tok) {
case XML_TOK_CDATA_SECT_CLOSE:
- if (parser->m_endCdataSectionHandler)
+ if (parser->m_endCdataSectionHandler) {
+ beforeHandler(parser);
parser->m_endCdataSectionHandler(parser->m_handlerArg);
+ afterHandler(parser);
+ }
/* BEGIN disabled code */
/* see comment under XML_TOK_CDATA_SECT_OPEN */
- else if ((0) && parser->m_characterDataHandler)
+ else if ((0) && parser->m_characterDataHandler) {
+ beforeHandler(parser);
parser->m_characterDataHandler(parser->m_handlerArg, parser->m_dataBuf,
0);
- /* END disabled code */
- else if (parser->m_defaultHandler)
+ afterHandler(parser);
+ /* END disabled code */
+ } else if (parser->m_defaultHandler)
reportDefault(parser, enc, s, next);
*startPtr = next;
*nextPtr = next;
@@ -4566,7 +4600,9 @@ doCdataSection(XML_Parser parser, const ENCODING *enc, const char **startPtr,
case XML_TOK_DATA_NEWLINE:
if (parser->m_characterDataHandler) {
XML_Char c = 0xA;
+ beforeHandler(parser);
parser->m_characterDataHandler(parser->m_handlerArg, &c, 1);
+ afterHandler(parser);
} else if (parser->m_defaultHandler)
reportDefault(parser, enc, s, next);
break;
@@ -4579,16 +4615,21 @@ doCdataSection(XML_Parser parser, const ENCODING *enc, const char **startPtr,
const enum XML_Convert_Result convert_res = XmlConvert(
enc, &s, next, &dataPtr, (ICHAR *)parser->m_dataBufEnd);
*eventEndPP = next;
+ beforeHandler(parser);
charDataHandler(parser->m_handlerArg, parser->m_dataBuf,
(int)(dataPtr - (ICHAR *)parser->m_dataBuf));
+ afterHandler(parser);
if ((convert_res == XML_CONVERT_COMPLETED)
|| (convert_res == XML_CONVERT_INPUT_INCOMPLETE))
break;
*eventPP = s;
}
- } else
+ } else {
+ beforeHandler(parser);
charDataHandler(parser->m_handlerArg, (const XML_Char *)s,
(int)((const XML_Char *)next - (const XML_Char *)s));
+ afterHandler(parser);
+ }
} else if (parser->m_defaultHandler)
reportDefault(parser, enc, s, next);
} break;
@@ -4633,7 +4674,7 @@ doCdataSection(XML_Parser parser, const ENCODING *enc, const char **startPtr,
if (parser->m_reenter) {
return XML_ERROR_UNEXPECTED_STATE; // LCOV_EXCL_LINE
}
- /* Fall through */
+ EXPAT_FALLTHROUGH;
default:;
*eventPP = s = next;
}
@@ -4827,8 +4868,10 @@ processXmlDecl(XML_Parser parser, int isGeneralTextEntity, const char *s,
if (! storedversion)
return XML_ERROR_NO_MEMORY;
}
+ beforeHandler(parser);
parser->m_xmlDeclHandler(parser->m_handlerArg, storedversion, storedEncName,
standalone);
+ afterHandler(parser);
} else if (parser->m_defaultHandler)
reportDefault(parser, parser->m_encoding, s, next);
if (parser->m_protocolEncodingName == NULL) {
@@ -4878,8 +4921,11 @@ handleUnknownEncoding(XML_Parser parser, const XML_Char *encodingName) {
info.convert = NULL;
info.data = NULL;
info.release = NULL;
- if (parser->m_unknownEncodingHandler(parser->m_unknownEncodingHandlerData,
- encodingName, &info)) {
+ beforeHandler(parser);
+ const int status = parser->m_unknownEncodingHandler(
+ parser->m_unknownEncodingHandlerData, encodingName, &info);
+ afterHandler(parser);
+ if (status) {
ENCODING *enc;
parser->m_unknownEncodingMem = MALLOC(parser, XmlSizeOfUnknownEncoding());
if (! parser->m_unknownEncodingMem) {
@@ -5256,9 +5302,11 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end,
break;
case XML_ROLE_DOCTYPE_INTERNAL_SUBSET:
if (parser->m_startDoctypeDeclHandler) {
+ beforeHandler(parser);
parser->m_startDoctypeDeclHandler(
parser->m_handlerArg, parser->m_doctypeName, parser->m_doctypeSysid,
parser->m_doctypePubid, 1);
+ afterHandler(parser);
parser->m_doctypeName = NULL;
poolClear(&parser->m_tempPool);
handleDefault = XML_FALSE;
@@ -5297,7 +5345,7 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end,
handleDefault = XML_FALSE;
goto alreadyChecked;
}
- /* fall through */
+ EXPAT_FALLTHROUGH;
case XML_ROLE_ENTITY_PUBLIC_ID:
if (! XmlIsPublicId(enc, s, next, eventPP))
return XML_ERROR_PUBLICID;
@@ -5325,9 +5373,11 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end,
}
if (parser->m_doctypeName) {
+ beforeHandler(parser);
parser->m_startDoctypeDeclHandler(
parser->m_handlerArg, parser->m_doctypeName, parser->m_doctypeSysid,
parser->m_doctypePubid, 0);
+ afterHandler(parser);
poolClear(&parser->m_tempPool);
handleDefault = XML_FALSE;
}
@@ -5354,14 +5404,22 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end,
if (parser->m_useForeignDTD)
entity->base = parser->m_curBase;
dtd->paramEntityRead = XML_FALSE;
- if (! parser->m_externalEntityRefHandler(
- parser->m_externalEntityRefHandlerArg, 0, entity->base,
- entity->systemId, entity->publicId))
+ beforeHandler(parser);
+ const int status = parser->m_externalEntityRefHandler(
+ parser->m_externalEntityRefHandlerArg, 0, entity->base,
+ entity->systemId, entity->publicId);
+ afterHandler(parser);
+ if (! status)
return XML_ERROR_EXTERNAL_ENTITY_HANDLING;
if (dtd->paramEntityRead) {
- if (! dtd->standalone && parser->m_notStandaloneHandler
- && ! parser->m_notStandaloneHandler(parser->m_handlerArg))
- return XML_ERROR_NOT_STANDALONE;
+ if (! dtd->standalone && parser->m_notStandaloneHandler) {
+ beforeHandler(parser);
+ const int handlerStatus
+ = parser->m_notStandaloneHandler(parser->m_handlerArg);
+ afterHandler(parser);
+ if (! handlerStatus)
+ return XML_ERROR_NOT_STANDALONE;
+ }
}
/* if we didn't read the foreign DTD then this means that there
is no external subset and we must reset dtd->hasParamEntityRefs
@@ -5374,7 +5432,9 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end,
}
#endif /* XML_DTD */
if (parser->m_endDoctypeDeclHandler) {
+ beforeHandler(parser);
parser->m_endDoctypeDeclHandler(parser->m_handlerArg);
+ afterHandler(parser);
handleDefault = XML_FALSE;
}
break;
@@ -5394,14 +5454,22 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end,
return XML_ERROR_NO_MEMORY;
entity->base = parser->m_curBase;
dtd->paramEntityRead = XML_FALSE;
- if (! parser->m_externalEntityRefHandler(
- parser->m_externalEntityRefHandlerArg, 0, entity->base,
- entity->systemId, entity->publicId))
+ beforeHandler(parser);
+ const int status = parser->m_externalEntityRefHandler(
+ parser->m_externalEntityRefHandlerArg, 0, entity->base,
+ entity->systemId, entity->publicId);
+ afterHandler(parser);
+ if (! status)
return XML_ERROR_EXTERNAL_ENTITY_HANDLING;
if (dtd->paramEntityRead) {
- if (! dtd->standalone && parser->m_notStandaloneHandler
- && ! parser->m_notStandaloneHandler(parser->m_handlerArg))
- return XML_ERROR_NOT_STANDALONE;
+ if (! dtd->standalone && parser->m_notStandaloneHandler) {
+ beforeHandler(parser);
+ const int handlerStatus
+ = parser->m_notStandaloneHandler(parser->m_handlerArg);
+ afterHandler(parser);
+ if (! handlerStatus)
+ return XML_ERROR_NOT_STANDALONE;
+ }
}
/* if we didn't read the foreign DTD then this means that there
is no external subset and we must reset dtd->hasParamEntityRefs
@@ -5494,10 +5562,12 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end,
poolFinish(&parser->m_tempPool);
}
*eventEndPP = s;
+ beforeHandler(parser);
parser->m_attlistDeclHandler(
parser->m_handlerArg, parser->m_declElementType->name,
parser->m_declAttributeId->name, parser->m_declAttributeType, 0,
role == XML_ROLE_REQUIRED_ATTRIBUTE_VALUE);
+ afterHandler(parser);
handleDefault = XML_FALSE;
}
}
@@ -5532,10 +5602,12 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end,
poolFinish(&parser->m_tempPool);
}
*eventEndPP = s;
+ beforeHandler(parser);
parser->m_attlistDeclHandler(
parser->m_handlerArg, parser->m_declElementType->name,
parser->m_declAttributeId->name, parser->m_declAttributeType,
attVal, role == XML_ROLE_FIXED_ATTRIBUTE_VALUE);
+ afterHandler(parser);
poolClear(&parser->m_tempPool);
handleDefault = XML_FALSE;
}
@@ -5550,16 +5622,22 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end,
parser, enc, s + enc->minBytesPerChar, next - enc->minBytesPerChar,
XML_ACCOUNT_NONE);
if (parser->m_declEntity) {
+ /* Detect and prevent signed integer overflow */
+ if ((size_t)poolLength(&dtd->entityValuePool) > (size_t)INT_MAX) {
+ return XML_ERROR_NO_MEMORY;
+ }
parser->m_declEntity->textPtr = poolStart(&dtd->entityValuePool);
parser->m_declEntity->textLen
= (int)(poolLength(&dtd->entityValuePool));
poolFinish(&dtd->entityValuePool);
if (parser->m_entityDeclHandler) {
*eventEndPP = s;
+ beforeHandler(parser);
parser->m_entityDeclHandler(
parser->m_handlerArg, parser->m_declEntity->name,
parser->m_declEntity->is_param, parser->m_declEntity->textPtr,
parser->m_declEntity->textLen, parser->m_curBase, 0, 0, 0);
+ afterHandler(parser);
handleDefault = XML_FALSE;
}
} else
@@ -5577,10 +5655,12 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end,
if (parser->m_entityDeclHandler) {
*eventEndPP = s;
+ beforeHandler(parser);
parser->m_entityDeclHandler(
parser->m_handlerArg, parser->m_declEntity->name,
parser->m_declEntity->is_param, parser->m_declEntity->textPtr,
parser->m_declEntity->textLen, parser->m_curBase, 0, 0, 0);
+ afterHandler(parser);
handleDefault = XML_FALSE;
}
}
@@ -5611,9 +5691,13 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end,
#ifdef XML_DTD
&& ! parser->m_paramEntityParsing
#endif /* XML_DTD */
- && parser->m_notStandaloneHandler
- && ! parser->m_notStandaloneHandler(parser->m_handlerArg))
- return XML_ERROR_NOT_STANDALONE;
+ && parser->m_notStandaloneHandler) {
+ beforeHandler(parser);
+ const int status = parser->m_notStandaloneHandler(parser->m_handlerArg);
+ afterHandler(parser);
+ if (! status)
+ return XML_ERROR_NOT_STANDALONE;
+ }
#ifndef XML_DTD
break;
#else /* XML_DTD */
@@ -5625,7 +5709,7 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end,
parser->m_declEntity->publicId = NULL;
}
#endif /* XML_DTD */
- /* fall through */
+ EXPAT_FALLTHROUGH;
case XML_ROLE_ENTITY_SYSTEM_ID:
if (dtd->keepProcessing && parser->m_declEntity) {
parser->m_declEntity->systemId
@@ -5656,10 +5740,12 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end,
if (dtd->keepProcessing && parser->m_declEntity
&& parser->m_entityDeclHandler) {
*eventEndPP = s;
+ beforeHandler(parser);
parser->m_entityDeclHandler(
parser->m_handlerArg, parser->m_declEntity->name,
parser->m_declEntity->is_param, 0, 0, parser->m_declEntity->base,
parser->m_declEntity->systemId, parser->m_declEntity->publicId, 0);
+ afterHandler(parser);
handleDefault = XML_FALSE;
}
break;
@@ -5672,17 +5758,21 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end,
poolFinish(&dtd->pool);
if (parser->m_unparsedEntityDeclHandler) {
*eventEndPP = s;
+ beforeHandler(parser);
parser->m_unparsedEntityDeclHandler(
parser->m_handlerArg, parser->m_declEntity->name,
parser->m_declEntity->base, parser->m_declEntity->systemId,
parser->m_declEntity->publicId, parser->m_declEntity->notation);
+ afterHandler(parser);
handleDefault = XML_FALSE;
} else if (parser->m_entityDeclHandler) {
*eventEndPP = s;
+ beforeHandler(parser);
parser->m_entityDeclHandler(
parser->m_handlerArg, parser->m_declEntity->name, 0, 0, 0,
parser->m_declEntity->base, parser->m_declEntity->systemId,
parser->m_declEntity->publicId, parser->m_declEntity->notation);
+ afterHandler(parser);
handleDefault = XML_FALSE;
}
}
@@ -5789,9 +5879,11 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end,
if (! systemId)
return XML_ERROR_NO_MEMORY;
*eventEndPP = s;
+ beforeHandler(parser);
parser->m_notationDeclHandler(
parser->m_handlerArg, parser->m_declNotationName, parser->m_curBase,
systemId, parser->m_declNotationPublicId);
+ afterHandler(parser);
handleDefault = XML_FALSE;
}
poolClear(&parser->m_tempPool);
@@ -5799,9 +5891,11 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end,
case XML_ROLE_NOTATION_NO_SYSTEM_ID:
if (parser->m_declNotationPublicId && parser->m_notationDeclHandler) {
*eventEndPP = s;
+ beforeHandler(parser);
parser->m_notationDeclHandler(
parser->m_handlerArg, parser->m_declNotationName, parser->m_curBase,
0, parser->m_declNotationPublicId);
+ afterHandler(parser);
handleDefault = XML_FALSE;
}
poolClear(&parser->m_tempPool);
@@ -5835,41 +5929,18 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end,
case XML_ROLE_GROUP_OPEN:
if (parser->m_prologState.level >= parser->m_groupSize) {
if (parser->m_groupSize) {
- {
- /* Detect and prevent integer overflow */
- if (parser->m_groupSize > (unsigned int)(-1) / 2u) {
- return XML_ERROR_NO_MEMORY;
- }
-
- char *const new_connector = REALLOC(
- parser, parser->m_groupConnector, parser->m_groupSize *= 2);
- if (new_connector == NULL) {
- parser->m_groupSize /= 2;
- return XML_ERROR_NO_MEMORY;
- }
- parser->m_groupConnector = new_connector;
+ /* Detect and prevent integer overflow */
+ if (parser->m_groupSize > SIZE_MAX / 2) {
+ return XML_ERROR_NO_MEMORY;
}
- if (dtd->scaffIndex) {
- /* Detect and prevent integer overflow.
- * The preprocessor guard addresses the "always false" warning
- * from -Wtype-limits on platforms where
- * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */
-#if UINT_MAX >= SIZE_MAX
- if (parser->m_groupSize > SIZE_MAX / sizeof(int)) {
- parser->m_groupSize /= 2;
- return XML_ERROR_NO_MEMORY;
- }
-#endif
-
- int *const new_scaff_index = REALLOC(
- parser, dtd->scaffIndex, parser->m_groupSize * sizeof(int));
- if (new_scaff_index == NULL) {
- parser->m_groupSize /= 2;
- return XML_ERROR_NO_MEMORY;
- }
- dtd->scaffIndex = new_scaff_index;
+ char *const new_connector = REALLOC(parser, parser->m_groupConnector,
+ parser->m_groupSize *= 2);
+ if (new_connector == NULL) {
+ parser->m_groupSize /= 2;
+ return XML_ERROR_NO_MEMORY;
}
+ parser->m_groupConnector = new_connector;
} else {
parser->m_groupConnector = MALLOC(parser, parser->m_groupSize = 32);
if (! parser->m_groupConnector) {
@@ -5884,6 +5955,21 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end,
if (myindex < 0)
return XML_ERROR_NO_MEMORY;
assert(dtd->scaffIndex != NULL);
+ if ((size_t)dtd->scaffLevel >= dtd->scaffIndexSize) {
+ /* Detect and prevent integer overflow */
+ if (dtd->scaffIndexSize > SIZE_MAX / 2 / sizeof(int)) {
+ return XML_ERROR_NO_MEMORY;
+ }
+ assert(dtd->scaffIndexSize > 0);
+ const size_t new_size = dtd->scaffIndexSize * 2;
+ int *const new_scaff_index
+ = REALLOC(parser, dtd->scaffIndex, new_size * sizeof(int));
+ if (new_scaff_index == NULL) {
+ return XML_ERROR_NO_MEMORY;
+ }
+ dtd->scaffIndex = new_scaff_index;
+ dtd->scaffIndexSize = new_size;
+ }
dtd->scaffIndex[dtd->scaffLevel] = myindex;
dtd->scaffLevel++;
dtd->scaffold[myindex].type = XML_CTYPE_SEQ;
@@ -5964,7 +6050,9 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end,
/* cannot report skipped entities in declarations */
if ((role == XML_ROLE_PARAM_ENTITY_REF)
&& parser->m_skippedEntityHandler) {
+ beforeHandler(parser);
parser->m_skippedEntityHandler(parser->m_handlerArg, name, 1);
+ afterHandler(parser);
handleDefault = XML_FALSE;
}
break;
@@ -5985,9 +6073,12 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end,
dtd->paramEntityRead = XML_FALSE;
entity->open = XML_TRUE;
entityTrackingOnOpen(parser, entity, __LINE__);
- if (! parser->m_externalEntityRefHandler(
- parser->m_externalEntityRefHandlerArg, 0, entity->base,
- entity->systemId, entity->publicId)) {
+ beforeHandler(parser);
+ const int status = parser->m_externalEntityRefHandler(
+ parser->m_externalEntityRefHandlerArg, 0, entity->base,
+ entity->systemId, entity->publicId);
+ afterHandler(parser);
+ if (! status) {
entityTrackingOnClose(parser, entity, __LINE__);
entity->open = XML_FALSE;
return XML_ERROR_EXTERNAL_ENTITY_HANDLING;
@@ -6005,9 +6096,13 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end,
}
}
#endif /* XML_DTD */
- if (! dtd->standalone && parser->m_notStandaloneHandler
- && ! parser->m_notStandaloneHandler(parser->m_handlerArg))
- return XML_ERROR_NOT_STANDALONE;
+ if (! dtd->standalone && parser->m_notStandaloneHandler) {
+ beforeHandler(parser);
+ const int status = parser->m_notStandaloneHandler(parser->m_handlerArg);
+ afterHandler(parser);
+ if (! status)
+ return XML_ERROR_NOT_STANDALONE;
+ }
break;
/* Element declaration stuff */
@@ -6042,8 +6137,10 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end,
content->type = ((role == XML_ROLE_CONTENT_ANY) ? XML_CTYPE_ANY
: XML_CTYPE_EMPTY);
*eventEndPP = s;
+ beforeHandler(parser);
parser->m_elementDeclHandler(
parser->m_handlerArg, parser->m_declElementType->name, content);
+ afterHandler(parser);
handleDefault = XML_FALSE;
}
dtd->in_eldecl = XML_FALSE;
@@ -6087,9 +6184,7 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end,
return XML_ERROR_NO_MEMORY;
name = el->name;
dtd->scaffold[myindex].name = name;
- nameLen = 0;
- while (name[nameLen++])
- ;
+ nameLen = xcslen(name) + /*null terminator*/ 1;
/* Detect and prevent integer overflow */
if (nameLen > UINT_MAX - dtd->contentStringLen) {
@@ -6125,8 +6220,10 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end,
if (! model)
return XML_ERROR_NO_MEMORY;
*eventEndPP = s;
+ beforeHandler(parser);
parser->m_elementDeclHandler(
parser->m_handlerArg, parser->m_declElementType->name, model);
+ afterHandler(parser);
}
dtd->in_eldecl = XML_FALSE;
dtd->contentStringLen = 0;
@@ -6188,7 +6285,7 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end,
*nextPtr = next;
return XML_ERROR_NONE;
}
- /* Fall through */
+ EXPAT_FALLTHROUGH;
default:
s = next;
tok = XmlPrologTok(enc, s, end, &next);
@@ -6268,7 +6365,7 @@ epilogProcessor(XML_Parser parser, const char *s, const char *end,
if (parser->m_reenter) {
return XML_ERROR_UNEXPECTED_STATE; // LCOV_EXCL_LINE
}
- /* Fall through */
+ EXPAT_FALLTHROUGH;
default:;
parser->m_eventPtr = s = next;
}
@@ -6278,20 +6375,18 @@ epilogProcessor(XML_Parser parser, const char *s, const char *end,
static enum XML_Error
processEntity(XML_Parser parser, ENTITY *entity, XML_Bool betweenDecl,
enum EntityType type) {
- OPEN_INTERNAL_ENTITY *openEntity, **openEntityList, **freeEntityList;
+ OPEN_INTERNAL_ENTITY *openEntity, **openEntityList;
+ OPEN_INTERNAL_ENTITY **const freeEntityList = &parser->m_freeEntities;
switch (type) {
case ENTITY_INTERNAL:
parser->m_processor = internalEntityProcessor;
openEntityList = &parser->m_openInternalEntities;
- freeEntityList = &parser->m_freeInternalEntities;
break;
case ENTITY_ATTRIBUTE:
openEntityList = &parser->m_openAttributeEntities;
- freeEntityList = &parser->m_freeAttributeEntities;
break;
case ENTITY_VALUE:
openEntityList = &parser->m_openValueEntities;
- freeEntityList = &parser->m_freeValueEntities;
break;
/* default case serves merely as a safety net in case of a
* wrong entityType. Therefore we exclude the following lines
@@ -6408,8 +6503,8 @@ internalEntityProcessor(XML_Parser parser, const char *s, const char *end,
parser->m_openInternalEntities = parser->m_openInternalEntities->next;
/* put openEntity back in list of free instances */
- openEntity->next = parser->m_freeInternalEntities;
- parser->m_freeInternalEntities = openEntity;
+ openEntity->next = parser->m_freeEntities;
+ parser->m_freeEntities = openEntity;
if (parser->m_openInternalEntities == NULL) {
parser->m_processor = entity->is_param ? prologProcessor : contentProcessor;
@@ -6485,8 +6580,8 @@ storeAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata,
parser->m_openAttributeEntities = parser->m_openAttributeEntities->next;
/* put openEntity back in list of free instances */
- openEntity->next = parser->m_freeAttributeEntities;
- parser->m_freeAttributeEntities = openEntity;
+ openEntity->next = parser->m_freeEntities;
+ parser->m_freeEntities = openEntity;
}
// Break if an error occurred or there is nothing left to process
@@ -6539,7 +6634,6 @@ appendAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata,
return XML_ERROR_INVALID_TOKEN;
case XML_TOK_CHAR_REF: {
XML_Char buf[XML_ENCODE_MAX];
- int i;
int n = XmlCharRefNumber(enc, ptr);
if (n < 0) {
if (enc == parser->m_encoding)
@@ -6559,10 +6653,9 @@ appendAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata,
* XmlEncode() is never passed a value it might return an
* error for.
*/
- for (i = 0; i < n; i++) {
- if (! poolAppendChar(pool, buf[i]))
- return XML_ERROR_NO_MEMORY;
- }
+
+ if (! poolAppendChars(pool, buf, n))
+ return XML_ERROR_NO_MEMORY;
} break;
case XML_TOK_DATA_CHARS:
if (! poolAppend(pool, enc, ptr, next))
@@ -6570,7 +6663,7 @@ appendAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata,
break;
case XML_TOK_TRAILING_CR:
next = ptr + enc->minBytesPerChar;
- /* fall through */
+ EXPAT_FALLTHROUGH;
case XML_TOK_ATTRIBUTE_VALUE_S:
case XML_TOK_DATA_NEWLINE:
if (! isCdata && (poolLength(pool) == 0 || poolLastChar(pool) == 0x20))
@@ -6624,8 +6717,11 @@ appendAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata,
} else if (! entity) {
/* Cannot report skipped entity here - see comments on
parser->m_skippedEntityHandler.
- if (parser->m_skippedEntityHandler)
+ if (parser->m_skippedEntityHandler) {
+ beforeHandler(parser);
parser->m_skippedEntityHandler(parser->m_handlerArg, name, 0);
+ afterHandler(parser);
+ }
*/
/* Cannot call the default handler because this would be
out of sync with the call to the startElementHandler.
@@ -6758,8 +6854,11 @@ storeEntityValue(XML_Parser parser, const ENCODING *enc,
/* not a well-formedness error - see XML 1.0: WFC Entity Declared */
/* cannot report skipped entity here - see comments on
parser->m_skippedEntityHandler
- if (parser->m_skippedEntityHandler)
+ if (parser->m_skippedEntityHandler) {
+ beforeHandler(parser);
parser->m_skippedEntityHandler(parser->m_handlerArg, name, 0);
+ afterHandler(parser);
+ }
*/
dtd->keepProcessing = dtd->standalone;
goto endEntityValue;
@@ -6775,9 +6874,12 @@ storeEntityValue(XML_Parser parser, const ENCODING *enc,
dtd->paramEntityRead = XML_FALSE;
entity->open = XML_TRUE;
entityTrackingOnOpen(parser, entity, __LINE__);
- if (! parser->m_externalEntityRefHandler(
- parser->m_externalEntityRefHandlerArg, 0, entity->base,
- entity->systemId, entity->publicId)) {
+ beforeHandler(parser);
+ const int status = parser->m_externalEntityRefHandler(
+ parser->m_externalEntityRefHandlerArg, 0, entity->base,
+ entity->systemId, entity->publicId);
+ afterHandler(parser);
+ if (! status) {
entityTrackingOnClose(parser, entity, __LINE__);
entity->open = XML_FALSE;
result = XML_ERROR_EXTERNAL_ENTITY_HANDLING;
@@ -6813,17 +6915,15 @@ storeEntityValue(XML_Parser parser, const ENCODING *enc,
break;
case XML_TOK_TRAILING_CR:
next = entityTextPtr + enc->minBytesPerChar;
- /* fall through */
+ EXPAT_FALLTHROUGH;
case XML_TOK_DATA_NEWLINE:
- if (pool->end == pool->ptr && ! poolGrow(pool)) {
+ if (! poolAppendChar(pool, 0xA)) {
result = XML_ERROR_NO_MEMORY;
goto endEntityValue;
}
- *(pool->ptr)++ = 0xA;
break;
case XML_TOK_CHAR_REF: {
XML_Char buf[XML_ENCODE_MAX];
- int i;
int n = XmlCharRefNumber(enc, entityTextPtr);
if (n < 0) {
if (enc == parser->m_encoding)
@@ -6841,12 +6941,9 @@ storeEntityValue(XML_Parser parser, const ENCODING *enc,
* XmlEncode() is never passed a value it might return an
* error for.
*/
- for (i = 0; i < n; i++) {
- if (pool->end == pool->ptr && ! poolGrow(pool)) {
- result = XML_ERROR_NO_MEMORY;
- goto endEntityValue;
- }
- *(pool->ptr)++ = buf[i];
+ if (! poolAppendChars(pool, buf, n)) {
+ result = XML_ERROR_NO_MEMORY;
+ goto endEntityValue;
}
} break;
case XML_TOK_PARTIAL:
@@ -6943,8 +7040,8 @@ callStoreEntityValue(XML_Parser parser, const ENCODING *enc,
parser->m_openValueEntities = parser->m_openValueEntities->next;
/* put openEntity back in list of free instances */
- openEntity->next = parser->m_freeValueEntities;
- parser->m_freeValueEntities = openEntity;
+ openEntity->next = parser->m_freeEntities;
+ parser->m_freeEntities = openEntity;
}
// Break if an error occurred or there is nothing left to process
@@ -6974,6 +7071,11 @@ storeSelfEntityValue(XML_Parser parser, ENTITY *entity) {
return XML_ERROR_NO_MEMORY;
}
+ /* Detect and prevent signed integer overflow */
+ if ((size_t)poolLength(pool) > (size_t)INT_MAX) {
+ poolDiscard(pool);
+ return XML_ERROR_NO_MEMORY;
+ }
entity->textPtr = poolStart(pool);
entity->textLen = (int)(poolLength(pool));
poolFinish(pool);
@@ -7026,7 +7128,9 @@ reportProcessingInstruction(XML_Parser parser, const ENCODING *enc,
if (! data)
return 0;
normalizeLines(data);
+ beforeHandler(parser);
parser->m_processingInstructionHandler(parser->m_handlerArg, target, data);
+ afterHandler(parser);
poolClear(&parser->m_tempPool);
return 1;
}
@@ -7046,7 +7150,9 @@ reportComment(XML_Parser parser, const ENCODING *enc, const char *start,
if (! data)
return 0;
normalizeLines(data);
+ beforeHandler(parser);
parser->m_commentHandler(parser->m_handlerArg, data);
+ afterHandler(parser);
poolClear(&parser->m_tempPool);
return 1;
}
@@ -7087,15 +7193,20 @@ reportDefault(XML_Parser parser, const ENCODING *enc, const char *s,
convert_res
= XmlConvert(enc, &s, end, &dataPtr, (ICHAR *)parser->m_dataBufEnd);
*eventEndPP = s;
+ beforeHandler(parser);
parser->m_defaultHandler(parser->m_handlerArg, parser->m_dataBuf,
(int)(dataPtr - (ICHAR *)parser->m_dataBuf));
+ afterHandler(parser);
*eventPP = s;
} while ((convert_res != XML_CONVERT_COMPLETED)
&& (convert_res != XML_CONVERT_INPUT_INCOMPLETE));
- } else
+ } else {
+ beforeHandler(parser);
parser->m_defaultHandler(
parser->m_handlerArg, (const XML_Char *)s,
(int)((const XML_Char *)end - (const XML_Char *)s));
+ afterHandler(parser);
+ }
}
static int
@@ -7106,48 +7217,34 @@ defineAttribute(ELEMENT_TYPE *type, ATTRIBUTE_ID *attId, XML_Bool isCdata,
/* The handling of default attributes gets messed up if we have
a default which duplicates a non-default. */
NAMED *const nameFound
- = (NAMED *)lookup(parser, &(type->defaultAttsNames), attId->name, 0);
+ = lookup(parser, &(type->defaultAttsNames), attId->name, 0);
if (nameFound)
return 1;
if (isId && ! type->idAtt && ! attId->xmlns)
type->idAtt = attId;
}
if (type->nDefaultAtts == type->allocDefaultAtts) {
- if (type->allocDefaultAtts == 0) {
- type->allocDefaultAtts = 8;
- type->defaultAtts
- = MALLOC(parser, type->allocDefaultAtts * sizeof(DEFAULT_ATTRIBUTE));
- if (! type->defaultAtts) {
- type->allocDefaultAtts = 0;
- return 0;
- }
- } else {
- DEFAULT_ATTRIBUTE *temp;
-
- /* Detect and prevent integer overflow */
- if (type->allocDefaultAtts > INT_MAX / 2) {
- return 0;
- }
-
- int count = type->allocDefaultAtts * 2;
+ /* Detect and prevent integer overflow */
+ if (type->allocDefaultAtts > SIZE_MAX / 2) {
+ return 0;
+ }
- /* Detect and prevent integer overflow.
- * The preprocessor guard addresses the "always false" warning
- * from -Wtype-limits on platforms where
- * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */
-#if UINT_MAX >= SIZE_MAX
- if ((unsigned)count > SIZE_MAX / sizeof(DEFAULT_ATTRIBUTE)) {
- return 0;
- }
-#endif
+ size_t count = type->allocDefaultAtts * 2;
+ if (count == 0) {
+ count = 8;
+ }
- temp = REALLOC(parser, type->defaultAtts,
- (count * sizeof(DEFAULT_ATTRIBUTE)));
- if (temp == NULL)
- return 0;
- type->allocDefaultAtts = count;
- type->defaultAtts = temp;
+ /* Detect and prevent integer overflow. */
+ if (count > SIZE_MAX / sizeof(DEFAULT_ATTRIBUTE)) {
+ return 0;
}
+
+ DEFAULT_ATTRIBUTE *const temp = REALLOC(
+ parser, type->defaultAtts, (count * sizeof(DEFAULT_ATTRIBUTE)));
+ if (temp == NULL)
+ return 0;
+ type->allocDefaultAtts = count;
+ type->defaultAtts = temp;
}
att = type->defaultAtts + type->nDefaultAtts;
att->id = attId;
@@ -7156,8 +7253,8 @@ defineAttribute(ELEMENT_TYPE *type, ATTRIBUTE_ID *attId, XML_Bool isCdata,
if (! isCdata)
attId->maybeTokenized = XML_TRUE;
- NAMED *const nameAddedOrFound = (NAMED *)lookup(
- parser, &(type->defaultAttsNames), attId->name, sizeof(NAMED));
+ NAMED *const nameAddedOrFound
+ = lookup(parser, &(type->defaultAttsNames), attId->name, sizeof(NAMED));
if (! nameAddedOrFound)
return 0;
@@ -7230,13 +7327,14 @@ getAttributeId(XML_Parser parser, const ENCODING *enc, const char *start,
} else {
int i;
for (i = 0; name[i]; i++) {
+ /* Detect and prevent signed integer overflow */
+ if (i == INT_MAX) {
+ return NULL;
+ }
/* attributes without prefix are *not* in the default namespace */
if (name[i] == XML_T(ASCII_COLON)) {
- int j;
- for (j = 0; j < i; j++) {
- if (! poolAppendChar(&dtd->pool, name[j]))
- return NULL;
- }
+ if (! poolAppendChars(&dtd->pool, name, i))
+ return NULL;
if (! poolAppendChar(&dtd->pool, XML_T('\0')))
return NULL;
id->prefix = (PREFIX *)lookup(parser, &dtd->prefixes,
@@ -7264,46 +7362,39 @@ getContext(XML_Parser parser) {
XML_Bool needSep = XML_FALSE;
if (dtd->defaultPrefix.binding) {
- int i;
- int len;
if (! poolAppendChar(&parser->m_tempPool, XML_T(ASCII_EQUALS)))
return NULL;
- len = dtd->defaultPrefix.binding->uriLen;
+ size_t len = dtd->defaultPrefix.binding->uriLen;
if (parser->m_namespaceSeparator)
len--;
- for (i = 0; i < len; i++) {
- if (! poolAppendChar(&parser->m_tempPool,
- dtd->defaultPrefix.binding->uri[i])) {
- /* Because of memory caching, I don't believe this line can be
- * executed.
- *
- * This is part of a loop copying the default prefix binding
- * URI into the parser's temporary string pool. Previously,
- * that URI was copied into the same string pool, with a
- * terminating NUL character, as part of setContext(). When
- * the pool was cleared, that leaves a block definitely big
- * enough to hold the URI on the free block list of the pool.
- * The URI copy in getContext() therefore cannot run out of
- * memory.
- *
- * If the pool is used between the setContext() and
- * getContext() calls, the worst it can do is leave a bigger
- * block on the front of the free list. Given that this is
- * all somewhat inobvious and program logic can be changed, we
- * don't delete the line but we do exclude it from the test
- * coverage statistics.
- */
- return NULL; /* LCOV_EXCL_LINE */
- }
+ if (! poolAppendChars(&parser->m_tempPool, dtd->defaultPrefix.binding->uri,
+ len)) {
+ /* Because of memory caching, I don't believe this line can be
+ * executed.
+ *
+ * This is part of a loop copying the default prefix binding
+ * URI into the parser's temporary string pool. Previously,
+ * that URI was copied into the same string pool, with a
+ * terminating NUL character, as part of setContext(). When
+ * the pool was cleared, that leaves a block definitely big
+ * enough to hold the URI on the free block list of the pool.
+ * The URI copy in getContext() therefore cannot run out of
+ * memory.
+ *
+ * If the pool is used between the setContext() and
+ * getContext() calls, the worst it can do is leave a bigger
+ * block on the front of the free list. Given that this is
+ * all somewhat inobvious and program logic can be changed, we
+ * don't delete the line but we do exclude it from the test
+ * coverage statistics.
+ */
+ return NULL; /* LCOV_EXCL_LINE */
}
needSep = XML_TRUE;
}
hashTableIterInit(&iter, &(dtd->prefixes));
for (;;) {
- int i;
- int len;
- const XML_Char *s;
PREFIX *prefix = (PREFIX *)hashTableIterNext(&iter);
if (! prefix)
break;
@@ -7318,23 +7409,21 @@ getContext(XML_Parser parser) {
}
if (needSep && ! poolAppendChar(&parser->m_tempPool, CONTEXT_SEP))
return NULL;
- for (s = prefix->name; *s; s++)
- if (! poolAppendChar(&parser->m_tempPool, *s))
- return NULL;
+ if (! poolAppendChars(&parser->m_tempPool, prefix->name,
+ xcslen(prefix->name)))
+ return NULL;
if (! poolAppendChar(&parser->m_tempPool, XML_T(ASCII_EQUALS)))
return NULL;
- len = prefix->binding->uriLen;
+ size_t len = prefix->binding->uriLen;
if (parser->m_namespaceSeparator)
len--;
- for (i = 0; i < len; i++)
- if (! poolAppendChar(&parser->m_tempPool, prefix->binding->uri[i]))
- return NULL;
+ if (! poolAppendChars(&parser->m_tempPool, prefix->binding->uri, len))
+ return NULL;
needSep = XML_TRUE;
}
hashTableIterInit(&iter, &(dtd->generalEntities));
for (;;) {
- const XML_Char *s;
ENTITY *e = (ENTITY *)hashTableIterNext(&iter);
if (! e)
break;
@@ -7342,9 +7431,8 @@ getContext(XML_Parser parser) {
continue;
if (needSep && ! poolAppendChar(&parser->m_tempPool, CONTEXT_SEP))
return NULL;
- for (s = e->name; *s; s++)
- if (! poolAppendChar(&parser->m_tempPool, *s))
- return 0;
+ if (! poolAppendChars(&parser->m_tempPool, e->name, xcslen(e->name)))
+ return NULL;
needSep = XML_TRUE;
}
@@ -7466,6 +7554,7 @@ dtdCreate(XML_Parser parser) {
p->in_eldecl = XML_FALSE;
p->scaffIndex = NULL;
+ p->scaffIndexSize = 0;
p->scaffold = NULL;
p->scaffLevel = 0;
p->scaffSize = 0;
@@ -7487,8 +7576,7 @@ dtdReset(DTD *p, XML_Parser parser) {
if (! e)
break;
hashTableDestroy(&(e->defaultAttsNames));
- if (e->allocDefaultAtts != 0)
- FREE(parser, e->defaultAtts);
+ FREE(parser, e->defaultAtts);
}
hashTableClear(&(p->generalEntities));
#ifdef XML_DTD
@@ -7507,6 +7595,7 @@ dtdReset(DTD *p, XML_Parser parser) {
FREE(parser, p->scaffIndex);
p->scaffIndex = NULL;
+ p->scaffIndexSize = 0;
FREE(parser, p->scaffold);
p->scaffold = NULL;
@@ -7529,8 +7618,7 @@ dtdDestroy(DTD *p, XML_Bool isDocEntity, XML_Parser parser) {
if (! e)
break;
hashTableDestroy(&(e->defaultAttsNames));
- if (e->allocDefaultAtts != 0)
- FREE(parser, e->defaultAtts);
+ FREE(parser, e->defaultAtts);
}
hashTableDestroy(&(p->generalEntities));
#ifdef XML_DTD
@@ -7609,7 +7697,6 @@ dtdCopy(XML_Parser oldParser, DTD *newDtd, const DTD *oldDtd,
hashTableIterInit(&iter, &(oldDtd->elementTypes));
for (;;) {
- int i;
ELEMENT_TYPE *newE;
const XML_Char *name;
const ELEMENT_TYPE *oldE = (ELEMENT_TYPE *)hashTableIterNext(&iter);
@@ -7627,15 +7714,10 @@ dtdCopy(XML_Parser oldParser, DTD *newDtd, const DTD *oldDtd,
hashTableInit(&(newE->defaultAttsNames), parser);
if (oldE->nDefaultAtts) {
- /* Detect and prevent integer overflow.
- * The preprocessor guard addresses the "always false" warning
- * from -Wtype-limits on platforms where
- * sizeof(int) < sizeof(size_t), e.g. on x86_64. */
-#if UINT_MAX >= SIZE_MAX
- if ((size_t)oldE->nDefaultAtts > SIZE_MAX / sizeof(DEFAULT_ATTRIBUTE)) {
+ /* Detect and prevent integer overflow. */
+ if (oldE->nDefaultAtts > SIZE_MAX / sizeof(DEFAULT_ATTRIBUTE)) {
return 0;
}
-#endif
newE->defaultAtts
= MALLOC(parser, oldE->nDefaultAtts * sizeof(DEFAULT_ATTRIBUTE));
if (! newE->defaultAtts) {
@@ -7649,7 +7731,7 @@ dtdCopy(XML_Parser oldParser, DTD *newDtd, const DTD *oldDtd,
if (oldE->prefix)
newE->prefix = (PREFIX *)lookup(oldParser, &(newDtd->prefixes),
oldE->prefix->name, 0);
- for (i = 0; i < newE->nDefaultAtts; i++) {
+ for (size_t i = 0; i < newE->nDefaultAtts; i++) {
const XML_Char *const attributeName = oldE->defaultAtts[i].id->name;
newE->defaultAtts[i].id = (ATTRIBUTE_ID *)lookup(
oldParser, &(newDtd->attributeIds), attributeName, 0);
@@ -7662,8 +7744,8 @@ dtdCopy(XML_Parser oldParser, DTD *newDtd, const DTD *oldDtd,
} else
newE->defaultAtts[i].value = NULL;
- NAMED *const nameAddedOrFound = (NAMED *)lookup(
- parser, &(newE->defaultAttsNames), attributeName, sizeof(NAMED));
+ NAMED *const nameAddedOrFound = lookup(parser, &(newE->defaultAttsNames),
+ attributeName, sizeof(NAMED));
if (! nameAddedOrFound) {
return 0;
}
@@ -7693,6 +7775,7 @@ dtdCopy(XML_Parser oldParser, DTD *newDtd, const DTD *oldDtd,
newDtd->scaffSize = oldDtd->scaffSize;
newDtd->scaffLevel = oldDtd->scaffLevel;
newDtd->scaffIndex = oldDtd->scaffIndex;
+ newDtd->scaffIndexSize = oldDtd->scaffIndexSize;
return 1;
} /* End dtdCopy */
@@ -7764,18 +7847,23 @@ copyEntityTable(XML_Parser oldParser, HASH_TABLE *newTable,
static XML_Bool FASTCALL
keyeq(KEY s1, KEY s2) {
+#ifdef XML_UNICODE
+# ifdef XML_UNICODE_WCHAR_T
+ return (wcscmp(s1, s2) == 0) ? XML_TRUE : XML_FALSE;
+# else
for (; *s1 == *s2; s1++, s2++)
if (*s1 == 0)
return XML_TRUE;
return XML_FALSE;
+# endif
+#else
+ return (strcmp(s1, s2) == 0) ? XML_TRUE : XML_FALSE;
+#endif
}
static size_t
keylen(KEY s) {
- size_t len = 0;
- for (; *s; s++, len++)
- ;
- return len;
+ return xcslen(s);
}
static void
@@ -7993,10 +8081,8 @@ poolAppend(STRING_POOL *pool, const ENCODING *enc, const char *ptr,
static const XML_Char *FASTCALL
poolCopyString(STRING_POOL *pool, const XML_Char *s) {
- do {
- if (! poolAppendChar(pool, *s))
- return NULL;
- } while (*s++);
+ if (! poolAppendChars(pool, s, xcslen(s) + /*null terminator*/ 1))
+ return NULL;
s = pool->start;
poolFinish(pool);
return s;
@@ -8035,10 +8121,8 @@ poolCopyStringN(STRING_POOL *pool, const XML_Char *s, int n) {
*/
return NULL; /* LCOV_EXCL_LINE */
}
- for (; n > 0; --n, s++) {
- if (! poolAppendChar(pool, *s))
- return NULL;
- }
+ if (n > 0 && ! poolAppendChars(pool, s, n))
+ return NULL;
s = pool->start;
poolFinish(pool);
return s;
@@ -8046,11 +8130,8 @@ poolCopyStringN(STRING_POOL *pool, const XML_Char *s, int n) {
static const XML_Char *FASTCALL
poolAppendString(STRING_POOL *pool, const XML_Char *s) {
- while (*s) {
- if (! poolAppendChar(pool, *s))
- return NULL;
- s++;
- }
+ if (! poolAppendChars(pool, s, xcslen(s)))
+ return NULL;
return pool->start;
}
@@ -8059,9 +8140,8 @@ poolStoreString(STRING_POOL *pool, const ENCODING *enc, const char *ptr,
const char *end) {
if (! poolAppend(pool, enc, ptr, end))
return NULL;
- if (pool->ptr == pool->end && ! poolGrow(pool))
+ if (! poolAppendChar(pool, 0))
return NULL;
- *(pool->ptr)++ = 0;
return pool->start;
}
@@ -8196,6 +8276,19 @@ poolGrow(STRING_POOL *pool) {
return XML_TRUE;
}
+static bool FASTCALL
+poolGrowUntil(STRING_POOL *pool, size_t needed) {
+ for (;;) {
+ const size_t available = pool->end - pool->ptr;
+ if (available >= needed) {
+ return true;
+ }
+ if (! poolGrow(pool)) {
+ return false;
+ }
+ }
+}
+
static int FASTCALL
nextScaffoldPart(XML_Parser parser) {
DTD *const dtd = parser->m_dtd; /* save one level of indirection */
@@ -8203,18 +8296,14 @@ nextScaffoldPart(XML_Parser parser) {
int next;
if (! dtd->scaffIndex) {
- /* Detect and prevent integer overflow.
- * The preprocessor guard addresses the "always false" warning
- * from -Wtype-limits on platforms where
- * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */
-#if UINT_MAX >= SIZE_MAX
+ /* Detect and prevent integer overflow. */
if (parser->m_groupSize > SIZE_MAX / sizeof(int)) {
return -1;
}
-#endif
dtd->scaffIndex = MALLOC(parser, parser->m_groupSize * sizeof(int));
if (! dtd->scaffIndex)
return -1;
+ dtd->scaffIndexSize = parser->m_groupSize;
dtd->scaffIndex[0] = 0;
}
@@ -8375,12 +8464,21 @@ build_model(XML_Parser parser) {
const XML_Char *src;
dest->name = str;
src = dtd->scaffold[src_node].name;
- for (;;) {
- *str++ = *src;
- if (! *src)
- break;
- src++;
+
+ const size_t nameLen = xcslen(src) + /* null terminator*/ 1;
+
+ // Detect and prevent integer overflow
+ if (nameLen > SIZE_MAX / sizeof(XML_Char)) {
+ // NOTE: We are avoiding FREE(..) here because the model
+ // is not being allocated with MALLOC(..) but with plain
+ // .malloc_fcn(..).
+ parser->m_mem.free_fcn(ret);
+ return NULL;
}
+
+ memcpy(str, src, nameLen * sizeof(XML_Char));
+ str += nameLen;
+
dest->numchildren = 0;
dest->children = NULL;
} else {
@@ -8427,22 +8525,24 @@ getElementType(XML_Parser parser, const ENCODING *enc, const char *ptr,
static XML_Char *
copyString(const XML_Char *s, XML_Parser parser) {
- size_t charsRequired = 0;
- XML_Char *result;
-
/* First determine how long the string is */
- while (s[charsRequired] != 0) {
- charsRequired++;
- }
- /* Include the terminator */
- charsRequired++;
+ const size_t charsRequired = xcslen(s) + /*null terminator*/ 1;
+
+ /* Detect and prevent integer overflow */
+ if (charsRequired > SIZE_MAX / sizeof(XML_Char))
+ return NULL;
+
+ const size_t bytesRequired = charsRequired * sizeof(XML_Char);
/* Now allocate space for the copy */
- result = MALLOC(parser, charsRequired * sizeof(XML_Char));
+ XML_Char *const result = MALLOC(parser, bytesRequired);
+
if (result == NULL)
return NULL;
+
/* Copy the original into place */
- memcpy(result, s, charsRequired * sizeof(XML_Char));
+ memcpy(result, s, bytesRequired);
+
return result;
}
@@ -8609,15 +8709,25 @@ entityTrackingReportStats(XML_Parser rootParser, ENTITY *entity,
const char *const entityName = entity->name;
# endif
+ const bool limitingWanted = rootParser->m_entity_stats.debugLevel < 2;
+ const int maxLimitedDepth = 10; // somewhat arbitrary
+ const int candidateIndentDepth
+ = (int)rootParser->m_entity_stats.currentDepth - 1;
+ const bool limitingNeeded
+ = limitingWanted && (candidateIndentDepth > maxLimitedDepth);
+ const char *const ellipisOrEmpty = limitingNeeded ? " [..] " : "";
+ const int indentDepth
+ = limitingNeeded ? (maxLimitedDepth - /* make space for ellipis */ 2)
+ : candidateIndentDepth;
+
fprintf(
stderr,
- "expat: Entities(%p): Count %9u, depth %2u/%2u %*s%s%s; %s length %d (xmlparse.c:%d)\n",
+ "expat: Entities(%p): Count %9u, depth %2u/%2u %*s%s%s%s; %s length %d (xmlparse.c:%d)\n",
(void *)rootParser, rootParser->m_entity_stats.countEverOpened,
rootParser->m_entity_stats.currentDepth,
- rootParser->m_entity_stats.maximumDepthSeen,
- ((int)rootParser->m_entity_stats.currentDepth - 1) * 2, "",
- entity->is_param ? "%" : "&", entityName, action, entity->textLen,
- sourceLine);
+ rootParser->m_entity_stats.maximumDepthSeen, indentDepth * 2, "",
+ ellipisOrEmpty, entity->is_param ? "%" : "&", entityName, action,
+ entity->textLen, sourceLine);
}
static void
diff --git a/lib/xmltok.c b/lib/xmltok.c
index 32cd5f147e93..387c6e44a25f 100644
--- a/lib/xmltok.c
+++ b/lib/xmltok.c
@@ -25,6 +25,7 @@
Copyright (c) 2022 Sean McBride <sean@rogue-research.com>
Copyright (c) 2023 Hanno Böck <hanno@gentoo.org>
Copyright (c) 2025 Alfonso Gregory <gfunni234@gmail.com>
+ Copyright (c) 2026 Nick Begg <nick@stunttruck.net>
Licensed under the MIT license:
Permission is hereby granted, free of charge, to any person obtaining
@@ -58,6 +59,7 @@
#endif
#include "internal.h"
+#include "fallthrough.h"
#include "xmltok.h"
#include "nametab.h"
@@ -641,7 +643,7 @@ unicode_byte_type(char hi, char lo) {
*(*toP)++ = lo; \
break; \
} \
- /* fall through */ \
+ EXPAT_FALLTHROUGH; \
case 0x1: \
case 0x2: \
case 0x3: \
@@ -1557,7 +1559,7 @@ initScan(const ENCODING *const *encodingTable, const INIT_ENCODING *enc,
case 0xEF: /* possibly first byte of UTF-8 BOM */
if (INIT_ENC_INDEX(enc) == ISO_8859_1_ENC && state == XML_CONTENT_STATE)
break;
- /* fall through */
+ EXPAT_FALLTHROUGH;
case 0x00:
case 0x3C:
return XML_TOK_PARTIAL;
diff --git a/lib/xmltok_impl.c b/lib/xmltok_impl.c
index 239a2d06c451..eae11bdd968a 100644
--- a/lib/xmltok_impl.c
+++ b/lib/xmltok_impl.c
@@ -17,6 +17,7 @@
Copyright (c) 2019 David Loffredo <loffredo@steptools.com>
Copyright (c) 2020 Boris Kolpackov <boris@codesynthesis.com>
Copyright (c) 2022 Martin Ettl <ettl.martin78@googlemail.com>
+ Copyright (c) 2026 Nick Begg <nick@stunttruck.net>
Licensed under the MIT license:
Permission is hereby granted, free of charge, to any person obtaining
@@ -83,7 +84,7 @@
*nextTokPtr = ptr; \
return XML_TOK_INVALID; \
} \
- /* fall through */ \
+ EXPAT_FALLTHROUGH; \
case BT_NMSTRT: \
case BT_HEX: \
case BT_DIGIT: \
@@ -112,7 +113,7 @@
*nextTokPtr = ptr; \
return XML_TOK_INVALID; \
} \
- /* fall through */ \
+ EXPAT_FALLTHROUGH; \
case BT_NMSTRT: \
case BT_HEX: \
ptr += MINBPC(enc); \
@@ -209,7 +210,7 @@ PREFIX(scanDecl)(const ENCODING *enc, const char *ptr, const char *end,
*nextTokPtr = ptr;
return XML_TOK_INVALID;
}
- /* fall through */
+ EXPAT_FALLTHROUGH;
case BT_S:
case BT_CR:
case BT_LF:
@@ -323,7 +324,7 @@ PREFIX(scanPi)(const ENCODING *enc, const char *ptr, const char *end,
*nextTokPtr = ptr + MINBPC(enc);
return tok;
}
- /* fall through */
+ EXPAT_FALLTHROUGH;
default:
*nextTokPtr = ptr;
return XML_TOK_INVALID;
@@ -615,7 +616,7 @@ PREFIX(scanAtts)(const ENCODING *enc, const char *ptr, const char *end,
return XML_TOK_INVALID;
}
}
- /* fall through */
+ EXPAT_FALLTHROUGH;
case BT_EQUALS: {
int open;
# ifdef XML_NS
@@ -898,7 +899,7 @@ PREFIX(contentTok)(const ENCODING *enc, const char *ptr, const char *end,
return XML_TOK_INVALID;
}
}
- /* fall through */
+ EXPAT_FALLTHROUGH;
case BT_AMP:
case BT_LT:
case BT_NONXML:
@@ -1059,7 +1060,7 @@ PREFIX(prologTok)(const ENCODING *enc, const char *ptr, const char *end,
/* indicate that this might be part of a CR/LF pair */
return -XML_TOK_PROLOG_S;
}
- /* fall through */
+ EXPAT_FALLTHROUGH;
case BT_S:
case BT_LF:
for (;;) {
@@ -1074,7 +1075,7 @@ PREFIX(prologTok)(const ENCODING *enc, const char *ptr, const char *end,
/* don't split CR/LF pair */
if (ptr + MINBPC(enc) != end)
break;
- /* fall through */
+ EXPAT_FALLTHROUGH;
default:
*nextTokPtr = ptr;
return XML_TOK_PROLOG_S;
@@ -1189,7 +1190,7 @@ PREFIX(prologTok)(const ENCODING *enc, const char *ptr, const char *end,
tok = XML_TOK_NMTOKEN;
break;
}
- /* fall through */
+ EXPAT_FALLTHROUGH;
default:
*nextTokPtr = ptr;
return XML_TOK_INVALID;
@@ -1484,7 +1485,7 @@ PREFIX(isPublicId)(const ENCODING *enc, const char *ptr, const char *end,
case BT_NMSTRT:
if (! (BYTE_TO_ASCII(enc, ptr) & ~0x7f))
break;
- /* fall through */
+ EXPAT_FALLTHROUGH;
default:
switch (BYTE_TO_ASCII(enc, ptr)) {
case 0x24: /* $ */
diff --git a/tests/acc_tests.c b/tests/acc_tests.c
index b58647a2ab02..2e078e6adff5 100644
--- a/tests/acc_tests.c
+++ b/tests/acc_tests.c
@@ -10,7 +10,7 @@
Copyright (c) 2003 Greg Stein <gstein@users.sourceforge.net>
Copyright (c) 2005-2007 Steven Solie <steven@solie.ca>
Copyright (c) 2005-2012 Karl Waclawek <karl@waclawek.net>
- Copyright (c) 2016-2024 Sebastian Pipping <sebastian@pipping.org>
+ Copyright (c) 2016-2026 Sebastian Pipping <sebastian@pipping.org>
Copyright (c) 2017-2022 Rhodri James <rhodri@wildebeest.org.uk>
Copyright (c) 2017 Joe Orton <jorton@redhat.com>
Copyright (c) 2017 José Gutiérrez de la Concha <jose@zeroc.com>
@@ -41,12 +41,12 @@
USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
+#include "expat_config.h"
+
#include <math.h> /* NAN, INFINITY */
#include <stdio.h>
#include <string.h>
-#include "expat_config.h"
-
#include "expat.h"
#include "internal.h"
#include "common.h"
diff --git a/tests/alloc_tests.c b/tests/alloc_tests.c
index 433bf611808a..d97bb277e1e8 100644
--- a/tests/alloc_tests.c
+++ b/tests/alloc_tests.c
@@ -10,7 +10,7 @@
Copyright (c) 2003 Greg Stein <gstein@users.sourceforge.net>
Copyright (c) 2005-2007 Steven Solie <steven@solie.ca>
Copyright (c) 2005-2012 Karl Waclawek <karl@waclawek.net>
- Copyright (c) 2016-2025 Sebastian Pipping <sebastian@pipping.org>
+ Copyright (c) 2016-2026 Sebastian Pipping <sebastian@pipping.org>
Copyright (c) 2017-2022 Rhodri James <rhodri@wildebeest.org.uk>
Copyright (c) 2017 Joe Orton <jorton@redhat.com>
Copyright (c) 2017 José Gutiérrez de la Concha <jose@zeroc.com>
@@ -47,14 +47,14 @@
# undef NDEBUG /* because test suite relies on assert(...) at the moment */
#endif
+#include "expat_config.h"
+
#include <math.h> /* NAN, INFINITY */
#include <stdbool.h>
#include <stdint.h> /* for SIZE_MAX */
#include <string.h>
#include <assert.h>
-#include "expat_config.h"
-
#include "expat.h"
#include "internal.h"
#include "common.h"
diff --git a/tests/basic_tests.c b/tests/basic_tests.c
index b5088bcda988..d4dd9adce1fd 100644
--- a/tests/basic_tests.c
+++ b/tests/basic_tests.c
@@ -48,18 +48,15 @@
# undef NDEBUG /* because test suite relies on assert(...) at the moment */
#endif
+#include "expat_config.h"
+
#include <assert.h>
+#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
-#if ! defined(__cplusplus)
-# include <stdbool.h>
-#endif
-
-#include "expat_config.h"
-
#include "expat.h"
#include "internal.h"
#include "minicheck.h"
@@ -3306,7 +3303,6 @@ get_feature(enum XML_FeatureEnum feature_id, long *presult) {
/* Test odd corners of the XML_GetBuffer interface */
START_TEST(test_get_buffer_1) {
const char *text = get_buffer_test_text;
- void *buffer;
long context_bytes;
/* Attempt to allocate a negative length buffer */
@@ -3314,7 +3310,7 @@ START_TEST(test_get_buffer_1) {
fail("Negative length buffer not failed");
/* Now get a small buffer and extend it past valid length */
- buffer = XML_GetBuffer(g_parser, 1536);
+ void *const buffer = XML_GetBuffer(g_parser, 1536);
if (buffer == NULL)
fail("1.5K buffer failed");
assert(buffer != NULL);
@@ -3349,10 +3345,9 @@ END_TEST
/* Test more corners of the XML_GetBuffer interface */
START_TEST(test_get_buffer_2) {
const char *text = get_buffer_test_text;
- void *buffer;
/* Now get a decent buffer */
- buffer = XML_GetBuffer(g_parser, 1536);
+ void *const buffer = XML_GetBuffer(g_parser, 1536);
if (buffer == NULL)
fail("1.5K buffer failed");
assert(buffer != NULL);
@@ -4387,6 +4382,37 @@ START_TEST(test_skipped_external_entity) {
}
END_TEST
+START_TEST(test_scaff_index_shared_across_external_entity_parser) {
+ const char text[]
+ = "<!DOCTYPE doc [\n"
+ "<!ELEMENT a "
+ "((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((b))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))>\n"
+ "<!ENTITY % e SYSTEM 'ext'>\n"
+ "%e;\n"
+ "<!ELEMENT c "
+ "(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((d)))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))>\n"
+ "]>\n"
+ "<doc/>";
+ ExtOption options[]
+ = {{XCS("ext"),
+ "<!ELEMENT x "
+ "((((((((((((((((((((((((((((((((y))))))))))))))))))))))))))))))))>"},
+ {NULL, NULL}};
+
+ XML_Parser parser = XML_ParserCreate(NULL);
+ XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
+ XML_SetUserData(parser, options);
+ XML_SetExternalEntityRefHandler(parser, external_entity_optioner);
+ XML_SetElementDeclHandler(parser, dummy_element_decl_handler);
+
+ if (_XML_Parse_SINGLE_BYTES(parser, text, (int)strlen(text), XML_TRUE)
+ == XML_STATUS_ERROR)
+ xml_failure(parser);
+
+ XML_ParserFree(parser);
+}
+END_TEST
+
/* Test a different form of unknown external entity */
START_TEST(test_skipped_null_loaded_ext_entity) {
const char *text = "<!DOCTYPE doc SYSTEM 'http://example.org/one.ent'>\n"
@@ -6754,6 +6780,8 @@ make_basic_test_case(Suite *s) {
tcase_add_test(tc_basic, test_trailing_cr_in_att_value);
tcase_add_test(tc_basic, test_standalone_internal_entity);
tcase_add_test(tc_basic, test_skipped_external_entity);
+ tcase_add_test__ifdef_xml_dtd(
+ tc_basic, test_scaff_index_shared_across_external_entity_parser);
tcase_add_test(tc_basic, test_skipped_null_loaded_ext_entity);
tcase_add_test(tc_basic, test_skipped_unloaded_ext_entity);
tcase_add_test__ifdef_xml_dtd(tc_basic, test_param_entity_with_trailing_cr);
diff --git a/tests/chardata.c b/tests/chardata.c
index 2adb2c5462bd..ef50a714856d 100644
--- a/tests/chardata.c
+++ b/tests/chardata.c
@@ -47,15 +47,7 @@
#include <string.h>
#include "chardata.h"
-
-static int
-xmlstrlen(const XML_Char *s) {
- int len = 0;
- assert(s != NULL);
- while (s[len] != 0)
- ++len;
- return len;
-}
+#include "../lib/xcsinc.c"
void
CharData_Init(CharData *storage) {
@@ -73,7 +65,7 @@ CharData_AppendXMLChars(CharData *storage, const XML_Char *s, int len) {
if (storage->count < 0)
storage->count = 0;
if (len < 0)
- len = xmlstrlen(s);
+ len = (int)xcslen(s);
if ((len + storage->count) > maxchars) {
len = (maxchars - storage->count);
}
@@ -85,7 +77,7 @@ CharData_AppendXMLChars(CharData *storage, const XML_Char *s, int len) {
int
CharData_CheckXMLChars(CharData *storage, const XML_Char *expected) {
- int len = xmlstrlen(expected);
+ int len = (int)xcslen(expected);
int count;
assert(storage != NULL);
diff --git a/tests/common.c b/tests/common.c
index 389d61f00b3a..3c4a3661a8bc 100644
--- a/tests/common.c
+++ b/tests/common.c
@@ -10,7 +10,7 @@
Copyright (c) 2003 Greg Stein <gstein@users.sourceforge.net>
Copyright (c) 2005-2007 Steven Solie <steven@solie.ca>
Copyright (c) 2005-2012 Karl Waclawek <karl@waclawek.net>
- Copyright (c) 2016-2025 Sebastian Pipping <sebastian@pipping.org>
+ Copyright (c) 2016-2026 Sebastian Pipping <sebastian@pipping.org>
Copyright (c) 2017-2022 Rhodri James <rhodri@wildebeest.org.uk>
Copyright (c) 2017 Joe Orton <jorton@redhat.com>
Copyright (c) 2017 José Gutiérrez de la Concha <jose@zeroc.com>
@@ -42,13 +42,14 @@
USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
+#include "expat_config.h"
+
#include <assert.h>
#include <errno.h>
#include <stdint.h> // for SIZE_MAX
#include <stdio.h>
#include <string.h>
-#include "expat_config.h"
#include "expat.h"
#include "internal.h"
#include "chardata.h"
diff --git a/tests/handlers.c b/tests/handlers.c
index cef04ed849b0..bdc0e148a23b 100644
--- a/tests/handlers.c
+++ b/tests/handlers.c
@@ -21,6 +21,7 @@
Copyright (c) 2023-2024 Sony Corporation / Snild Dolkow <snild@sony.com>
Copyright (c) 2026 Matthew Fernandez <matthew.fernandez@gmail.com>
Copyright (c) 2026 Berkay Eren Ürün <berkay.ueruen@siemens.com>
+ Copyright (c) 2026 Kartik Kenchi <netliomax25@gmail.com>
Licensed under the MIT license:
Permission is hereby granted, free of charge, to any person obtaining
@@ -47,13 +48,13 @@
# undef NDEBUG /* because test suite relies on assert(...) at the moment */
#endif
+#include "expat_config.h"
+
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
-#include "expat_config.h"
-
#include "expat.h"
#include "internal.h"
#include "chardata.h"
@@ -1992,3 +1993,51 @@ accumulate_and_suspend_comment_handler(void *userData, const XML_Char *data) {
accumulate_comment(parserPlusStorage->storage, data);
XML_StopParser(parserPlusStorage->parser, XML_TRUE);
}
+
+void XMLCALL
+forbidden_calls_character_handler(void *userData, const XML_Char *s, int len) {
+ UNUSED_P(s);
+ UNUSED_P(len);
+ XML_Parser parser = userData;
+
+ assert_true(parser != NULL); // self-test
+
+ assert_true(XML_GetBuffer(parser, 123) == NULL); // i.e. rejected
+
+ assert_true(XML_Parse(parser, "", 0, /*isFinal=*/XML_FALSE)
+ == XML_STATUS_ERROR); // i.e. rejected
+
+ assert_true(XML_ParseBuffer(parser, 0, /*isFinal=*/XML_FALSE)
+ == XML_STATUS_ERROR); // i.e. rejected
+
+ XML_ParserFree(parser); // rejected
+
+ assert_true(XML_ParserReset(parser, /*encodingName=*/NULL)
+ == XML_FALSE); // i.e. rejected
+
+ assert_true(XML_GetErrorCode(parser) == XML_ERROR_NONE);
+}
+
+void XMLCALL
+suspend_then_resume_character_handler(void *userData, const XML_Char *s,
+ int len) {
+ UNUSED_P(s);
+ UNUSED_P(len);
+ ResumeFromHandlerData *const data = (ResumeFromHandlerData *)userData;
+
+ data->callCount++;
+ if (data->callCount > 1) {
+ // Reached only if the guard under test is missing: XML_ResumeParser would
+ // then have driven the parser re-entrantly and called us again. Bail out
+ // so the test fails by assertion below rather than recursing without bound.
+ return;
+ }
+
+ // Put the parser into XML_SUSPENDED so that, without the guard,
+ // XML_ResumeParser would proceed into a re-entrant parse.
+ assert_true(XML_StopParser(data->parser, /*resumable=*/XML_TRUE)
+ == XML_STATUS_OK);
+
+ // Resuming the parser from inside a handler must be rejected.
+ assert_true(XML_ResumeParser(data->parser) == XML_STATUS_ERROR);
+}
diff --git a/tests/handlers.h b/tests/handlers.h
index be8f8931982c..54e084cdb920 100644
--- a/tests/handlers.h
+++ b/tests/handlers.h
@@ -20,6 +20,7 @@
Copyright (c) 2021 Donghee Na <donghee.na@python.org>
Copyright (c) 2023 Sony Corporation / Snild Dolkow <snild@sony.com>
Copyright (c) 2026 Berkay Eren Ürün <berkay.ueruen@siemens.com>
+ Copyright (c) 2026 Kartik Kenchi <netliomax25@gmail.com>
Licensed under the MIT license:
Permission is hereby granted, free of charge, to any person obtaining
@@ -613,6 +614,19 @@ typedef struct {
extern void XMLCALL
accumulate_and_suspend_comment_handler(void *userData, const XML_Char *data);
+extern void XMLCALL forbidden_calls_character_handler(void *userData,
+ const XML_Char *s,
+ int len);
+
+typedef struct {
+ XML_Parser parser;
+ int callCount;
+} ResumeFromHandlerData;
+
+extern void XMLCALL suspend_then_resume_character_handler(void *userData,
+ const XML_Char *s,
+ int len);
+
#endif /* XML_HANDLERS_H */
#ifdef __cplusplus
diff --git a/tests/misc_tests.c b/tests/misc_tests.c
index 015fb87295e6..fb85c1acec28 100644
--- a/tests/misc_tests.c
+++ b/tests/misc_tests.c
@@ -21,6 +21,7 @@
Copyright (c) 2023 Sony Corporation / Snild Dolkow <snild@sony.com>
Copyright (c) 2025 Berkay Eren Ürün <berkay.ueruen@siemens.com>
Copyright (c) 2026 Matthew Fernandez <matthew.fernandez@gmail.com>
+ Copyright (c) 2026 Kartik Kenchi <netliomax25@gmail.com>
Licensed under the MIT license:
Permission is hereby granted, free of charge, to any person obtaining
@@ -47,11 +48,11 @@
# undef NDEBUG /* because test suite relies on assert(...) at the moment */
#endif
+#include "expat_config.h"
+
#include <assert.h>
#include <string.h>
-#include "expat_config.h"
-
#include "expat.h"
#include "internal.h"
#include "minicheck.h"
@@ -213,7 +214,7 @@ START_TEST(test_misc_version) {
if (! versions_equal(&read_version, &parsed_version))
fail("Version mismatch");
- if (xcstrcmp(version_text, XCS("expat_2.8.1"))
+ if (xcstrcmp(version_text, XCS("expat_2.8.2"))
!= 0) /* needs bump on releases */
fail("XML_*_VERSION in expat.h out of sync?\n");
}
@@ -802,6 +803,39 @@ START_TEST(test_misc_no_infinite_loop_issue_1161) {
}
END_TEST
+START_TEST(test_misc_calls_forbidden_from_handlers) {
+ const char *const doc = "<doc>Hello world!</doc>";
+
+ XML_Parser parser = XML_ParserCreate(NULL);
+ XML_UseParserAsHandlerArg(parser);
+ XML_SetCharacterDataHandler(parser, forbidden_calls_character_handler);
+
+ assert_true(XML_Parse(parser, doc, (int)strlen(doc), /*isFinal=*/XML_TRUE)
+ == XML_STATUS_OK);
+
+ XML_ParserFree(parser);
+}
+END_TEST
+
+START_TEST(test_misc_resume_parser_forbidden_from_handler) {
+ const char *const doc = "<doc>Hello world!</doc>";
+
+ XML_Parser parser = XML_ParserCreate(NULL);
+ ResumeFromHandlerData data = {parser, 0};
+ XML_SetUserData(parser, &data);
+ XML_SetCharacterDataHandler(parser, suspend_then_resume_character_handler);
+
+ // The handler suspends the parser, so the top-level parse reports suspension
+ // rather than completion. The handler also asserts that resuming from inside
+ // itself is rejected.
+ assert_true(XML_Parse(parser, doc, (int)strlen(doc), /*isFinal=*/XML_TRUE)
+ == XML_STATUS_SUSPENDED);
+ assert_true(data.callCount == 1);
+
+ XML_ParserFree(parser);
+}
+END_TEST
+
void
make_miscellaneous_test_case(Suite *s) {
TCase *tc_misc = tcase_create("miscellaneous tests");
@@ -833,4 +867,6 @@ make_miscellaneous_test_case(Suite *s) {
tcase_add_test(tc_misc, test_misc_sync_entity_tolerated);
tcase_add_test(tc_misc, test_misc_async_entity_rejected);
tcase_add_test(tc_misc, test_misc_no_infinite_loop_issue_1161);
+ tcase_add_test(tc_misc, test_misc_calls_forbidden_from_handlers);
+ tcase_add_test(tc_misc, test_misc_resume_parser_forbidden_from_handler);
}
diff --git a/xmlwf/filemap.h b/xmlwf/filemap.h
index 5e0c70fa99b6..b4dc9432424f 100644
--- a/xmlwf/filemap.h
+++ b/xmlwf/filemap.h
@@ -10,6 +10,7 @@
Copyright (c) 2000 Clark Cooper <coopercc@users.sourceforge.net>
Copyright (c) 2002 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
Copyright (c) 2016-2017 Sebastian Pipping <sebastian@pipping.org>
+ Copyright (c) 2026 Matthew Fernandez <matthew.fernandez@gmail.com>
Licensed under the MIT license:
Permission is hereby granted, free of charge, to any person obtaining
@@ -36,7 +37,7 @@
#include <stddef.h>
/* The following limit (for XML_Parse's int len) derives from
- * this loop in xmparse.c:
+ * this loop in xmlparse.c:
*
* do {
* bufferSize = (int) (2U * (unsigned) bufferSize);
diff --git a/xmlwf/win32filemap.c b/xmlwf/win32filemap.c
index a2db8eafc43c..a0ed75beb92d 100644
--- a/xmlwf/win32filemap.c
+++ b/xmlwf/win32filemap.c
@@ -9,7 +9,7 @@
Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
Copyright (c) 2000 Clark Cooper <coopercc@users.sourceforge.net>
Copyright (c) 2002 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
- Copyright (c) 2016-2022 Sebastian Pipping <sebastian@pipping.org>
+ Copyright (c) 2016-2017 Sebastian Pipping <sebastian@pipping.org>
Copyright (c) 2022 Martin Ettl <ettl.martin78@googlemail.com>
Licensed under the MIT license:
diff --git a/xmlwf/xmlfile.c b/xmlwf/xmlfile.c
index 912b890ee0a2..21a7073b2943 100644
--- a/xmlwf/xmlfile.c
+++ b/xmlwf/xmlfile.c
@@ -18,6 +18,7 @@
Copyright (c) 2024 Hanno Böck <hanno@gentoo.org>
Copyright (c) 2025 Alfonso Gregory <gfunni234@gmail.com>
Copyright (c) 2026 Matthew Fernandez <matthew.fernandez@gmail.com>
+ Copyright (c) 2026 Kartik Kenchi <netliomax25@gmail.com>
Licensed under the MIT license:
Permission is hereby granted, free of charge, to any person obtaining
@@ -139,7 +140,22 @@ resolveSystemId(const XML_Char *base, const XML_Char *systemId,
#endif
)
return systemId;
- *toFree = malloc((tcslen(base) + tcslen(systemId) + 2) * sizeof(XML_Char));
+
+ const size_t baseLen = tcslen(base);
+ const size_t systemIdLen = tcslen(systemId);
+
+ /* Detect and prevent integer overflow in the addition (without risking
+ underflow) */
+ if (baseLen > SIZE_MAX - systemIdLen || baseLen > SIZE_MAX - systemIdLen - 2)
+ return systemId;
+
+ const size_t charsRequired = baseLen + systemIdLen + 2;
+
+ /* Detect and prevent integer overflow in the multiplication */
+ if (charsRequired > SIZE_MAX / sizeof(XML_Char))
+ return systemId;
+
+ *toFree = malloc(charsRequired * sizeof(XML_Char));
if (! *toFree)
return systemId;
tcscpy(*toFree, base);
diff --git a/xmlwf/xmlwf.c b/xmlwf/xmlwf.c
index dc44c443904d..0374fe6685d7 100644
--- a/xmlwf/xmlwf.c
+++ b/xmlwf/xmlwf.c
@@ -11,7 +11,7 @@
Copyright (c) 2001-2003 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
Copyright (c) 2004-2009 Karl Waclawek <karl@waclawek.net>
Copyright (c) 2005-2007 Steven Solie <steven@solie.ca>
- Copyright (c) 2016-2025 Sebastian Pipping <sebastian@pipping.org>
+ Copyright (c) 2016-2026 Sebastian Pipping <sebastian@pipping.org>
Copyright (c) 2017 Rhodri James <rhodri@wildebeest.org.uk>
Copyright (c) 2019 David Loffredo <loffredo@steptools.com>
Copyright (c) 2020 Joe Orton <jorton@redhat.com>
@@ -21,6 +21,8 @@
Copyright (c) 2022 Sean McBride <sean@rogue-research.com>
Copyright (c) 2025 Alfonso Gregory <gfunni234@gmail.com>
Copyright (c) 2026 Matthew Fernandez <matthew.fernandez@gmail.com>
+ Copyright (c) 2026 Nick Begg <nick@stunttruck.net>
+ Copyright (c) 2026 Kartik Kenchi <netliomax25@gmail.com>
Licensed under the MIT license:
Permission is hereby granted, free of charge, to any person obtaining
@@ -56,6 +58,7 @@
#include "expat.h"
#include "codepage.h"
#include "internal.h" /* for UNUSED_P only */
+#include "fallthrough.h"
#include "xmlfile.h"
#include "xmltchar.h"
@@ -67,6 +70,8 @@
# include <wchar.h>
#endif
+#include "../lib/xcsinc.c"
+
enum ExitCode {
XMLWF_EXIT_SUCCESS = 0,
XMLWF_EXIT_INTERNAL_ERROR = 1,
@@ -305,16 +310,14 @@ processingInstruction(void *userData, const XML_Char *target,
static XML_Char *
xcsdup(const XML_Char *s) {
- XML_Char *result;
- int count = 0;
- size_t numBytes;
+ const size_t count = xcslen(s) + /* null terminator */ 1;
- /* Get the length of the string, including terminator */
- while (s[count++] != 0) {
- /* Do nothing */
- }
- numBytes = count * sizeof(XML_Char);
- result = malloc(numBytes);
+ // Detect and prevent integer overflow
+ if (count > SIZE_MAX / sizeof(XML_Char))
+ return NULL;
+
+ const size_t numBytes = count * sizeof(XML_Char);
+ XML_Char *const result = malloc(numBytes);
if (result == NULL)
return NULL;
memcpy(result, s, numBytes);
@@ -379,13 +382,25 @@ notationCmp(const void *a, const void *b) {
return xcscmp(n1->notationName, n2->notationName);
}
+/* Write a SystemLiteral/PubidLiteral, choosing a delimiter that does not
+ occur in the value. The grammar forbids a literal from containing its
+ own delimiter, so a value reported by Expat never holds both quote
+ characters and a safe delimiter always exists. */
+static void
+writeLiteral(FILE *fp, const XML_Char *value) {
+ const XML_Char quote = (tcschr(value, T('\'')) != NULL) ? T('"') : T('\'');
+ puttc(quote, fp);
+ fputts(value, fp);
+ puttc(quote, fp);
+}
+
static void XMLCALL
endDoctypeDecl(void *userData) {
XmlwfUserData *data = userData;
NotationList **notations;
- int notationCount = 0;
+ size_t notationCount = 0;
NotationList *p;
- int i;
+ size_t i;
/* How many notations do we have? */
for (p = data->notationListHead; p != NULL; p = p->next)
@@ -395,6 +410,13 @@ endDoctypeDecl(void *userData) {
goto cleanUp;
}
+ /* Detect and prevent integer overflow in the multiplication, mirroring
+ the guards in xcsdup() and resolveSystemId() */
+ if (notationCount > SIZE_MAX / sizeof(NotationList *)) {
+ fprintf(stderr, "Unable to sort notations");
+ goto cleanUp;
+ }
+
notations = malloc(notationCount * sizeof(NotationList *));
if (notations == NULL) {
fprintf(stderr, "Unable to sort notations");
@@ -416,19 +438,15 @@ endDoctypeDecl(void *userData) {
fputts(T("<!NOTATION "), data->fp);
fputts(notations[i]->notationName, data->fp);
if (notations[i]->publicId != NULL) {
- fputts(T(" PUBLIC '"), data->fp);
- fputts(notations[i]->publicId, data->fp);
- puttc(T('\''), data->fp);
+ fputts(T(" PUBLIC "), data->fp);
+ writeLiteral(data->fp, notations[i]->publicId);
if (notations[i]->systemId != NULL) {
puttc(T(' '), data->fp);
- puttc(T('\''), data->fp);
- fputts(notations[i]->systemId, data->fp);
- puttc(T('\''), data->fp);
+ writeLiteral(data->fp, notations[i]->systemId);
}
} else if (notations[i]->systemId != NULL) {
- fputts(T(" SYSTEM '"), data->fp);
- fputts(notations[i]->systemId, data->fp);
- puttc(T('\''), data->fp);
+ fputts(T(" SYSTEM "), data->fp);
+ writeLiteral(data->fp, notations[i]->systemId);
}
puttc(T('>'), data->fp);
puttc(T('\n'), data->fp);
@@ -561,8 +579,11 @@ static void
metaLocation(XML_Parser parser) {
const XML_Char *uri = XML_GetBase(parser);
FILE *fp = ((XmlwfUserData *)XML_GetUserData(parser))->fp;
- if (uri)
- ftprintf(fp, T(" uri=\"%s\""), uri);
+ if (uri) {
+ fputts(T(" uri=\""), fp);
+ characterData(XML_GetUserData(parser), uri, (int)tcslen(uri));
+ puttc(T('"'), fp);
+ }
ftprintf(fp,
T(" byte=\"%") T(XML_FMT_INT_MOD) T("d\"") T(" nbytes=\"%d\"")
T(" line=\"%") T(XML_FMT_INT_MOD) T("u\"") T(" col=\"%")
@@ -596,12 +617,16 @@ metaStartElement(void *userData, const XML_Char *name, const XML_Char **atts) {
else
idAttPtr = atts + idAttIndex;
- ftprintf(fp, T("<starttag name=\"%s\""), name);
+ fputts(T("<starttag name=\""), fp);
+ characterData(data, name, (int)tcslen(name));
+ puttc(T('"'), fp);
metaLocation(parser);
if (*atts) {
fputts(T(">\n"), fp);
do {
- ftprintf(fp, T("<attribute name=\"%s\" value=\""), atts[0]);
+ fputts(T("<attribute name=\""), fp);
+ characterData(data, atts[0], (int)tcslen(atts[0]));
+ fputts(T("\" value=\""), fp);
characterData(data, atts[1], (int)tcslen(atts[1]));
if (atts >= specifiedAttsEnd)
fputts(T("\" defaulted=\"yes\"/>\n"), fp);
@@ -620,7 +645,9 @@ metaEndElement(void *userData, const XML_Char *name) {
XML_Parser parser = userData;
XmlwfUserData *data = XML_GetUserData(parser);
FILE *fp = data->fp;
- ftprintf(fp, T("<endtag name=\"%s\""), name);
+ fputts(T("<endtag name=\""), fp);
+ characterData(data, name, (int)tcslen(name));
+ puttc(T('"'), fp);
metaLocation(parser);
fputts(T("/>\n"), fp);
}
@@ -929,7 +956,7 @@ usage(const XML_Char *prog, int rc) {
T("environment variables:\n")
T(" EXPAT_ACCOUNTING_DEBUG=(0|1|2|3)\n")
T(" Control verbosity of accounting debugging (default: 0)\n")
- T(" EXPAT_ENTITY_DEBUG=(0|1)\n")
+ T(" EXPAT_ENTITY_DEBUG=(0|1|2)\n")
T(" Control verbosity of entity debugging (default: 0)\n")
T(" EXPAT_ENTROPY_DEBUG=(0|1)\n")
T(" Control verbosity of entropy debugging (default: 0)\n")
@@ -1034,7 +1061,7 @@ tmain(int argc, XML_Char **argv) {
break;
case T('p'):
paramEntityParsing = XML_PARAM_ENTITY_PARSING_ALWAYS;
- /* fall through */
+ EXPAT_FALLTHROUGH;
case T('x'):
processFlags |= XML_EXTERNAL_ENTITIES;
j++;
@@ -1152,7 +1179,7 @@ tmain(int argc, XML_Char **argv) {
j = 0;
break;
}
- /* fall through */
+ EXPAT_FALLTHROUGH;
default:
usage(argv[0], XMLWF_EXIT_USAGE_ERROR);
// usage called exit(..), never gets here
@@ -1234,8 +1261,26 @@ tmain(int argc, XML_Char **argv) {
}
#endif
}
- outName
- = malloc((tcslen(outputDir) + tcslen(file) + 2) * sizeof(XML_Char));
+ const size_t outputDirLen = tcslen(outputDir);
+ const size_t fileLen = tcslen(file);
+
+ /* Detect and prevent integer overflow in the addition (without
+ risking underflow) and the multiplication, mirroring the guards
+ in xcsdup() and resolveSystemId() */
+ if (outputDirLen > SIZE_MAX - fileLen
+ || outputDirLen > SIZE_MAX - fileLen - 2) {
+ tperror(T("Could not allocate memory"));
+ exit(XMLWF_EXIT_INTERNAL_ERROR);
+ }
+
+ const size_t charsRequired = outputDirLen + fileLen + 2;
+
+ if (charsRequired > SIZE_MAX / sizeof(XML_Char)) {
+ tperror(T("Could not allocate memory"));
+ exit(XMLWF_EXIT_INTERNAL_ERROR);
+ }
+
+ outName = malloc(charsRequired * sizeof(XML_Char));
if (! outName) {
tperror(T("Could not allocate memory"));
exit(XMLWF_EXIT_INTERNAL_ERROR);
diff --git a/xmlwf/xmlwf_helpgen.py b/xmlwf/xmlwf_helpgen.py
index be41d59f0a8c..18df46dd65c8 100755
--- a/xmlwf/xmlwf_helpgen.py
+++ b/xmlwf/xmlwf_helpgen.py
@@ -37,7 +37,7 @@ epilog = dedent(
environment variables:
EXPAT_ACCOUNTING_DEBUG=(0|1|2|3)
Control verbosity of accounting debugging (default: 0)
- EXPAT_ENTITY_DEBUG=(0|1)
+ EXPAT_ENTITY_DEBUG=(0|1|2)
Control verbosity of entity debugging (default: 0)
EXPAT_ENTROPY_DEBUG=(0|1)
Control verbosity of entropy debugging (default: 0)