Expat 2.10.0 released, fixes four vulnerabilities
For readers new to Expat:
libexpat is a fast streaming XML parser. Alongside libxml2, Expat is one of the most widely used software libre XML parsers written in C, specifically C99. It is cross-platform and licensed under the MIT license.
Hi!
Expat 2.10.0 was released a few hours ago. The key motivation for cutting a release now was to get the security fixes into the hands of Expat's users.
The four fixed security issues are:
-
CVE-2026-113351 —
Denial of service with
XML_ParserReset - CVE-2026-115506 — Denial of service from variations of billion laughs attacks and parameter laughs attacks
- CVE-2026-117610 — Denial of service or more from issues with initial non-final empty subparser parse calls
-
CVE-2026-118370 —
Signed integer overflow in function
storeAtts
Two of these seem worthy of explanation.
Let's start with CVE-2026-115506. Back in November 2025, about a year ago, my friend Hanno Böck had a closer look at billion laughs attacks again. The Wikipedia version of the attack is this (slightly modified) XML document:
<!DOCTYPE lolz [ <!ENTITY lol0 "lol"> <!ENTITY lol1 "&lol0;&lol0;&lol0;&lol0;&lol0;&lol0;&lol0;"> <!ENTITY lol2 "&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;"> <!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;"> <!ENTITY lol4 "&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;"> <!ENTITY lol5 "&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;"> <!ENTITY lol6 "&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;"> <!ENTITY lol7 "&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;"> <!ENTITY lol8 "&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;"> <!ENTITY lol9 "&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;"> ]> <lolz>&lol9;</lolz>
For this document, someone made at least six decisions:
- How long should the content of the root entity
lol0be? Answer: 3. - How many references should each non-root entity contain? Answer: 7.
- How many levels deep should entities be nested? Answer: 9.
- How many references should point into the tree? Answer: 1.
- Should the attack use
general entities (
&name;) or parameter entities (%name;)? Answer: general entities. - Should declaration order be forward or backward? Answer: forward.
Hanno's question was: Is there a chance that some other combination of parameters could still take a lot of time to parse, could evade the detection mechanism? And so he created a simple XML fuzzer "lololol" to approach that question with code, and did indeed find combinations that took Expat too long to parse. The symptom essentially was that Expat already did detect the attack but that it exercised too much of the document before coming to the conclusion: this is malicious content.
Then in January 2026 Shani Yosef of echo.ai reached out offering help with a fix, signed an informal NDA with me, and got to work. Since then, one bottleneck was waiting for my next round of review: until August, Expat was unpaid spare time for me with little time, and starting August I now had more and paid time for Expat but also had multiple other high-priority Expat things on my plate at once. It was four of us teaming up in a private GitHub repository — Shani of Echo in the driver's seat and bringing the key idea for a fix, Matthew Fernandez, Berkay Eren Ürün of Siemens, and me in the review seats —, and then @brspoon also reported a remaining issue at the last minute. It was not an easy task but exciting! As a result, not only does Expat now detect and reject these remaining forms of the billion and parameter laughs attacks but also detect faster, in general. It does so by pre-computing how much an entity would expand, right before starting to actually go ahead and expand. A big "thank you!" to Shani, Matthew, Berkay, @brspoon, Echo and Siemens — thank you!
The other issue worth explaining —
CVE-2026-113351 —
was with the allocation tracker introduced with Expat 2.7.2, where every new
allocation increases a per-parser total, and every freeing of memory decreases
the total, to be able to detect maliciously excessive use of memory relative
to document file size: bigger documents are allowed to allocate more memory.
Now the issue was that
XML_ParserReset,
which resets a parser to a point where
it is ready to parse another document while keeping some memory for reuse,
would correctly account for the memory that the parser still kept
but could then reject the next document in alleged violation of memory use
amplification limits that in fact the previous document had built up.
The fix now offsets that value while keeping the memory, so that the next
document has the allocation tracker set back to zero effectively.
Previously, an attacker could force follow-up parses to fail by feeding
a large document to the parser and cause denial of service.
Thanks to Pu Cheng for the report!
It is worth noting that back in August the count of known unfixed security issues in Expat was at 5, and now, after this release, it is at 13. So for now, in the same time, more new findings are coming in (20) than could be fixed (12).
As usual, there are multiple non-security, non-feature improvements included with this release. For more details about this release, please check out the change log.
It should be noted that it was the Open Source Sabbatical of the City of Munich that allowed me to focus on libexpat for this release through its funding — thank you! We're in the fourth month of hopefully six months total; there continues to be plenty to do. Wish me luck!
If your business relies on Expat beyond January 2027, please consider funding the maintenance of Expat to ensure its health and security for you and others. Thank you!
Thanks to everyone who contributed to this release of Expat!
If you maintain Expat packaging, a bundled copy of Expat, or a pinned version of Expat, please update to version 2.10.0. Thank you!
Sebastian Pipping