]> dgit.raspbian.org Git - qtbase-opensource-src.git/commitdiff
QDomNode: fix unbounded nesting depth on destruction and clear()
authorDebian Qt/KDE Maintainers <debian-qt-kde@lists.debian.org>
Mon, 21 Sep 2026 18:14:33 +0000 (21:14 +0300)
committerDmitry Shachnev <mitya57@debian.org>
Mon, 21 Sep 2026 18:14:33 +0000 (21:14 +0300)
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

index 04b868a7e1d7ee207fae81add79e17a2d09cf011..38910b894735e879422ae55aeb80e831fbeb97bb 100644 (file)
@@ -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;