nimble/host: Fix possible overflow in ble_gatts_peer_cl_sup_feat_update
This function would write pass conn->bhc_gatt_svr.peer_cl_sup_feat
buffer. Since supported features are likely to be small (currently
only 3 bits are defined) lets keep this simple and just make local
copy before validation.
diff --git a/nimble/host/src/ble_gatts.c b/nimble/host/src/ble_gatts.c
index 2c18cc4..bf50305 100644
--- a/nimble/host/src/ble_gatts.c
+++ b/nimble/host/src/ble_gatts.c
@@ -1613,8 +1613,9 @@
ble_gatts_peer_cl_sup_feat_update(uint16_t conn_handle, struct os_mbuf *om)
{
struct ble_hs_conn *conn;
+ uint8_t feat[BLE_GATT_CHR_CLI_SUP_FEAT_SZ] = {};
+ uint16_t len;
int rc = 0;
- int feat_idx = 0;
int i;
BLE_HS_LOG(DEBUG, "");
@@ -1623,46 +1624,42 @@
return BLE_ATT_ERR_INSUFFICIENT_RES;
}
+ /* RFU bits are ignored so we can skip any bytes larger than supported */
+ len = os_mbuf_len(om);
+ if (len > BLE_GATT_CHR_CLI_SUP_FEAT_SZ) {
+ len = BLE_GATT_CHR_CLI_SUP_FEAT_SZ;
+ }
+
+ if (os_mbuf_copydata(om, 0, len, feat) < 0) {
+ return BLE_ATT_ERR_UNLIKELY;
+ }
+
+ /* clear RFU bits */
+ for (i = 0; i < BLE_GATT_CHR_CLI_SUP_FEAT_SZ; i++) {
+ feat[i] &= (BLE_GATT_CHR_CLI_SUP_FEAT_MASK >> (8 * i));
+ }
+
ble_hs_lock();
conn = ble_hs_conn_find(conn_handle);
if (conn == NULL) {
rc = BLE_ATT_ERR_UNLIKELY;
goto done;
}
- if (om->om_len == 0) {
- /* Nothing to do */
- goto done;
- } else if (os_mbuf_len(om) > BLE_ATT_ATTR_MAX_LEN) {
- rc = BLE_ATT_ERR_INSUFFICIENT_RES;
- goto done;
- }
- while(om) {
- for (i = 0; i < om->om_len; i++) {
- /**
- * Disabling already enabled features is not permitted
- * (Vol. 3, Part F, 3.3.3)
- * If value of saved octet, as uint8_t, is greatet than requested
- * it means more bits are set.
- */
- if (conn->bhc_gatt_svr.peer_cl_sup_feat[feat_idx] >
- om->om_data[i]) {
- rc = BLE_ATT_ERR_VALUE_NOT_ALLOWED;
- goto done;
- }
-
- /* All RFU bits should be unset */
- if (feat_idx == 0) {
- om->om_data[i] &= BLE_GATT_CHR_CLI_SUP_FEAT_MASK;
- }
-
- conn->bhc_gatt_svr.peer_cl_sup_feat[feat_idx] |= om->om_data[i];
-
- feat_idx++;
+ /**
+ * Disabling already enabled features is not permitted
+ * (Vol. 3, Part F, 3.3.3)
+ */
+ for (i = 0; i < BLE_GATT_CHR_CLI_SUP_FEAT_SZ; i++) {
+ if ((conn->bhc_gatt_svr.peer_cl_sup_feat[i] & feat[i]) !=
+ conn->bhc_gatt_svr.peer_cl_sup_feat[i]) {
+ rc = BLE_ATT_ERR_VALUE_NOT_ALLOWED;
+ goto done;
}
- om = SLIST_NEXT(om, om_next);
}
+ memcpy(conn->bhc_gatt_svr.peer_cl_sup_feat, feat, BLE_GATT_CHR_CLI_SUP_FEAT_SZ);
+
done:
ble_hs_unlock();
return rc;