Don't let the XML input archive destructor throw at end of input#349
Merged
Conversation
gennaroprota
force-pushed
the
fix/xml-iarchive-eof-handling
branch
from
July 20, 2026 14:15
a07c613 to
b2cd58a
Compare
The xml input archive destructor calls `windup()` to consume the trailing tag; `windup()` calls `my_parse()`. Since Boost 1.66 (commit 64dc620), `my_parse()` threw `input_stream_error` whenever `get()` set `failbit`. But `get()` sets both `failbit` and `eofbit` at end of input, and the check tested `fail()` before `eof()`---so simply reaching the end while scanning for a tag threw, escaping the implicitly noexcept destructor and terminating the process. No closing tag is left to find in several cases: a truncated stream, an archive whose writer was never flushed, or a well-formed one whose closing tag an earlier failed read had already consumed. So, test `eof()` before `fail()` and return `false` for end of input---as it did before 1.66 (a genuine, non-EOF stream error still throws). As defense in depth, this also wraps the `windup()` call in both XML input destructors so no exception can escape them regardless. Fixes issue #99, the same defect as the already-closed #82. It also removes the terminate reported in #109, though that issue's broader broken-state-after-exception concern is a by-design limitation of the forward-only parse and isn't fixed. Refs #109.
Issue #109 is the same Boost 1.66 `my_parse` regression as #82 and #99, reached by a different path: reading an nvp that is not present fails and consumes the closing tag, so at destruction `windup()` meets end of input and (before the fix) threw out of the noexcept destructor, terminating the process. The `my_parse` fix already resolves it; this adds the guarding test. It reads a value, then a missing nvp (catching the exception), and lets the archive destruct: it aborts before the fix and passes after. Refs issue #109.
gennaroprota
force-pushed
the
fix/xml-iarchive-eof-handling
branch
from
July 20, 2026 14:27
b2cd58a to
7135618
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
An XML input archive whose stream reaches end of input during
windup()(run from the destructor) no longer throws, which, from an implicitlynoexceptdestructor, was callingstd::terminate/abort.Root cause
windup()callsmy_parse(). Boost 1.66 (commit64dc620, whose real purpose was dropping a<codecvt>dependency) changedmy_parse()from returningfalseon a streamfail()to throwinginput_stream_error:get()sets bothfailbitandeofbitat end of input, and the check testedfail()beforeeof(). So simply reaching the end while scanning for a tag threw; and since that happens inside the destructor'swindup(), the exception escaped thenoexceptdestructor and terminated the process.The close tag can be absent at that point for several reasons, which is why this surfaced as three separate reports:
Issues
Fixes #99 (same root cause as the already-closed #82).
It also removes the process-terminating crash reported in #109, but not that issue's broader "broken state after an exception": a failed read still consumes input and leaves the archive unusable, which is a separate, by-design limitation of the forward-only positional parse (you can't probe for an absent named element without consuming input). So this only refs #109 rather than closing it; we can later decide whether #109 should be closed as "crash fixed, remainder by design" or kept open.