From ae6fe2afe2b76dcc8dfa00aac00451cdefac2293 Mon Sep 17 00:00:00 2001 From: Debian Qt/KDE Maintainers Date: Mon, 21 Sep 2026 21:14:33 +0300 Subject: [PATCH] QDomNode: fix unbounded nesting depth on destruction and clear() Origin: upstream, https://code.qt.io/cgit/qt/qtbase.git/commit/?id=647b221ca885739a Last-Update: 2026-09-21 In both ~QDomNodePrivate() and QDomNodePrivate::clear(), the code simply walked the list of chldren (->first, ->next), and deleted each one in turn. This recurses into ~QDomNodePrivate() and uses stack space proportional to the height of the tree. Since the latter is user-controlled, this is a an easy DoS, so fix the implementation to use iteration instead of recursion. Note that it suffices to fix ~QDomNodePrivate(), as clear() only recurses via the dtor, too. Gbp-Pq: Name CVE-2026-19248.diff --- src/xml/dom/qdom.cpp | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/src/xml/dom/qdom.cpp b/src/xml/dom/qdom.cpp index 04b868a7e..38910b894 100644 --- a/src/xml/dom/qdom.cpp +++ b/src/xml/dom/qdom.cpp @@ -992,16 +992,21 @@ QDomNodePrivate::QDomNodePrivate(QDomNodePrivate *n, bool deep) : ref(1) QDomNodePrivate::~QDomNodePrivate() { - QDomNodePrivate* p = first; - QDomNodePrivate* n; - - while (p) { - n = p->next; - if (!p->ref.deref()) - delete p; - else - p->setNoParent(); - p = n; + QDomNodePrivate *p = this; + + // post-order depth-first-search; visitation is deletion (avoids recursion) + while (true) { + if (QDomNodePrivate *c = p->first) { + p->first = c->next; // peel firstChild off p + if (c->ref.deref()) + c->setNoParent(); // survivor: detach, don't descend + else + p = c; // descend; c's parent() remembers p + } else { // p ran out of children (= is a leaf now) + if (p == this) + break; // we're done, don't `delete this` + delete std::exchange(p, p->parent()); // deletes and ascends + } } first = nullptr; last = nullptr; -- 2.39.5