!! THANK YOU! Sebastian Pipping -- Berlin, 2024-03-09 !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+Release 2.6.3 Wed September 4 2024
+ Security fixes:
+ #887 #890 CVE-2024-45490 -- Calling function XML_ParseBuffer with
+ len < 0 without noticing and then calling XML_GetBuffer
+ will have XML_ParseBuffer fail to recognize the problem
+ and XML_GetBuffer corrupt memory.
+ With the fix, XML_ParseBuffer now complains with error
+ XML_ERROR_INVALID_ARGUMENT just like sibling XML_Parse
+ has been doing since Expat 2.2.1, and now documented.
+ Impact is denial of service to potentially artitrary code
+ execution.
+ #888 #891 CVE-2024-45491 -- Internal function dtdCopy can have an
+ integer overflow for nDefaultAtts on 32-bit platforms
+ (where UINT_MAX equals SIZE_MAX).
+ Impact is denial of service to potentially artitrary code
+ execution.
+ #889 #892 CVE-2024-45492 -- Internal function nextScaffoldPart can
+ have an integer overflow for m_groupSize on 32-bit
+ platforms (where UINT_MAX equals SIZE_MAX).
+ Impact is denial of service to potentially artitrary code
+ execution.
+
+ Other changes:
+ #851 #879 Autotools: Sync CMake templates with CMake 3.28
+ #853 Autotools: Always provide path to find(1) for portability
+ #861 Autotools: Ensure that the m4 directory always exists.
+ #870 Autotools: Simplify handling of SIZEOF_VOID_P
+ #869 Autotools: Support non-GNU sed
+ #856 Autotools|CMake: Fix main() to main(void)
+ #865 Autotools|CMake: Fix compile tests for HAVE_SYSCALL_GETRANDOM
+ #863 Autotools|CMake: Stop requiring dos2unix
+ #854 #855 CMake: Fix check for symbols size_t and off_t
+ #864 docs|tests: Convert README to Markdown and update
+ #741 Windows: Drop support for Visual Studio <=15.0/2017
+ #886 Drop needless XML_DTD guards around is_param access
+ #885 Fix typo in a code comment
+ #894 #896 Version info bumped from 10:2:9 (libexpat*.so.1.9.2)
+ to 10:3:9 (libexpat*.so.1.9.3); see https://verbump.de/
+ for what these numbers do
+
+ Infrastructure:
+ #880 Readme: Promote the call for help
+ #868 CI: Fix various issues
+ #849 CI: Allow triggering GitHub Actions workflows manually
+ #851 #872 ..
+ #873 #879 CI: Adapt to breaking changes in GitHub Actions
+
+ Special thanks to:
+ Alexander Bluhm
+ Berkay Eren Ürün
+ Dag-Erling Smørgrav
+ Ferenc Géczi
+ TaiYou
+
Release 2.6.2 Wed March 13 2024
Security fixes:
#839 #842 CVE-2024-28757 -- Prevent billion laughs attacks with
[![Downloads SourceForge](https://img.shields.io/sourceforge/dt/expat?label=Downloads%20SourceForge)](https://sourceforge.net/projects/expat/files/)
[![Downloads GitHub](https://img.shields.io/github/downloads/libexpat/libexpat/total?label=Downloads%20GitHub)](https://github.com/libexpat/libexpat/releases)
+> [!CAUTION]
+>
+> Expat is **understaffed** and without funding.
+> There is a [call for help with details](https://github.com/libexpat/libexpat/blob/master/expat/Changes)
+> at the top of the `Changes` file.
-# Expat, Release 2.6.2
+
+# Expat, Release 2.6.3
This is Expat, a C99 library for parsing
[XML 1.0 Fourth Edition](https://www.w3.org/TR/2006/REC-xml-20060816/), started by
- GNU GCC >=4.5
- LLVM Clang >=3.5
-- Microsoft Visual Studio >=15.0/2017 (rolling `${today} minus 5 years`)
+- Microsoft Visual Studio >=16.0/2019 (rolling `${today} minus 5 years`)
Windows users can use the
[`expat-win32bin-*.*.*.{exe,zip}` download](https://github.com/libexpat/libexpat/releases),
1. Mass-patch `Makefile.am` files to use `libexpatw.la` for a library name:
<br/>
- `find -name Makefile.am -exec sed
+ `find . -name Makefile.am -exec sed
-e 's,libexpat\.la,libexpatw.la,'
-e 's,libexpat_la,libexpatw_la,'
- -i {} +`
+ -i.bak {} +`
1. Run `automake` to re-write `Makefile.in` files:<br/>
`automake`
<div>
<h1>
The Expat XML Parser
- <small>Release 2.6.2</small>
+ <small>Release 2.6.3</small>
</h1>
</div>
<div class="content">
Developer Studio installed,
you can use CMake to generate a <code>.sln</code> file, e.g.
<code>
-cmake -G"Visual Studio 15 2017" -DCMAKE_BUILD_TYPE=RelWithDebInfo .
+cmake -G"Visual Studio 16 2019" -DCMAKE_BUILD_TYPE=RelWithDebInfo .
</code>, and build Expat using <code>msbuild /m expat.sln</code> after.</p>
<p>Alternatively, you may download the Win32 binary package that
that are part of the document is indicated by <code>len</code>. This means
that <code>s</code> doesn't have to be null-terminated. It also means that
if <code>len</code> is larger than the number of bytes in the block of
-memory that <code>s</code> points at, then a memory fault is likely. The
+memory that <code>s</code> points at, then a memory fault is likely.
+Negative values for <code>len</code> are rejected since Expat 2.2.1.
+The
<code>isFinal</code> parameter informs the parser that this is the last
piece of the document. Frequently, the last piece is empty (i.e.
<code>len</code> is zero.)
int isFinal);
</pre>
<div class="fcndef">
+<p>
This is just like <code><a href= "#XML_Parse" >XML_Parse</a></code>,
except in this case Expat provides the buffer. By obtaining the
buffer from Expat with the <code><a href= "#XML_GetBuffer"
>XML_GetBuffer</a></code> function, the application can avoid double
copying of the input.
+</p>
+
+<p>
+Negative values for <code>len</code> are rejected since Expat 2.6.3.
+</p>
</div>
<h4 id="XML_GetBuffer">XML_GetBuffer</h4>
*/
#define XML_MAJOR_VERSION 2
#define XML_MINOR_VERSION 6
-#define XML_MICRO_VERSION 2
+#define XML_MICRO_VERSION 3
#ifdef __cplusplus
}
| ((uint64_t)((p)[4]) << 32) | ((uint64_t)((p)[5]) << 40) \
| ((uint64_t)((p)[6]) << 48) | ((uint64_t)((p)[7]) << 56))
-#define SIPHASH_INITIALIZER \
- { 0, 0, 0, 0, {0}, 0, 0 }
+#define SIPHASH_INITIALIZER {0, 0, 0, 0, {0}, 0, 0}
struct siphash {
uint64_t v0, v1, v2, v3;
-/* 2a14271ad4d35e82bde8ba210b4edb7998794bcbae54deab114046a300f9639a (2.6.2+)
+/* ba4cdf9bdb534f355a9def4c9e25d20ee8e72f95b0a4d930be52e563f5080196 (2.6.3+)
__ __ _
___\ \/ /_ __ __ _| |_
/ _ \\ /| '_ \ / _` | __|
Copyright (c) 2022 Sean McBride <sean@rogue-research.com>
Copyright (c) 2023 Owain Davies <owaind@bath.edu>
Copyright (c) 2023-2024 Sony Corporation / Snild Dolkow <snild@sony.com>
+ Copyright (c) 2024 Berkay Eren Ürün <berkay.ueruen@siemens.com>
Licensed under the MIT license:
Permission is hereby granted, free of charge, to any person obtaining
The name of the element is stored in both the document and API
encodings. The memory buffer 'buf' is a separately-allocated
memory area which stores the name. During the XML_Parse()/
- XMLParseBuffer() when the element is open, the memory for the 'raw'
+ XML_ParseBuffer() when the element is open, the memory for the 'raw'
version of the name (in the document encoding) is shared with the
document buffer. If the element is open across calls to
XML_Parse()/XML_ParseBuffer(), the buffer is re-allocated to
if (parser == NULL)
return XML_STATUS_ERROR;
+
+ if (len < 0) {
+ parser->m_errorCode = XML_ERROR_INVALID_ARGUMENT;
+ return XML_STATUS_ERROR;
+ }
+
switch (parser->m_parsingStatus.parsing) {
case XML_SUSPENDED:
parser->m_errorCode = XML_ERROR_SUSPENDED;
/* Set a safe default value in case 'next' does not get set */
next = textStart;
-#ifdef XML_DTD
if (entity->is_param) {
int tok
= XmlPrologTok(parser->m_internalEncoding, textStart, textEnd, &next);
result = doProlog(parser, parser->m_internalEncoding, textStart, textEnd,
tok, next, &next, XML_FALSE, XML_FALSE,
XML_ACCOUNT_ENTITY_EXPANSION);
- } else
-#endif /* XML_DTD */
+ } else {
result = doContent(parser, parser->m_tagLevel, parser->m_internalEncoding,
textStart, textEnd, &next, XML_FALSE,
XML_ACCOUNT_ENTITY_EXPANSION);
+ }
if (result == XML_ERROR_NONE) {
if (textEnd != next && parser->m_parsingStatus.parsing == XML_SUSPENDED) {
/* Set a safe default value in case 'next' does not get set */
next = textStart;
-#ifdef XML_DTD
if (entity->is_param) {
int tok
= XmlPrologTok(parser->m_internalEncoding, textStart, textEnd, &next);
result = doProlog(parser, parser->m_internalEncoding, textStart, textEnd,
tok, next, &next, XML_FALSE, XML_TRUE,
XML_ACCOUNT_ENTITY_EXPANSION);
- } else
-#endif /* XML_DTD */
+ } else {
result = doContent(parser, openEntity->startTagLevel,
parser->m_internalEncoding, textStart, textEnd, &next,
XML_FALSE, XML_ACCOUNT_ENTITY_EXPANSION);
+ }
if (result != XML_ERROR_NONE)
return result;
return XML_ERROR_NONE;
}
-#ifdef XML_DTD
if (entity->is_param) {
int tok;
parser->m_processor = prologProcessor;
return doProlog(parser, parser->m_encoding, s, end, tok, next, nextPtr,
(XML_Bool)! parser->m_parsingStatus.finalBuffer, XML_TRUE,
XML_ACCOUNT_DIRECT);
- } else
-#endif /* XML_DTD */
- {
+ } else {
parser->m_processor = contentProcessor;
/* see externalEntityContentProcessor vs contentProcessor */
result = doContent(parser, parser->m_parentParser ? 1 : 0,
if (! newE)
return 0;
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_t)(-1) / sizeof(DEFAULT_ATTRIBUTE))) {
+ return 0;
+ }
+#endif
newE->defaultAtts
= ms->malloc_fcn(oldE->nDefaultAtts * sizeof(DEFAULT_ATTRIBUTE));
if (! newE->defaultAtts) {
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
+ if (parser->m_groupSize > ((size_t)(-1) / sizeof(int))) {
+ return -1;
+ }
+#endif
dtd->scaffIndex = (int *)MALLOC(parser, parser->m_groupSize * sizeof(int));
if (! dtd->scaffIndex)
return -1;
}
END_TEST
+/* Test XML_Parse for len < 0 */
+START_TEST(test_negative_len_parse) {
+ const char *const doc = "<root/>";
+ for (int isFinal = 0; isFinal < 2; isFinal++) {
+ set_subtest("isFinal=%d", isFinal);
+
+ XML_Parser parser = XML_ParserCreate(NULL);
+
+ if (XML_GetErrorCode(parser) != XML_ERROR_NONE)
+ fail("There was not supposed to be any initial parse error.");
+
+ const enum XML_Status status = XML_Parse(parser, doc, -1, isFinal);
+
+ if (status != XML_STATUS_ERROR)
+ fail("Negative len was expected to fail the parse but did not.");
+
+ if (XML_GetErrorCode(parser) != XML_ERROR_INVALID_ARGUMENT)
+ fail("Parse error does not match XML_ERROR_INVALID_ARGUMENT.");
+
+ XML_ParserFree(parser);
+ }
+}
+END_TEST
+
+/* Test XML_ParseBuffer for len < 0 */
+START_TEST(test_negative_len_parse_buffer) {
+ const char *const doc = "<root/>";
+ for (int isFinal = 0; isFinal < 2; isFinal++) {
+ set_subtest("isFinal=%d", isFinal);
+
+ XML_Parser parser = XML_ParserCreate(NULL);
+
+ if (XML_GetErrorCode(parser) != XML_ERROR_NONE)
+ fail("There was not supposed to be any initial parse error.");
+
+ void *const buffer = XML_GetBuffer(parser, (int)strlen(doc));
+
+ if (buffer == NULL)
+ fail("XML_GetBuffer failed.");
+
+ memcpy(buffer, doc, strlen(doc));
+
+ const enum XML_Status status = XML_ParseBuffer(parser, -1, isFinal);
+
+ if (status != XML_STATUS_ERROR)
+ fail("Negative len was expected to fail the parse but did not.");
+
+ if (XML_GetErrorCode(parser) != XML_ERROR_INVALID_ARGUMENT)
+ fail("Parse error does not match XML_ERROR_INVALID_ARGUMENT.");
+
+ XML_ParserFree(parser);
+ }
+}
+END_TEST
+
/* Test odd corners of the XML_GetBuffer interface */
static enum XML_Status
get_feature(enum XML_FeatureEnum feature_id, long *presult) {
tcase_add_test__ifdef_xml_dtd(tc_basic, test_user_parameters);
tcase_add_test__ifdef_xml_dtd(tc_basic, test_ext_entity_ref_parameter);
tcase_add_test(tc_basic, test_empty_parse);
+ tcase_add_test(tc_basic, test_negative_len_parse);
+ tcase_add_test(tc_basic, test_negative_len_parse_buffer);
tcase_add_test(tc_basic, test_get_buffer_1);
tcase_add_test(tc_basic, test_get_buffer_2);
#if XML_CONTEXT_BYTES > 0
if (! versions_equal(&read_version, &parsed_version))
fail("Version mismatch");
- if (xcstrcmp(version_text, XCS("expat_2.6.2"))) /* needs bump on releases */
+ if (xcstrcmp(version_text, XCS("expat_2.6.3"))) /* needs bump on releases */
fail("XML_*_VERSION in expat.h out of sync?\n");
}
END_TEST