Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package qt6-coap for openSUSE:Factory 
checked in at 2026-10-18 19:22:40
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/qt6-coap (Old)
 and      /work/SRC/openSUSE:Factory/.qt6-coap.new.3698736 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "qt6-coap"

Sun Oct 18 19:22:40 2026 rev:45 rq:1384387 version:6.12.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/qt6-coap/qt6-coap.changes        2026-08-21 
16:54:03.201521533 +0200
+++ /work/SRC/openSUSE:Factory/.qt6-coap.new.3698736/qt6-coap.changes   
2026-10-18 19:23:00.394969714 +0200
@@ -1,0 +2,6 @@
+Wed Sep 30 16:08:55 UTC 2026 - Christophe Marin <[email protected]>
+
+- Update to 6.12.0
+  https://www.qt.io/blog/qt-6.12-released
+
+-------------------------------------------------------------------

Old:
----
  qtcoap-everywhere-src-6.11.2.tar.xz

New:
----
  qtcoap-everywhere-src-6.12.0.tar.xz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ qt6-coap.spec ++++++
--- /var/tmp/diff_new_pack.wDoo1H/_old  2026-10-18 19:23:01.068997626 +0200
+++ /var/tmp/diff_new_pack.wDoo1H/_new  2026-10-18 19:23:01.069997667 +0200
@@ -16,8 +16,8 @@
 #
 
 
-%define real_version 6.11.2
-%define short_version 6.11
+%define real_version 6.12.0
+%define short_version 6.12
 %define tar_name qtcoap-everywhere-src
 %define tar_suffix %{nil}
 #
@@ -27,7 +27,7 @@
 %endif
 #
 Name:           qt6-coap%{?pkg_suffix}
-Version:        6.11.2
+Version:        6.12.0
 Release:        0
 Summary:        Qt 6 CoAP client module
 License:        GPL-3.0-or-later

++++++ _service ++++++
--- /var/tmp/diff_new_pack.wDoo1H/_old  2026-10-18 19:23:01.113999489 +0200
+++ /var/tmp/diff_new_pack.wDoo1H/_new  2026-10-18 19:23:01.119999737 +0200
@@ -1,9 +1,9 @@
 <services>
   <service name="tar_scm" mode="disabled">
    <param name="changesgenerate">disable</param>
-   <param name="version">6.11.2</param>
+   <param name="version">6.12.0</param>
    <param name="url">git://code.qt.io/qt/qtcoap.git</param>
-   <param name="revision">v6.11.2</param>
+   <param name="revision">v6.12.0</param>
    <param name="scm">git</param>
    <param name="filename">qtcoap-everywhere-src</param>
   </service>

++++++ qtcoap-everywhere-src-6.11.2.tar.xz -> 
qtcoap-everywhere-src-6.12.0.tar.xz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/qtcoap-everywhere-src-6.11.2/.cmake.conf 
new/qtcoap-everywhere-src-6.12.0/.cmake.conf
--- old/qtcoap-everywhere-src-6.11.2/.cmake.conf        2026-08-11 
19:38:38.000000000 +0200
+++ new/qtcoap-everywhere-src-6.12.0/.cmake.conf        2026-09-26 
12:11:53.000000000 +0200
@@ -1,4 +1,4 @@
-set(QT_REPO_MODULE_VERSION "6.11.2")
+set(QT_REPO_MODULE_VERSION "6.12.0")
 set(QT_REPO_MODULE_PRERELEASE_VERSION_SEGMENT "alpha1")
 set(QT_EXTRA_INTERNAL_TARGET_DEFINES
     "QT_NO_CONTEXTLESS_CONNECT=1"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/qtcoap-everywhere-src-6.11.2/CONTRIBUTING.md 
new/qtcoap-everywhere-src-6.12.0/CONTRIBUTING.md
--- old/qtcoap-everywhere-src-6.11.2/CONTRIBUTING.md    1970-01-01 
01:00:00.000000000 +0100
+++ new/qtcoap-everywhere-src-6.12.0/CONTRIBUTING.md    2026-09-26 
12:11:53.000000000 +0200
@@ -0,0 +1,11 @@
+## Qt contribution guidelines
+
+We welcome contributions to Qt!
+
+Note that we cannot accept pull requests on GitHub. All contributions
+to the Qt project are exclusively handled through the
+[Gerrit code review system](https://codereview.qt-project.org).
+
+See
+[contribute.qt-project.org](https://contribute.qt-project.org/) to learn
+more.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/qtcoap-everywhere-src-6.11.2/REUSE.toml 
new/qtcoap-everywhere-src-6.12.0/REUSE.toml
--- old/qtcoap-everywhere-src-6.11.2/REUSE.toml 2026-08-11 19:38:38.000000000 
+0200
+++ new/qtcoap-everywhere-src-6.12.0/REUSE.toml 2026-09-26 12:11:53.000000000 
+0200
@@ -21,7 +21,7 @@
 SPDX-License-Identifier = "LicenseRef-Qt-Commercial OR BSD-3-Clause"
 
 [[annotations]]
-path = ["**/README*", "**.qdocconf", "src/coap/doc/style/style.css"]
+path = ["**.qdocconf", "src/coap/doc/style/style.css", "**/README*", 
"CONTRIBUTING.md"]
 comment = "documentation"
 precedence = "closest"
 SPDX-FileCopyrightText = "Copyright (C) The Qt Company Ltd."
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/qtcoap-everywhere-src-6.11.2/dependencies.yaml 
new/qtcoap-everywhere-src-6.12.0/dependencies.yaml
--- old/qtcoap-everywhere-src-6.11.2/dependencies.yaml  2026-08-11 
19:38:38.000000000 +0200
+++ new/qtcoap-everywhere-src-6.12.0/dependencies.yaml  2026-09-26 
12:11:53.000000000 +0200
@@ -1,4 +1,4 @@
 dependencies:
   ../qtbase:
-    ref: ef55f427f2c8b410d34f8a7681020a3000cf6866
+    ref: 025bdad181de241e81bf853c8a2d7bf3d19261a9
     required: true
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/qtcoap-everywhere-src-6.11.2/licenseRule.json 
new/qtcoap-everywhere-src-6.12.0/licenseRule.json
--- old/qtcoap-everywhere-src-6.11.2/licenseRule.json   2026-08-11 
19:38:38.000000000 +0200
+++ new/qtcoap-everywhere-src-6.12.0/licenseRule.json   2026-09-26 
12:11:53.000000000 +0200
@@ -58,8 +58,9 @@
     },
     {
         "comment": "Files with the following endings are Documentation 
licensed.",
-        "file_pattern_ending": [".qdoc", ".qdocinc" , ".qdocconf", "README", 
"qt_attribution.json",
-                                 "README.md", ".css"],
+        "file_pattern_ending": [".qdoc", ".qdocinc" , ".qdocconf", ".css",
+                                "README.md", "CONTRIBUTING.md",
+                                "qt_attribution.json"],
         "location":{
             "": {
                 "comment": "",
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/qtcoap-everywhere-src-6.11.2/src/coap/CMakeLists.txt 
new/qtcoap-everywhere-src-6.12.0/src/coap/CMakeLists.txt
--- old/qtcoap-everywhere-src-6.11.2/src/coap/CMakeLists.txt    2026-08-11 
19:38:38.000000000 +0200
+++ new/qtcoap-everywhere-src-6.12.0/src/coap/CMakeLists.txt    2026-09-26 
12:11:53.000000000 +0200
@@ -28,6 +28,7 @@
         Qt::NetworkPrivate
     PUBLIC_LIBRARIES
         Qt::Core
+        Qt::Network
     PRIVATE_MODULE_INTERFACE
         Qt::CorePrivate
         Qt::NetworkPrivate
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/qtcoap-everywhere-src-6.11.2/src/coap/doc/src/qtcoap-index.qdoc 
new/qtcoap-everywhere-src-6.12.0/src/coap/doc/src/qtcoap-index.qdoc
--- old/qtcoap-everywhere-src-6.11.2/src/coap/doc/src/qtcoap-index.qdoc 
2026-08-11 19:38:38.000000000 +0200
+++ new/qtcoap-everywhere-src-6.12.0/src/coap/doc/src/qtcoap-index.qdoc 
2026-09-26 12:11:53.000000000 +0200
@@ -28,6 +28,10 @@
     \li Resource discovery (multicast and single server)
     \endlist
 
+    \note Security is opt-in: QCoapClient uses QtCoap::NoSecurity by default 
and must
+    be constructed with a secure mode to enable DTLS.
+    See \l{Qt CoAP Overview#security}{Security}.
+
     \include module-use.qdocinc using qt module
 
     \code
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/qtcoap-everywhere-src-6.11.2/src/coap/doc/src/qtcoap-overview.qdoc 
new/qtcoap-everywhere-src-6.12.0/src/coap/doc/src/qtcoap-overview.qdoc
--- old/qtcoap-everywhere-src-6.11.2/src/coap/doc/src/qtcoap-overview.qdoc      
2026-08-11 19:38:38.000000000 +0200
+++ new/qtcoap-everywhere-src-6.12.0/src/coap/doc/src/qtcoap-overview.qdoc      
2026-09-26 12:11:53.000000000 +0200
@@ -17,9 +17,10 @@
     for addressing. At the same time it provides lightweight reliability 
mechanisms
     and security.
 
-    Qt CoAP implements the client side of CoAP. By default, the transport 
layer uses
-    QUdpSocket and QDtls for security. Alternative transports can be used by 
implementing
-    the QCoapConnection interface.
+    Qt CoAP implements the client side of CoAP. The default transport is built 
on
+    QUdpSocket, with QDtls providing security only when a secure mode is 
selected;
+    no security mode is enabled by default (see \l{#security}{Security}). 
Alternative
+    transports can be used by implementing the QCoapConnection interface.
 
     \section1 Messaging Model
 
@@ -141,6 +142,17 @@
     based on Datagram TLS (DTLS) over UDP. However, as mentioned above, it is 
possible to
     provide a custom transport with another security type.
 
+    \note When you provide a custom transport, the DTLS protection described 
here no longer
+    applies. Securing the exchange then becomes the responsibility of that 
transport, which
+    must provide encryption, peer authentication, and message framing 
appropriate to the
+    protocol it is built on. A transport that omits any of these leaves 
requests and
+    responses unprotected, and the loss is not visible to the rest of the 
application.
+
+    \warning By default Qt CoAP operates without transport security. Unless a 
secure
+    mode is selected, requests and responses are sent as plain UDP datagrams. 
Use the
+    default (\c{QtCoap::NoSecurity}) only on fully trusted networks; on any 
untrusted
+    network construct QCoapClient with a secure mode.
+
     For securing the CoAP client, one of the supported security modes should 
be specified
     when creating the client:
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/qtcoap-everywhere-src-6.11.2/src/coap/qcoapclient.cpp 
new/qtcoap-everywhere-src-6.12.0/src/coap/qcoapclient.cpp
--- old/qtcoap-everywhere-src-6.11.2/src/coap/qcoapclient.cpp   2026-08-11 
19:38:38.000000000 +0200
+++ new/qtcoap-everywhere-src-6.12.0/src/coap/qcoapclient.cpp   2026-09-26 
12:11:53.000000000 +0200
@@ -33,11 +33,14 @@
 
 QCoapClientPrivate::~QCoapClientPrivate()
 {
+    // protocol and connection (and their children, such as the retransmission
+    // timers and the UDP socket) live in workerThread. They must be destroyed 
in
+    // that thread, not here.
+    protocol->deleteLater();
+    connection->deleteLater();
     workerThread->quit();
     workerThread->wait();
     delete workerThread;
-    delete protocol;
-    delete connection;
 }
 
 /*!
@@ -88,6 +91,32 @@
     It can be used the same way as a QCoapReply but contains also a list of
     resources.
 
+    \section1 Security Considerations
+
+    During a block-wise transfer, the response payload is accumulated
+    in memory until the transfer completes, and QtCoap does not impose an upper
+    bound on the accumulated size. A malicious or malfunctioning server that
+    keeps sending blocks without ever signalling the final one can therefore
+    drive the client to exhaust memory. The accumulated size is not observable 
while
+    the transfer is in progress, so an application cannot bound it directly. 
Perform
+    block-wise exchanges only with trusted servers, and preferably on a 
trusted or
+    isolated network.
+
+    \section2 Multicast
+
+    DTLS protects a connection between two endpoints and therefore cannot 
apply to
+    multicast, where more than 2 endpoints may be present. Because of this, a
+    multicast request and its replies are neither encrypted nor authenticated.
+
+    Every host in the group may answer a multicast request. In CoAP these hosts
+    are all legitimate owners of the resource and are expected to serve
+    equivalent replies. When a reply is sent block by block, the remaining
+    blocks are retrieved by unicast from whichever host answered.
+
+    CoAP multicast is designed with the assumption that the network is secured
+    and every node in the group is trusted. Use multicast only in scenarios 
that
+    fulfill this assumption.
+
     \sa QCoapRequest, QCoapReply, QCoapResourceDiscoveryReply
 */
 
@@ -112,6 +141,9 @@
     been received, \a message contains the payload and the message details,
     and \a sender contains the sender address.
 
+    \note Multicast requests are unauthenticated; see \l {Multicast}
+    {multicast security considerations} for the trust model this relies on.
+
     \sa error(), QCoapReply::finished(), QCoapReply::error()
 */
 
@@ -181,7 +213,11 @@
 {
     Q_Q(QCoapClient);
 
-    delete connection;
+    if (connection) {
+        // The connection lives in workerThread, let that thread delete it.
+        connection->disconnect();
+        connection->deleteLater();
+    }
     connection = customConnection;
 
     q->connect(connection, &QCoapConnection::readyRead, protocol,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/qtcoap-everywhere-src-6.11.2/src/coap/qcoapconnection.cpp 
new/qtcoap-everywhere-src-6.12.0/src/coap/qcoapconnection.cpp
--- old/qtcoap-everywhere-src-6.11.2/src/coap/qcoapconnection.cpp       
2026-08-11 19:38:38.000000000 +0200
+++ new/qtcoap-everywhere-src-6.12.0/src/coap/qcoapconnection.cpp       
2026-09-26 12:11:53.000000000 +0200
@@ -21,6 +21,13 @@
 
     It isolates CoAP clients from the transport in use, so that any
     client can be used with any supported transport.
+
+    \note A subclass that implements a transport is responsible for the 
security
+    of the connection it establishes. Unless the transport itself provides
+    encryption, peer authentication, and correct message framing, the
+    confidentiality, integrity, and authenticity guarantees that DTLS provides
+    over the default transport no longer hold. See the security discussion in 
the
+    \l(Qt CoAP Overview#Security){Qt CoAP Overview}.
 */
 
 /*!
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/qtcoap-everywhere-src-6.11.2/src/coap/qcoapinternalmessage.cpp 
new/qtcoap-everywhere-src-6.12.0/src/coap/qcoapinternalmessage.cpp
--- old/qtcoap-everywhere-src-6.11.2/src/coap/qcoapinternalmessage.cpp  
2026-08-11 19:38:38.000000000 +0200
+++ new/qtcoap-everywhere-src-6.12.0/src/coap/qcoapinternalmessage.cpp  
2026-09-26 12:11:53.000000000 +0200
@@ -90,7 +90,11 @@
 
     const auto value = option.opaqueValue();
     const quint8 *optionData = reinterpret_cast<const quint8 *>(value.data());
-    const quint8 lastByte = optionData[option.length() - 1];
+
+    // A zero-length block option is valid and encodes NUM=0, M=0, SZX=0
+    // (RFC 7959 §2.2). Treat the absent last byte as 0 instead of reading
+    // optionData[-1]; the loop below is then skipped and every field resolves 
to 0.
+    const quint8 lastByte = option.length() > 0 ? optionData[option.length() - 
1] : 0;
     quint32 blockNumber = 0;
 
     for (int i = 0; i < option.length() - 1; ++i)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/qtcoap-everywhere-src-6.11.2/src/coap/qcoapinternalreply.cpp 
new/qtcoap-everywhere-src-6.12.0/src/coap/qcoapinternalreply.cpp
--- old/qtcoap-everywhere-src-6.11.2/src/coap/qcoapinternalreply.cpp    
2026-08-11 19:38:38.000000000 +0200
+++ new/qtcoap-everywhere-src-6.12.0/src/coap/qcoapinternalreply.cpp    
2026-09-26 12:11:53.000000000 +0200
@@ -4,8 +4,11 @@
 // Qt-Security score:critical reason:network-protocol
 
 #include "qcoapinternalreply_p.h"
+#include <QtCore/qendian.h>
 #include <QtCore/qmath.h>
 
+#include <memory>
+
 QT_BEGIN_NAMESPACE
 
 /*!
@@ -51,7 +54,11 @@
 //! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 QCoapInternalReply *QCoapInternalReply::createFromFrame(const QByteArray 
&reply, QObject *parent)
 {
-    QCoapInternalReply *internalReply = new QCoapInternalReply(parent);
+    // A CoAP message is at least 4 bytes long
+    if (reply.size() < 4)
+        return nullptr;
+
+    auto internalReply = std::make_unique<QCoapInternalReply>(parent);
     QCoapInternalReplyPrivate *d = internalReply->d_func();
 
     const quint8 *pduData = reinterpret_cast<const quint8 *>(reply.data());
@@ -60,39 +67,81 @@
     d->message.setVersion((pduData[0] >> 6) & 0x03);
     d->message.setType(QCoapMessage::Type((pduData[0] >> 4) & 0x03));
     quint8 tokenLength = (pduData[0]) & 0x0F;
+
+    // RFC 7252 §3: token lengths 9-15 are reserved and must be treated as a
+    // message format error. Also reject a token whose bytes are not all 
present.
+    if (tokenLength > 8 || 4 + tokenLength > reply.size())
+        return nullptr;
+
     d->responseCode = static_cast<QtCoap::ResponseCode>(pduData[1]);
     
d->message.setMessageId(static_cast<quint16>((static_cast<quint16>(pduData[2]) 
<< 8)
                                                  | 
static_cast<quint16>(pduData[3])));
     d->message.setToken(reply.mid(4, tokenLength));
 
     // Parse Options
-    int i = 4 + tokenLength;
-    quint16 lastOptionNumber = 0;
-    while (i != reply.size() && pduData[i] != 0xFF) {
-        quint16 optionDelta = ((pduData[i] >> 4) & 0x0F);
-        quint16 optionLength = (pduData[i] & 0x0F);
+    qsizetype i = 4 + tokenLength;
+    quint32 lastOptionNumber = 0;
+    while (i < reply.size() && pduData[i] != 0xFF) {
+        quint32 optionDelta = ((pduData[i] >> 4) & 0x0F);
+        quint32 optionLength = (pduData[i] & 0x0F);
 
         // Delta value > 12 : special values
         if (optionDelta == 13) {
-            ++i;
+            if (++i >= reply.size())
+                return nullptr;
             optionDelta = pduData[i] + 13;
         } else if (optionDelta == 14) {
-            ++i;
-            optionDelta = pduData[i] + 269;
+            // RFC 7252 §3.1: nibble 14 is a 16-bit unsigned integer in network
+            // byte order, giving the value minus 269.
+            if (i >= reply.size() - 2)
+                return nullptr;
+            optionDelta = qFromBigEndian<quint16>(pduData + i + 1) + 269;
+            i += 2;
         }
 
         // Delta length > 12 : special values
         if (optionLength == 13) {
-            ++i;
+            if (++i >= reply.size())
+                return nullptr;
             optionLength = pduData[i] + 13;
         } else if (optionLength == 14) {
-            ++i;
-            optionLength = pduData[i] + 269;
+            // RFC 7252 §3.1: nibble 14 is a 16-bit unsigned integer in network
+            // byte order, giving the value minus 269.
+            if (i >= reply.size() - 2)
+                return nullptr;
+            optionLength = qFromBigEndian<quint16>(pduData + i + 1) + 269;
+            i += 2;
+        }
+
+        // The option value is optionLength bytes following the current byte.
+        // Reject the frame if it would run past the end of the buffer.
+        if (i >= reply.size() - static_cast<qsizetype>(optionLength))
+            return nullptr;
+
+        quint32 optionNumber = lastOptionNumber + optionDelta;
+        // RFC 7252: option numbers do not exceed 65535. A larger cumulative
+        // number means a malformed frame; every following option would be out 
of
+        // range too, so reject the whole reply.
+        if (optionNumber > 65535)
+            return nullptr;
+
+        const QByteArray optionValue = reply.sliced(i + 1, optionLength);
+
+        // Validate block options (RFC 7959 §2.2) before they are interpreted.
+        if (optionNumber == QCoapOption::Block1 || optionNumber == 
QCoapOption::Block2) {
+            // A block option value is 0-3 bytes. A longer value yields a block
+            // number that overflows the 20-bit NUM field.
+            if (optionValue.size() > 3)
+                return nullptr;
+            // SZX value 7 (block size 2048) is reserved; it is the low 3 bits 
of
+            // the last byte. Accepting it yields a 2048-byte block size, 
which is illegal
+            if (!optionValue.isEmpty()
+                && (static_cast<quint8>(optionValue.back()) & 0x7) == 0x7) {
+                return nullptr;
+            }
         }
 
-        quint16 optionNumber = lastOptionNumber + optionDelta;
-        internalReply->addOption(QCoapOption::OptionName(optionNumber),
-                                 reply.mid(i + 1, optionLength));
+        internalReply->addOption(QCoapOption::OptionName(optionNumber), 
optionValue);
         lastOptionNumber = optionNumber;
         i += 1 + optionLength;
     }
@@ -104,7 +153,7 @@
         d->message.setPayload(d->message.payload().append(currentPayload));
     }
 
-    return internalReply;
+    return internalReply.release();
 }
 
 /*!
@@ -156,7 +205,8 @@
 
     const auto value = option.opaqueValue();
     const quint8 *optionData = reinterpret_cast<const quint8 *>(value.data());
-    const quint8 lastByte = optionData[option.length() - 1];
+    // Zero-length block option => M=0 (no further block); avoid 
optionData[-1].
+    const quint8 lastByte = option.length() > 0 ? optionData[option.length() - 
1] : 0;
 
     // M field
     bool hasNextBlock = ((lastByte & 0x8) == 0x8);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/qtcoap-everywhere-src-6.11.2/src/coap/qcoapoption.cpp 
new/qtcoap-everywhere-src-6.12.0/src/coap/qcoapoption.cpp
--- old/qtcoap-everywhere-src-6.11.2/src/coap/qcoapoption.cpp   2026-08-11 
19:38:38.000000000 +0200
+++ new/qtcoap-everywhere-src-6.12.0/src/coap/qcoapoption.cpp   2026-09-26 
12:11:53.000000000 +0200
@@ -172,14 +172,21 @@
 
 /*!
     Returns the integer value of the option.
+
+    \note A uint option holds at most 4 bytes. If the option value is longer,
+    only its low 4 bytes are used to compute the result; use \l length() to
+    detect an over-long value.
  */
 quint32 QCoapOption::uintValue() const
 {
     Q_D(const QCoapOption);
 
     quint32 intValue = 0;
-    for (int i = 0; i < d->value.size(); i++)
-        intValue |= static_cast<quint8>(d->value.at(i)) << (8 * i);
+    // A uint option holds at most 4 bytes (32 bits). Stop at 4 so the shift 
can
+    // never reach 32 bits (undefined behaviour), and cast to the result type
+    // before shifting to avoid signed overflow on the top byte.
+    for (int i = 0; i < d->value.size() && i < 4; ++i)
+        intValue |= static_cast<quint32>(static_cast<quint8>(d->value.at(i))) 
<< (8 * i);
 
     return intValue;
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/qtcoap-everywhere-src-6.11.2/src/coap/qcoapprotocol.cpp 
new/qtcoap-everywhere-src-6.12.0/src/coap/qcoapprotocol.cpp
--- old/qtcoap-everywhere-src-6.11.2/src/coap/qcoapprotocol.cpp 2026-08-11 
19:38:38.000000000 +0200
+++ new/qtcoap-everywhere-src-6.12.0/src/coap/qcoapprotocol.cpp 2026-09-26 
12:11:53.000000000 +0200
@@ -324,6 +324,13 @@
     Q_ASSERT(QThread::currentThread() == q->thread());
 
     QSharedPointer<QCoapInternalReply> reply(decode(data, sender));
+    if (!reply) {
+        qCDebug(lcCoapProtocol).nospace() << "QtCoap: Discarding malformed 
frame from "
+                                          << sender << " (" << data.size()
+                                          << " bytes): not a valid CoAP 
message";
+        return;
+    }
+
     const QCoapMessage *messageReceived = reply->message();
 
     QCoapInternalRequest *request = nullptr;
@@ -691,6 +698,9 @@
 {
     Q_Q(QCoapProtocol);
     QCoapInternalReply *reply = QCoapInternalReply::createFromFrame(data, q);
+    if (!reply)
+        return nullptr;
+
     reply->setSenderAddress(sender);
 
     return reply;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/qtcoap-everywhere-src-6.11.2/src/coap/qcoapreply.cpp 
new/qtcoap-everywhere-src-6.12.0/src/coap/qcoapreply.cpp
--- old/qtcoap-everywhere-src-6.11.2/src/coap/qcoapreply.cpp    2026-08-11 
19:38:38.000000000 +0200
+++ new/qtcoap-everywhere-src-6.12.0/src/coap/qcoapreply.cpp    2026-09-26 
12:11:53.000000000 +0200
@@ -186,7 +186,7 @@
     message details. The \a reply parameter is the QCoapReply itself for
     convenience.
 
-    \sa QCoapClient::finished(), isFinished(), finished(), notified()
+    \sa QCoapClient::finished(), isFinished(), finished(), aborted()
 */
 
 /*!
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/qtcoap-everywhere-src-6.11.2/src/coap/qcoapresourcediscoveryreply.cpp 
new/qtcoap-everywhere-src-6.12.0/src/coap/qcoapresourcediscoveryreply.cpp
--- old/qtcoap-everywhere-src-6.11.2/src/coap/qcoapresourcediscoveryreply.cpp   
2026-08-11 19:38:38.000000000 +0200
+++ new/qtcoap-everywhere-src-6.12.0/src/coap/qcoapresourcediscoveryreply.cpp   
2026-09-26 12:11:53.000000000 +0200
@@ -117,7 +117,7 @@
         for (const QByteArray &parameter : parameterList) {
             QString parameterString = QString::fromUtf8(parameter);
             const qsizetype length = parameterString.size();
-            if (parameter.startsWith('<'))
+            if (parameter.startsWith('<') && parameter.endsWith('>'))
                 resource.setPath(parameterString.mid(1, length - 2));
             else if (parameter.startsWith("title="))
                 resource.setTitle(parameterString.mid(6).remove(quote));
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/qtcoap-everywhere-src-6.11.2/tests/auto/qcoapinternalreply/tst_qcoapinternalreply.cpp
 
new/qtcoap-everywhere-src-6.12.0/tests/auto/qcoapinternalreply/tst_qcoapinternalreply.cpp
--- 
old/qtcoap-everywhere-src-6.11.2/tests/auto/qcoapinternalreply/tst_qcoapinternalreply.cpp
   2026-08-11 19:38:38.000000000 +0200
+++ 
new/qtcoap-everywhere-src-6.12.0/tests/auto/qcoapinternalreply/tst_qcoapinternalreply.cpp
   2026-09-26 12:11:53.000000000 +0200
@@ -15,6 +15,11 @@
 private Q_SLOTS:
     void parseReplyPdu_data();
     void parseReplyPdu();
+    void parseInvalidReplyPdu_data();
+    void parseInvalidReplyPdu();
+    void parseExtendedOption();
+    void parseEmptyBlockOption();
+    void nextBlockToSendWithEmptyBlock1();
     void updateReply_data();
     void updateReply();
 };
@@ -137,6 +142,121 @@
     QCOMPARE(reply->message()->payload(), payload.toUtf8());
 }
 
+void tst_QCoapInternalReply::parseInvalidReplyPdu_data()
+{
+    QTest::addColumn<QByteArray>("frame");
+
+    // A CoAP message is at least 4 bytes long (the fixed header). Frames 
shorter
+    // than that must be rejected before the header is dereferenced;
+    // createFromFrame() signals rejection by returning nullptr.
+    QTest::newRow("empty")       << QByteArray();
+    QTest::newRow("one_byte")    << QByteArray::fromHex("50");
+    QTest::newRow("two_bytes")   << QByteArray::fromHex("5045");
+    QTest::newRow("three_bytes") << QByteArray::fromHex("5045fb");
+
+    // Option-parser cases: each frame has a valid 4-byte header (no token)
+    // followed by a single option byte whose encoding demands bytes that are
+    // not present. createFromFrame() must reject the frame rather than read
+    // past the buffer or deliver a silently truncated option.
+
+    // 0x09: option value length 9, but no value bytes follow it.
+    QTest::newRow("option_value_overruns_buffer")   << 
QByteArray::fromHex("50450001" "09");
+    // 0xd0: delta nibble 13 promises one extended delta byte that is missing.
+    QTest::newRow("missing_option_delta_ext_byte")  << 
QByteArray::fromHex("50450001" "d0");
+    // 0x0d: length nibble 13 promises one extended length byte that is 
missing.
+    QTest::newRow("missing_option_length_ext_byte") << 
QByteArray::fromHex("50450001" "0d");
+    // 0x0d + 0xff: extended length yields optionLength 268, far past the 
buffer.
+    QTest::newRow("extended_option_length_overruns") << 
QByteArray::fromHex("50450001" "0d" "ff");
+    // 0xe0 + 0xffff: extended delta 0xffff -> option number 65535 + 269 = 
65804,
+    // which exceeds the 65535 maximum, so the frame is rejected.
+    QTest::newRow("option_number_exceeds_max") << 
QByteArray::fromHex("50450001" "e0" "ffff");
+
+    // Token-length cases.
+    // TKL 9 is reserved (RFC 7252 §3) and must be rejected even when the 9 
token
+    // bytes are present, so this frame includes them.
+    QTest::newRow("reserved_token_length") << QByteArray::fromHex("59450001" 
"aabbccddeeff001122");
+    // TKL 4 is valid, but no token bytes follow the 4-byte header.
+    QTest::newRow("truncated_token")       << QByteArray::fromHex("54450001");
+
+    // RFC 7959 §2.2: SZX value 7 (block size 2048) is reserved. Reject a block
+    // option whose last byte has SZX 7 (low 3 bits set). Value byte 0x07 
encodes
+    // NUM=0, M=0, SZX=7. Option header 0xd1 = delta 13 (extended), length 1.
+    // Block2 (number 23): extended delta byte 0x0a -> 10 + 13 = 23.
+    QTest::newRow("reserved_block2_szx7") << QByteArray::fromHex("50450001" 
"d10a" "07");
+    // Block1 (number 27): extended delta byte 0x0e -> 14 + 13 = 27.
+    QTest::newRow("reserved_block1_szx7") << QByteArray::fromHex("50450001" 
"d10e" "07");
+
+    // RFC 7959 §2.2: a block option value is 0-3 bytes; a 4-byte value is 
rejected
+    // (it would overflow the 20-bit block number). Option header 0xd4 = delta 
13
+    // (extended), length 4; 0x0a -> Block2 (23); last byte 0x04 has a valid 
SZX, so
+    // this exercises the length check rather than the SZX check.
+    QTest::newRow("oversized_block2_value") << QByteArray::fromHex("50450001" 
"d40a" "01020304");
+}
+
+void tst_QCoapInternalReply::parseInvalidReplyPdu()
+{
+    QFETCH(QByteArray, frame);
+
+    QScopedPointer<QCoapInternalReply> 
reply(QCoapInternalReply::createFromFrame(frame));
+    QVERIFY2(reply.isNull(),
+             "createFromFrame() must reject a malformed frame by returning 
nullptr");
+}
+
+void tst_QCoapInternalReply::parseExtendedOption()
+{
+    // RFC 7252 §3.1: a delta/length nibble of 14 is followed by a 16-bit
+    // unsigned integer in network byte order (the value minus 269). Build one
+    // option that uses the 16-bit form for BOTH its delta and its length, with
+    // distinct first/second bytes so the test also verifies the byte order:
+    //   0xee       -> delta nibble 14, length nibble 14
+    //   0x01 0x02  -> extended delta  = 0x0102 = 258 -> option number 258 + 
269 = 527
+    //   0x00 0x03  -> extended length = 0x0003 =   3 -> value length     3 + 
269 = 272
+    //   272 value bytes
+    // A decoder that read one byte instead of two, or used the wrong byte 
order,
+    // would compute different numbers and misalign the rest of the frame.
+    const QByteArray value(272, 'x');
+    const QByteArray frame = QByteArray::fromHex("50450001" "ee" "0102" 
"0003") + value;
+
+    QScopedPointer<QCoapInternalReply> 
reply(QCoapInternalReply::createFromFrame(frame));
+
+    QVERIFY(!reply.isNull());
+    QCOMPARE(reply->message()->optionCount(), 1);
+    const QCoapOption option = reply->message()->options().constFirst();
+    QCOMPARE(static_cast<int>(option.name()), 527);
+    QCOMPARE(option.opaqueValue(), value);
+}
+
+void tst_QCoapInternalReply::parseEmptyBlockOption()
+{
+    // RFC 7959 §2.2: a zero-length Block option is valid and encodes
+    // NUM=0, M=0, SZX=0. Frame: 4-byte header (no token), then a Block2
+    // option (number 23) carrying an empty value.
+    //   0xd0 -> option delta nibble 13 (extended), length nibble 0
+    //   0x0a -> extended delta byte, giving option number 13 + 10 = 23 
(Block2)
+    const QByteArray frame = QByteArray::fromHex("50450001" "d0" "0a");
+    QScopedPointer<QCoapInternalReply> 
reply(QCoapInternalReply::createFromFrame(frame));
+
+    QVERIFY(!reply.isNull());
+    QCOMPARE(reply->currentBlockNumber(), 0u);
+    QVERIFY(!reply->hasMoreBlocksToReceive());
+    QCOMPARE(reply->blockSize(), 16u);
+}
+
+void tst_QCoapInternalReply::nextBlockToSendWithEmptyBlock1()
+{
+    // A zero-length Block1 option is valid (RFC 7959 §2.2), encoding M=0, so
+    // there is no next block to send. nextBlockToSend() must return -1 rather
+    // than read optionData[-1]. Frame: 4-byte header, then a Block1 option
+    // (number 27) with an empty value.
+    //   0xd0 -> option delta nibble 13 (extended), length nibble 0
+    //   0x0e -> extended delta byte, giving option number 13 + 14 = 27 
(Block1)
+    const QByteArray frame = QByteArray::fromHex("50450001" "d0" "0e");
+    QScopedPointer<QCoapInternalReply> 
reply(QCoapInternalReply::createFromFrame(frame));
+
+    QVERIFY(!reply.isNull());
+    QCOMPARE(reply->nextBlockToSend(), -1);
+}
+
 void tst_QCoapInternalReply::updateReply_data()
 {
     QTest::addColumn<QByteArray>("data");
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/qtcoap-everywhere-src-6.11.2/tests/auto/qcoapoption/tst_qcoapoption.cpp 
new/qtcoap-everywhere-src-6.12.0/tests/auto/qcoapoption/tst_qcoapoption.cpp
--- old/qtcoap-everywhere-src-6.11.2/tests/auto/qcoapoption/tst_qcoapoption.cpp 
2026-08-11 19:38:38.000000000 +0200
+++ new/qtcoap-everywhere-src-6.12.0/tests/auto/qcoapoption/tst_qcoapoption.cpp 
2026-09-26 12:11:53.000000000 +0200
@@ -15,6 +15,8 @@
     void constructWithQByteArray();
     void constructWithQString();
     void constructWithInteger();
+    void uintValueIgnoresExcessBytes();
+    void uintValueHandlesHighBitBytes();
     void constructWithUtf8Characters();
 };
 
@@ -80,6 +82,30 @@
     QCOMPARE(option.uintValue(), value);
 }
 
+void tst_QCoapOption::uintValueIgnoresExcessBytes()
+{
+    // A uint option holds at most 4 bytes. uintValue() must not shift past 32
+    // bits when the value is longer; only the low 4 bytes are used 
(little-endian).
+    // Size2 has no per-option size check in setValue(), so all 6 bytes are 
stored.
+    const QByteArray value = QByteArray::fromHex("010203040506");
+    QCoapOption option(QCoapOption::Size2, value);
+
+    QCOMPARE(option.uintValue(), 0x04030201u);
+}
+
+void tst_QCoapOption::uintValueHandlesHighBitBytes()
+{
+    // A byte with the high bit set must be zero-extended, not sign-extended:
+    QCOMPARE(QCoapOption(QCoapOption::Size2, 
QByteArray::fromHex("ff")).uintValue(),
+             0xffu);
+
+    // All four bytes have the high bit set: the top byte exercises the 
byte<<24
+    // path (signed-overflow avoidance), and the result confirms every byte is
+    // zero-extended rather than sign-extended.
+    QCOMPARE(QCoapOption(QCoapOption::Size2, 
QByteArray::fromHex("deadbeef")).uintValue(),
+             0xefbeaddeu);
+}
+
 void tst_QCoapOption::constructWithUtf8Characters()
 {
     QByteArray ba = "\xc3\xa9~\xce\xbb\xe2\x82\xb2";
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/qtcoap-everywhere-src-6.11.2/tests/auto/qcoapresource/tst_qcoapresource.cpp 
new/qtcoap-everywhere-src-6.12.0/tests/auto/qcoapresource/tst_qcoapresource.cpp
--- 
old/qtcoap-everywhere-src-6.11.2/tests/auto/qcoapresource/tst_qcoapresource.cpp 
    2026-08-11 19:38:38.000000000 +0200
+++ 
new/qtcoap-everywhere-src-6.12.0/tests/auto/qcoapresource/tst_qcoapresource.cpp 
    2026-09-26 12:11:53.000000000 +0200
@@ -104,6 +104,21 @@
                            << maximumSizeList
                            << observableList
                            << coreLinks;
+
+    // A link that opens with '<' but has no closing '>' is malformed (RFC 
6690).
+    // It must be dropped without disturbing the valid entries around it: here
+    // "</malformed" is discarded and only "</valid>" yields a resource.
+    QTest::newRow("malformed_unterminated_link")
+            << 1
+            << QString("10.20.30.40")
+            << QList<QString>{ QStringLiteral("/valid") }
+            << QList<QString>{ QString() }
+            << QList<QString>{ QString() }
+            << QList<uint>{ 0u }
+            << QList<QString>{ QString() }
+            << QList<int>{ -1 }
+            << QList<bool>{ false }
+            << QByteArray("</valid>,</malformed");
 }
 
 void tst_QCoapResource::parseCoreLink()

Reply via email to