Skip to content

Commit 6c70195

Browse files
chore(deps): update bundled expat 2.8.1 -> 2.8.2 (13 CVEs) (#5396)
* chore(deps): update bundled expat 2.8.1 -> 2.8.2 (13 CVEs) Security fixes from upstream (Expat 2.8.2), all CVE-2026: Integer overflows (9): - CVE-2026-56403, CVE-2026-56404, CVE-2026-56405, CVE-2026-56406, CVE-2026-56407, CVE-2026-56408, CVE-2026-56409, CVE-2026-56410, CVE-2026-56411 Missing control flow integrity checks (3): - CVE-2026-50219, CVE-2026-56131, CVE-2026-56412 Out-of-bounds write (1): - CVE-2026-56132 The integer overflows land in xmlparse.c; the control-flow-integrity and bounds checks touch xmlparse.c, xmltok.c and xmltok_impl.c. Other upstream changes carried in: - New helper sources added to the bundle: fallthrough.h, memory_sanitizer.h and xcsinc.c (the latter is #included from the char-type variants, not a standalone translation unit). - errno-propagation and siphash.h cleanups; minor random_*.c churn. - Library VERSION/SOVERSION moved from 1.12.1 to 1.12.2 (LIBCURRENT 13 / LIBREVISION 2 / LIBAGE 12). Bundled-build adjustments: - dependencies/expat/CMakeLists.txt tracks upstream's reworked lib/CMakeLists.txt; Poco's portable expat_config.h is preserved (the update did not overwrite it). - dependencies/README.md dependency table: 2.8.1 -> 2.8.2. Ref: https://blog.hartwork.org/posts/expat-2-8-2-released/ Verified: XML-testrunner -all passes (138 tests) on Windows x64 (MSVC 19.44, VS 2022) in a minimal cmake build (POCO_MINIMAL_BUILD + ENABLE_XML + ENABLE_TESTS). * fix(XML): abort parse via XML_StopParser instead of throwing through Expat callbacks A C++ exception unwinding through Expat 2.8.2 leaves the parser marked in-handler, turning XML_ParserFree() into a silent no-op (memory leak). #5395 * fix(XML): harden ParserEngine handler exception capture Never drop a captured exception if XML_StopParser() failed; capture exceptions from the unknown-encoding handler; test throwing handlers. Co-authored-by: Matej Kenda <matejken@gmail.com> * chore(codeql): derive throwing test handler from DefaultHandler Removes raw-array parameters flagged by cpp/array-in-interface. --------- Co-authored-by: Matej Kenda <matejken@gmail.com>
1 parent c594b20 commit 6c70195

18 files changed

Lines changed: 1230 additions & 666 deletions

XML/src/ParserEngine.cpp

Lines changed: 273 additions & 87 deletions
Large diffs are not rendered by default.

XML/src/ParserEngine.h

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
//
88
// Definition of the ParseEngine class.
99
//
10-
// Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH.
10+
// Copyright (c) 2004-2026, Applied Informatics Software Engineering GmbH.
1111
// and Contributors.
1212
//
1313
// SPDX-License-Identifier: BSL-1.0
@@ -23,6 +23,7 @@
2323
#include "Poco/SAX/Locator.h"
2424
#include "Poco/TextEncoding.h"
2525
#include <expat.h>
26+
#include <exception>
2627
#include <map>
2728
#include <vector>
2829

@@ -220,6 +221,33 @@ class XML_API ParserEngine: public Locator
220221
/// Throws an XMLException with a message corresponding
221222
/// to the given Expat error code.
222223

224+
void checkError(XML_Parser parser);
225+
/// Called after a failed XML_Parse()/XML_ParseBuffer() call.
226+
/// If a C++ exception thrown from within a handler has been
227+
/// captured by abortParse(), rethrows it (and clears it);
228+
/// otherwise translates the parser's current Expat error code
229+
/// into an exception via handleError().
230+
231+
void abortParse(XML_Parser parser, std::exception_ptr ex);
232+
/// Called from an Expat callback when invoking application code
233+
/// (a SAX/DTD/lexical handler or entity resolver) throws a C++
234+
/// exception. Expat is a C library and does not support C++
235+
/// exceptions unwinding through its call stack -- doing so skips
236+
/// Expat's internal handler-call-depth bookkeeping and leaves the
237+
/// parser (and everything it owns) permanently leaked, since Expat
238+
/// then refuses to free a parser it believes is still inside a
239+
/// handler call.
240+
///
241+
/// Instead, the exception is captured here and the parser is
242+
/// stopped gracefully via XML_StopParser(), which Expat fully
243+
/// supports from within a handler. Once XML_Parse() returns,
244+
/// checkError() rethrows the captured exception.
245+
246+
XML_Parser currentParser() const;
247+
/// Returns the Expat parser instance (the main parser, or the
248+
/// external entity sub-parser currently being parsed) that is
249+
/// active for the top-most entry of the context stack.
250+
223251
void parseExternal(XML_Parser extParser, InputSource* pInputSource);
224252
/// Parse an XML document from the given InputSource.
225253

@@ -290,6 +318,8 @@ class XML_API ParserEngine: public Locator
290318
LexicalHandler* _pLexicalHandler;
291319
ErrorHandler* _pErrorHandler;
292320

321+
std::exception_ptr _exception;
322+
293323
float _maximumAmplificationFactor;
294324
Poco::UInt64 _activationThresholdBytes;
295325

XML/testsuite/src/SAXParserTest.cpp

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,14 @@
1414
#include "Poco/SAX/SAXParser.h"
1515
#include "Poco/SAX/InputSource.h"
1616
#include "Poco/SAX/EntityResolver.h"
17+
#include "Poco/SAX/DefaultHandler.h"
1718
#include "Poco/SAX/SAXException.h"
1819
#include "Poco/SAX/WhitespaceFilter.h"
1920
#include "Poco/XML/XMLWriter.h"
2021
#include "Poco/Latin9Encoding.h"
2122
#include "Poco/FileStream.h"
2223
#include <sstream>
24+
#include <stdexcept>
2325

2426

2527
using Poco::XML::SAXParser;
@@ -28,8 +30,11 @@ using Poco::XML::XMLReader;
2830
using Poco::XML::InputSource;
2931
using Poco::XML::EntityResolver;
3032
using Poco::XML::XMLString;
33+
using Poco::XML::XMLException;
3134
using Poco::XML::SAXParseException;
3235
using Poco::XML::WhitespaceFilter;
36+
using Poco::XML::DefaultHandler;
37+
using Poco::XML::Attributes;
3338

3439

3540
class TestEntityResolver: public EntityResolver
@@ -62,6 +67,32 @@ class TestEntityResolver: public EntityResolver
6267
};
6368

6469

70+
class ThrowingHandler: public DefaultHandler
71+
{
72+
public:
73+
enum Kind
74+
{
75+
THROW_STD,
76+
THROW_XML
77+
};
78+
79+
ThrowingHandler(Kind kind): _kind(kind)
80+
{
81+
}
82+
83+
void startElement(const XMLString& uri, const XMLString& localName, const XMLString& qname, const Attributes& attrList)
84+
{
85+
if (_kind == THROW_STD)
86+
throw std::runtime_error("handler failure");
87+
else
88+
throw XMLException("handler failure");
89+
}
90+
91+
private:
92+
Kind _kind;
93+
};
94+
95+
6596
SAXParserTest::SAXParserTest(const std::string& name): CppUnit::TestCase(name)
6697
{
6798
}
@@ -313,6 +344,38 @@ void SAXParserTest::testParsePartialReads()
313344
}
314345

315346

347+
void SAXParserTest::testContentHandlerThrows()
348+
{
349+
SAXParser parser;
350+
351+
ThrowingHandler stdHandler(ThrowingHandler::THROW_STD);
352+
parser.setContentHandler(&stdHandler);
353+
try
354+
{
355+
parser.parseString("<root/>");
356+
fail("must throw");
357+
}
358+
catch (const std::runtime_error& exc)
359+
{
360+
assertTrue (std::string(exc.what()) == "handler failure");
361+
}
362+
363+
ThrowingHandler xmlHandler(ThrowingHandler::THROW_XML);
364+
parser.setContentHandler(&xmlHandler);
365+
try
366+
{
367+
parser.parseString("<root/>");
368+
fail("must throw");
369+
}
370+
catch (const SAXParseException&)
371+
{
372+
}
373+
374+
parser.setContentHandler(nullptr);
375+
parser.parseString(SIMPLE1);
376+
}
377+
378+
316379
void SAXParserTest::setUp()
317380
{
318381
}
@@ -378,6 +441,7 @@ CppUnit::Test* SAXParserTest::suite()
378441
CppUnit_addTest(pSuite, SAXParserTest, testCharacters);
379442
CppUnit_addTest(pSuite, SAXParserTest, testParseMemory);
380443
CppUnit_addTest(pSuite, SAXParserTest, testParsePartialReads);
444+
CppUnit_addTest(pSuite, SAXParserTest, testContentHandlerThrows);
381445

382446
return pSuite;
383447
}

XML/testsuite/src/SAXParserTest.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ class SAXParserTest: public CppUnit::TestCase
4848
void testParseMemory();
4949
void testCharacters();
5050
void testParsePartialReads();
51+
void testContentHandlerThrows();
5152

5253
void setUp();
5354
void tearDown();

dependencies/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ are replaced with system-installed versions via `find_package()` or `-l` flags.
1414
| [zlib](zlib/) | 1.3.2 | Zlib | Foundation (compression/decompression streams) | Yes | https://github.com/madler/zlib |
1515
| [pcre2](pcre2/) | 10.47 | BSD-3-Clause | Foundation (regular expressions) | Yes | https://github.com/PCRE2Project/pcre2 |
1616
| [utf8proc](utf8proc/) | 2.11.3 | MIT | Foundation (Unicode normalization) | Yes | https://github.com/JuliaStrings/utf8proc |
17-
| [expat](expat/) | 2.8.1 | MIT | XML (SAX/DOM parser) | Yes | https://github.com/libexpat/libexpat |
17+
| [expat](expat/) | 2.8.2 | MIT | XML (SAX/DOM parser) | Yes | https://github.com/libexpat/libexpat |
1818
| [sqlite3](sqlite3/) | 3.53.1 | Public Domain | Data/SQLite (embedded database) | Yes | https://www.sqlite.org |
1919
| [png](png/) | 1.6.58 | libpng License | PDF (PNG image support) | Yes | https://github.com/pnggroup/libpng |
2020
| [v8_double_conversion](v8_double_conversion/) | 3.4.0 | BSD-3-Clause | Foundation (float-to-string conversion) | No | https://github.com/google/double-conversion |

dependencies/expat/CMakeLists.txt

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Expat XML parser library
2-
# Version: 2.8.1
3-
# Source: https://github.com/libexpat/libexpat/releases/tag/R_2_8_1
2+
# Version: 2.8.2
3+
# Source: https://github.com/libexpat/libexpat/releases/tag/R_2_8_2
44

55
if(POCO_UNBUNDLED)
66
if (ENABLE_XML)
@@ -11,8 +11,14 @@ else()
1111
# Core sources -- exclude the per-entropy-source random_*.c files;
1212
# those are added conditionally below to mirror the upstream
1313
# CMakeLists gating (see expat-2.8.1/CMakeLists.txt).
14+
# Also exclude xcsinc.c: unlike xmltok_impl.c/xmltok_ns.c (which guard
15+
# themselves with #ifdef XML_TOK_IMPL_C/XML_TOK_NS_C and compile to an
16+
# empty translation unit standalone), xcsinc.c has no include guard and
17+
# is only meant to be #include-d from xmlparse.c; compiling it as its
18+
# own translation unit fails since XML_Char is undefined.
1419
file(GLOB SRCS_G "src/*.c")
1520
list(FILTER SRCS_G EXCLUDE REGEX "/random_.*\\.c$")
21+
list(FILTER SRCS_G EXCLUDE REGEX "/xcsinc\\.c$")
1622
POCO_SOURCES(SRCS expat ${SRCS_G})
1723

1824
# Headers

dependencies/expat/src/expat.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1094,7 +1094,7 @@ XML_SetReparseDeferralEnabled(XML_Parser parser, XML_Bool enabled);
10941094
*/
10951095
# define XML_MAJOR_VERSION 2
10961096
# define XML_MINOR_VERSION 8
1097-
# define XML_MICRO_VERSION 1
1097+
# define XML_MICRO_VERSION 2
10981098

10991099
# ifdef __cplusplus
11001100
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
__ __ _
3+
___\ \/ /_ __ __ _| |_
4+
/ _ \\ /| '_ \ / _` | __|
5+
| __// \| |_) | (_| | |_
6+
\___/_/\_\ .__/ \__,_|\__|
7+
|_| XML parser
8+
9+
Copyright (c) 2026 Nick Begg <nick@stunttruck.net>
10+
Licensed under the MIT license:
11+
12+
Permission is hereby granted, free of charge, to any person obtaining
13+
a copy of this software and associated documentation files (the
14+
"Software"), to deal in the Software without restriction, including
15+
without limitation the rights to use, copy, modify, merge, publish,
16+
distribute, sublicense, and/or sell copies of the Software, and to permit
17+
persons to whom the Software is furnished to do so, subject to the
18+
following conditions:
19+
20+
The above copyright notice and this permission notice shall be included
21+
in all copies or substantial portions of the Software.
22+
23+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
26+
NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
27+
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
28+
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
29+
USE OR OTHER DEALINGS IN THE SOFTWARE.
30+
*/
31+
32+
#ifndef FALLTHROUGH_H
33+
# define FALLTHROUGH_H 1
34+
35+
// Explicit fallthrough in switch case to avoid warnings
36+
// with compiler flag -Wimplicit-fallthrough.
37+
38+
# define EXPAT_FALLTHROUGH \
39+
do { \
40+
} while (0)
41+
42+
# if defined(__has_attribute)
43+
# if __has_attribute(fallthrough)
44+
# undef EXPAT_FALLTHROUGH
45+
# define EXPAT_FALLTHROUGH __attribute__((fallthrough))
46+
# endif
47+
# endif
48+
49+
#endif // FALLTHROUGH_H
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
__ __ _
3+
___\ \/ /_ __ __ _| |_
4+
/ _ \\ /| '_ \ / _` | __|
5+
| __// \| |_) | (_| | |_
6+
\___/_/\_\ .__/ \__,_|\__|
7+
|_| XML parser
8+
9+
Copyright (c) 2026 Matthew Fernandez <matthew.fernandez@gmail.com>
10+
Licensed under the MIT license:
11+
12+
Permission is hereby granted, free of charge, to any person obtaining
13+
a copy of this software and associated documentation files (the
14+
"Software"), to deal in the Software without restriction, including
15+
without limitation the rights to use, copy, modify, merge, publish,
16+
distribute, sublicense, and/or sell copies of the Software, and to permit
17+
persons to whom the Software is furnished to do so, subject to the
18+
following conditions:
19+
20+
The above copyright notice and this permission notice shall be included
21+
in all copies or substantial portions of the Software.
22+
23+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
26+
NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
27+
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
28+
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
29+
USE OR OTHER DEALINGS IN THE SOFTWARE.
30+
*/
31+
32+
#if ! defined(MEMORY_SANITIZER_H)
33+
# define MEMORY_SANITIZER_H 1
34+
35+
# if defined(__has_feature)
36+
# if __has_feature(memory_sanitizer)
37+
# include <sanitizer/msan_interface.h>
38+
39+
// inform Memory Sanitizer that [base, base + extent) is now initialized
40+
# define MSAN_UNPOISON(base, extent) __msan_unpoison((base), (extent))
41+
42+
# endif
43+
# endif
44+
45+
# if ! defined(MSAN_UNPOISON)
46+
# define MSAN_UNPOISON(base, extent) \
47+
do { \
48+
} while (0)
49+
# endif
50+
51+
#endif // ! defined(MEMORY_SANITIZER_H)

dependencies/expat/src/random_arc4random_buf.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
|_| XML parser
88
99
Copyright (c) 2026 Sebastian Pipping <sebastian@pipping.org>
10+
Copyright (c) 2026 Matthew Fernandez <matthew.fernandez@gmail.com>
1011
Licensed under the MIT license:
1112
1213
Permission is hereby granted, free of charge, to any person obtaining
@@ -35,9 +36,12 @@
3536
# define _DEFAULT_SOURCE 1 /* for glibc */
3637
#endif
3738

39+
#include "memory_sanitizer.h"
3840
#include <stdlib.h> // for arc4random_buf
3941

4042
void
4143
writeRandomBytes_arc4random_buf(void *target, size_t count) {
4244
arc4random_buf(target, count);
45+
// MSan does not understand `arc4random_buf`, so explain its effects
46+
MSAN_UNPOISON(target, count);
4347
}

0 commit comments

Comments
 (0)