const int maxPrepareQueueSize = 1024;
-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<QBluezConst::AttCommand>(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<QBluezConst::AttCommand>(data[1]);
qCDebug(QT_BT_BLUEZ) << "Error:" << errorCode << "Error description:" << errorString
<< "last command:" << lastCommand << "handle:" << handle;
+
+ return true;
}
static int getUuidSize(const QBluetoothUuid &uuid)
{
Q_ASSERT(charData);
Q_ASSERT(data);
+ Q_ASSERT(elementLength >= 5);
QLowEnergyHandle attributeHandle = bt_get_le16(&data[0]);
charData->properties =
// Bluetooth LE data comes as little endian
if (elementLength == 7) // 16 bit uuid
charData->uuid = QBluetoothUuid(bt_get_le16(&data[5]));
- else
+ else if (elementLength == 21) // 128 bit uuid
charData->uuid = QUuid::fromBytes(&data[5], QSysInfo::LittleEndian);
qCDebug(QT_BT_BLUEZ) << "Found handle:" << Qt::hex << attributeHandle
{
Q_ASSERT(foundServices);
Q_ASSERT(data);
+ Q_ASSERT(elementLength >= 6);
QLowEnergyHandle attributeHandle = bt_get_le16(&data[0]);
// 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(QUuid::fromBytes(&data[6], QSysInfo::LittleEndian));
qCDebug(QT_BT_BLUEZ) << "Found included service: " << Qt::hex
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<QBluezConst::AttCommand>(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<QBluezConst::AttCommand>(response.constData()[1]);
isErrorResponse = true;
}
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;
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();
}
/* packet format:
- * if GATT_CHARACTERISTIC discovery
+ * if GATT_CHARACTERISTIC discovery (Spec 5.3, Vol. 3, Part G, 4.6)
* <opcode><elementLength>
* [<handle><property><charHandle><uuid>]+
+ * 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)
* <opcode><elementLength>
* [<handle><startHandle_included><endHandle_included><uuid>]+
+ * 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;
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) {
void QLowEnergyControllerPrivateBluez::processUnsolicitedReply(const QByteArray &payload)
{
+ Q_ASSERT(!payload.isEmpty());
+
const char *data = payload.constData();
- bool isNotification = (static_cast<QBluezConst::AttCommand>(data[0])
+ const auto command = static_cast<QBluezConst::AttCommand>(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()) {