diff --git a/CVE-2025-23050-qtconnectivity-6.5.diff b/CVE-2025-23050-qtconnectivity-6.5.diff new file mode 100644 index 0000000000000000000000000000000000000000..3e95459202c3364967a0b3269712d65d9228703a --- /dev/null +++ b/CVE-2025-23050-qtconnectivity-6.5.diff @@ -0,0 +1,221 @@ +From 88c30a23dcff5e7c16a54f93accf743847e22617 Mon Sep 17 00:00:00 2001 +From: Ivan Solovev +Date: Thu, 02 Jan 2025 16:48:49 +0100 +Subject: [PATCH] QLowEnergyControllerPrivateBluez: guard against malformed replies + +The QLowEnergyControllerPrivateBluez::l2cpReadyRead() slot reads the +data from a Bluetooth L2CAP socket and then tries to process it +according to ATT protocol specs. + +However, the code was missing length and sanity checks at some +codepaths in processUnsolicitedReply() and processReply() helper +methods, simply relying on the data to be in the proper format. + +This patch adds some minimal checks to make sure that we do not read +past the end of the received array and do not divide by zero. + +This problem was originally pointed out by Marc Mutz in an unrelated +patch. + +Pick-to: 5.15 +Change-Id: I8dcfe031f70ad61fa3d87dc9d771c3fabc6d0edc +Reviewed-by: Alex Blasche +Reviewed-by: Juha Vuolle +(cherry picked from commit aecbd657c841a2a8c74631ceac96b8ff1f03ab5c) +Reviewed-by: Qt Cherry-pick Bot +(cherry picked from commit 53e991671f725c136e9aa824c59ec13934c63fb4) +(cherry picked from commit 465e3f3112a9c158aa6dd2f8b9439ae6c2de336f) +--- + +diff --git a/src/bluetooth/qlowenergycontroller_bluez.cpp b/src/bluetooth/qlowenergycontroller_bluez.cpp +index 4a2248c..b4bad61 100644 +--- a/src/bluetooth/qlowenergycontroller_bluez.cpp ++++ b/src/bluetooth/qlowenergycontroller_bluez.cpp +@@ -80,14 +80,15 @@ + return QBluetoothUuid(qtdst); + } + +-static void dumpErrorInformation(const QByteArray &response) ++/* returns false if the format is incorrect */ ++static bool dumpErrorInformation(const QByteArray &response) + { + const char *data = response.constData(); + if (response.size() != 5 + || (static_cast(data[0]) + != QBluezConst::AttCommand::ATT_OP_ERROR_RESPONSE)) { + qCWarning(QT_BT_BLUEZ) << QLatin1String("Not a valid error response"); +- return; ++ return false; + } + + QBluezConst::AttCommand lastCommand = static_cast(data[1]); +@@ -142,6 +143,8 @@ + + qCDebug(QT_BT_BLUEZ) << "Error:" << errorCode << "Error description:" << errorString + << "last command:" << lastCommand << "handle:" << handle; ++ ++ return true; + } + + static int getUuidSize(const QBluetoothUuid &uuid) +@@ -919,6 +922,7 @@ + { + Q_ASSERT(charData); + Q_ASSERT(data); ++ Q_ASSERT(elementLength >= 5); + + QLowEnergyHandle attributeHandle = bt_get_le16(&data[0]); + charData->properties = +@@ -927,7 +931,7 @@ + + if (elementLength == 7) // 16 bit uuid + charData->uuid = QBluetoothUuid(bt_get_le16(&data[5])); +- else ++ else if (elementLength == 21) // 128 bit uuid + charData->uuid = convert_uuid128((quint128 *)&data[5]); + + qCDebug(QT_BT_BLUEZ) << "Found handle:" << Qt::hex << attributeHandle +@@ -944,6 +948,7 @@ + { + Q_ASSERT(foundServices); + Q_ASSERT(data); ++ Q_ASSERT(elementLength >= 6); + + QLowEnergyHandle attributeHandle = bt_get_le16(&data[0]); + +@@ -953,9 +958,14 @@ + // data[2] -> included service start handle + // data[4] -> included service end handle + ++ // TODO: Spec v. 5.3, Vol. 3, Part G, 4.5.1 mentions that only ++ // 16-bit UUID can be returned here. If the UUID is 128-bit, ++ // then it is omitted from the response, and should be requested ++ // separately with the ATT_READ_REQ command. ++ + if (elementLength == 8) //16 bit uuid + foundServices->append(QBluetoothUuid(bt_get_le16(&data[6]))); +- else ++ else if (elementLength == 22) // 128 bit uuid + foundServices->append(convert_uuid128((quint128 *) &data[6])); + + qCDebug(QT_BT_BLUEZ) << "Found included service: " << Qt::hex +@@ -964,17 +974,29 @@ + return attributeHandle; + } + ++Q_DECL_COLD_FUNCTION ++static void reportMalformedData(QBluezConst::AttCommand cmd, const QByteArray &response) ++{ ++ qCDebug(QT_BT_BLUEZ, "%s malformed data: %s", qt_getEnumName(cmd), ++ response.toHex().constData()); ++} ++ + void QLowEnergyControllerPrivateBluez::processReply( + const Request &request, const QByteArray &response) + { + Q_Q(QLowEnergyController); + ++ // We already have an isEmpty() check at the only calling site that reads ++ // incoming data, so Q_ASSERT is enough. ++ Q_ASSERT(!response.isEmpty()); ++ + QBluezConst::AttCommand command = static_cast(response.constData()[0]); + + bool isErrorResponse = false; + // if error occurred 2. byte is previous request type + if (command == QBluezConst::AttCommand::ATT_OP_ERROR_RESPONSE) { +- dumpErrorInformation(response); ++ if (!dumpErrorInformation(response)) ++ return; + command = static_cast(response.constData()[1]); + isErrorResponse = true; + } +@@ -987,6 +1009,10 @@ + if (isErrorResponse) { + mtuSize = ATT_DEFAULT_LE_MTU; + } else { ++ if (response.size() < 3) { ++ reportMalformedData(command, response); ++ break; ++ } + const char *data = response.constData(); + quint16 mtu = bt_get_le16(&data[1]); + mtuSize = mtu; +@@ -1015,8 +1041,15 @@ + break; + } + ++ // response[1] == elementLength. According to the spec it should be ++ // at least 4 bytes. See Spec v5.3, Vol 3, Part F, 3.4.4.10 ++ if (response.size() < 2 || response[1] < 4) { ++ reportMalformedData(command, response); ++ break; ++ } ++ + QLowEnergyHandle start = 0, end = 0; +- const quint16 elementLength = response.constData()[1]; ++ const quint16 elementLength = response.constData()[1]; // value checked above + const quint16 numElements = (response.size() - 2) / elementLength; + quint16 offset = 2; + const char *data = response.constData(); +@@ -1092,16 +1125,25 @@ + } + + /* packet format: +- * if GATT_CHARACTERISTIC discovery ++ * if GATT_CHARACTERISTIC discovery (Spec 5.3, Vol. 3, Part G, 4.6) + * + * []+ ++ * The minimum elementLength is 7 bytes (uuid is always included) + * +- * if GATT_INCLUDE discovery ++ * if GATT_INCLUDE discovery (Spec 5.3, Vol. 3, Part G, 4.5.1) + * + * []+ ++ * The minimum elementLength is 6 bytes (uuid can be omitted). + * + * The uuid can be 16 or 128 bit. + */ ++ ++ const quint8 minimumElementLength = attributeType == GATT_CHARACTERISTIC ? 7 : 6; ++ if (response.size() < 2 || response[1] < minimumElementLength) { ++ reportMalformedData(command, response); ++ break; ++ } ++ + QLowEnergyHandle lastHandle; + const quint16 elementLength = response.constData()[1]; + const quint16 numElements = (response.size() - 2) / elementLength; +@@ -1298,6 +1340,12 @@ + break; + } + ++ // Spec 5.3, Vol. 3, Part F, 3.4.3.2 ++ if (response.size() < 6) { ++ reportMalformedData(command, response); ++ break; ++ } ++ + const quint8 format = response[1]; + quint16 elementLength; + switch (format) { +@@ -1735,9 +1783,18 @@ + + void QLowEnergyControllerPrivateBluez::processUnsolicitedReply(const QByteArray &payload) + { ++ Q_ASSERT(!payload.isEmpty()); ++ + const char *data = payload.constData(); +- bool isNotification = (static_cast(data[0]) ++ const auto command = static_cast(data[0]); ++ bool isNotification = (command + == QBluezConst::AttCommand::ATT_OP_HANDLE_VAL_NOTIFICATION); ++ ++ if (payload.size() < 3) { ++ reportMalformedData(command, payload); ++ return; ++ } ++ + const QLowEnergyHandle changedHandle = bt_get_le16(&data[1]); + + if (QT_BT_BLUEZ().isDebugEnabled()) { diff --git a/qt6-qtconnectivity.spec b/qt6-qtconnectivity.spec index bb5e1f861df095c55195d9c43c52d3da67c0c8e2..0d6cffc877f8449a43715b8a2f5d34b577c48629 100644 --- a/qt6-qtconnectivity.spec +++ b/qt6-qtconnectivity.spec @@ -6,7 +6,7 @@ Summary: Qt6 - Connectivity components Name: qt6-%{qt_module} Version: 6.5.2 -Release: 2 +Release: 3 # See LICENSE.GPL3, respectively, for exception details License: LGPL-3.0-only OR GPL-3.0-only WITH Qt-GPL-exception-1.0 @@ -16,6 +16,8 @@ Url: http://qt.io Source0: https://download.qt.io/official_releases/qt/%{majmin}/%{version}/submodules/%{qt_module}-everywhere-src-%{version}.tar.xz +Patch6001: CVE-2025-23050-qtconnectivity-6.5.diff + # filter qml provides %global __provides_exclude_from ^%{_qt6_archdatadir}/qml/.*\\.so$ @@ -104,6 +106,9 @@ popd %endif %changelog +* Mon Jan 20 2025 Funda Wang - 6.5.2-3 +- fix CVE-2025-23050 + * Wed Nov 20 2024 Funda Wang - 6.5.2-2 - adopt to new cmake macro