QXmlSimpleReader: fix unbounded recursion when parsing nested elements


Each level of element nesting cost two stack frames (parseElement() →
parseContent() → parseElement() → …), so a valid, if deeply nested,
document could overflow the stack. Resuming an incremental parse
recursed the same way.

The preceding commits split both functions into step functions,
parse{Element,Content}Frame(), driven by parseNestedFrames(). This
patch removes the last two recursive calls: where the step functions
used to call parseContent() (state STagEnd2) or parseElement() (state
Elem), they now return EnterContent/EnterElement, and
parseNestedFrames() pushes a new frame onto a local stack and loops.

When a child frame is done, the parent is simply re-stepped with its
state unchanged. This works for the same reason resuming a suspended
call works: the saved state is the one whose post-action was the
nested call, and neither STagEnd2 nor Elem has a pre-action, so
re-entry continues with the next character, exactly as the old code
did after the nested call returned.

When running out of data, the frames are transferred to parseStack in
the layout the recursive unwind used to produce (outermost on top, the
leaf that ran out of data at the bottom), and back again on resume, so
the parseStack protocol is unchanged. The innermost frame has already
saved its own state (or reported an error, which makes parseFailed() a
no-op) when it returns Failed, so suspendFrames() skips it. We can't
keep the live frames on parseStack itself: to the rest of the parser,
its existence means "incremental" and its non-emptiness "resume".

Stack usage is now constant; instead, each level costs 8 bytes of heap
(on top of what `tags` already costs). An allocation failure throws
std::bad_alloc, which, unlike a stack overflow, users can handle.

The new tests nest 200'000 levels deep (plain, namespace-prefixed and
mixed content) and check both full and incremental parsing. They
crashed with a stack overflow without the fix.

[ChangeLog][QtCore5Compat][QXmlSimpleReader] Fixed a stack overflow
when parsing documents with very deeply nested elements.

Task-number: QTBUG-149171
Pick-to: 6.12 6.12.0 6.11.3 6.8 6.5 5.15
Change-Id: I78aec53199d0299dbf6d5ff9176f04a0998ef170
Reviewed-by: Marc Mutz <marc.mutz@qt.io>
Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
commit464577c5f76dc8f11ab5badd49d1f81c96a607e4
parenta36eb11cf7f6fe4831baf0d80a28e90a7c3bf926
authorIvan Solovev <ivan.solovev@qt.io>
committerMarc Mutz <marc.mutz@qt.io>
commit timeFri, 25 Sep 2026 15:57:40 +0000