QDnsLookup/Unix: make sure we don't overflow the buffer
authorDebian Qt/KDE Maintainers <debian-qt-kde@lists.debian.org>
Thu, 25 May 2023 10:45:05 +0000 (11:45 +0100)
committerDmitry Shachnev <mitya57@debian.org>
Thu, 25 May 2023 10:45:05 +0000 (11:45 +0100)
Origin: upstream, https://code.qt.io/cgit/qt/qtbase.git/commit/?id=7dba2c87619d558a
Last-Update: 2023-05-25

The DNS Records are variable length and encode their size in 16 bits
before the Record Data (RDATA). Ensure that both the RDATA and the
Record header fields before it fall inside the buffer we have.

Additionally reject any replies containing more than one query records.

Gbp-Pq: Name CVE-2023-33285.diff

src/network/kernel/qdnslookup_unix.cpp

index 12b40fc35dda785232cdf518c06b18dd49c3e047..99e999d436c9181949c2a5208f363b1097b7dd96 100644 (file)
@@ -227,7 +227,6 @@ void QDnsLookupRunnable::query(const int requestType, const QByteArray &requestN
     // responseLength in case of error, we still can extract the
     // exact error code from the response.
     HEADER *header = (HEADER*)response;
-    const int answerCount = ntohs(header->ancount);
     switch (header->rcode) {
     case NOERROR:
         break;
@@ -260,18 +259,31 @@ void QDnsLookupRunnable::query(const int requestType, const QByteArray &requestN
         return;
     }
 
-    // Skip the query host, type (2 bytes) and class (2 bytes).
     char host[PACKETSZ], answer[PACKETSZ];
     unsigned char *p = response + sizeof(HEADER);
-    int status = local_dn_expand(response, response + responseLength, p, host, sizeof(host));
-    if (status < 0) {
+    int status;
+
+    if (ntohs(header->qdcount) == 1) {
+        // Skip the query host, type (2 bytes) and class (2 bytes).
+        status = local_dn_expand(response, response + responseLength, p, host, sizeof(host));
+        if (status < 0) {
+            reply->error = QDnsLookup::InvalidReplyError;
+            reply->errorString = tr("Could not expand domain name");
+            return;
+        }
+        if ((p - response) + status + 4 >= responseLength)
+            header->qdcount = 0xffff;   // invalid reply below
+        else
+            p += status + 4;
+    }
+    if (ntohs(header->qdcount) > 1) {
         reply->error = QDnsLookup::InvalidReplyError;
-        reply->errorString = tr("Could not expand domain name");
+        reply->errorString = tr("Invalid reply received");
         return;
     }
-    p += status + 4;
 
     // Extract results.
+    const int answerCount = ntohs(header->ancount);
     int answerIndex = 0;
     while ((p < response + responseLength) && (answerIndex < answerCount)) {
         status = local_dn_expand(response, response + responseLength, p, host, sizeof(host));
@@ -283,6 +295,11 @@ void QDnsLookupRunnable::query(const int requestType, const QByteArray &requestN
         const QString name = QUrl::fromAce(host);
 
         p += status;
+
+        if ((p - response) + 10 > responseLength) {
+            // probably just a truncated reply, return what we have
+            return;
+        }
         const quint16 type = (p[0] << 8) | p[1];
         p += 2; // RR type
         p += 2; // RR class
@@ -290,6 +307,8 @@ void QDnsLookupRunnable::query(const int requestType, const QByteArray &requestN
         p += 4;
         const quint16 size = (p[0] << 8) | p[1];
         p += 2;
+        if ((p - response) + size > responseLength)
+            return;             // truncated
 
         if (type == QDnsLookup::A) {
             if (size != 4) {