From 2ece85f3acb7b293a0a5d3a561626b1b22363413 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5vard=20Reierstad?= Date: Thu, 5 Jun 2025 15:48:25 +0200 Subject: [PATCH 1/7] [nrf fromtree] Bluetooth: Host: Update LE legacy pairing check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Updates the LE legacy pairing procedure as a result of errata ES-24491. New part: If the initiating device receives an LP_CONFIRM_R value that is equal to the LP_CONFIRM_I value, the pairing process shall be aborted and fail with "Confirm Value Failed" as reason. Signed-off-by: Håvard Reierstad (cherry picked from commit 2a8118c9454542119e9d06c49093e77da609a3ab) Signed-off-by: Håvard Reierstad (cherry picked from commit 2c900ff71beb178c58b4d0f295e003bfbce94da8) --- subsys/bluetooth/host/smp.c | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/subsys/bluetooth/host/smp.c b/subsys/bluetooth/host/smp.c index 916bb661bb98..805b18f33c48 100644 --- a/subsys/bluetooth/host/smp.c +++ b/subsys/bluetooth/host/smp.c @@ -2555,22 +2555,35 @@ static uint8_t legacy_pairing_req(struct bt_smp *smp) static uint8_t legacy_pairing_random(struct bt_smp *smp) { struct bt_conn *conn = smp->chan.chan.conn; - uint8_t tmp[16]; + uint8_t tmp[16], cfm_i[16]; int err; LOG_DBG(""); - /* calculate confirmation */ + /* calculate LP_CONFIRM_R */ err = smp_c1(smp->tk, smp->rrnd, smp->preq, smp->prsp, &conn->le.init_addr, &conn->le.resp_addr, tmp); if (err) { return BT_SMP_ERR_UNSPECIFIED; } + /* calculate LP_CONFIRM_I */ + err = smp_c1(smp->tk, smp->prnd, smp->preq, smp->prsp, + &conn->le.init_addr, &conn->le.resp_addr, cfm_i); + if (err) { + return BT_SMP_ERR_UNSPECIFIED; + } + LOG_DBG("pcnf %s", bt_hex(smp->pcnf, 16)); - LOG_DBG("cfm %s", bt_hex(tmp, 16)); + LOG_DBG("cfm (remote) %s", bt_hex(tmp, 16)); + LOG_DBG("cfm (local) %s", bt_hex(cfm_i, 16)); - if (memcmp(smp->pcnf, tmp, sizeof(smp->pcnf))) { + /* Core Specification, Vol 3, Part H, section 2.3.5.5 (Errata ES-24491): If the computed + * LP_CONFIRM_R value is not equal to the received LP_CONFIRM_R value, or the received + * LP_CONFIRM_R value is equal to the LP_CONFIRM_I value, fail pairing. + */ + if (memcmp(smp->pcnf, tmp, sizeof(smp->pcnf)) || + !memcmp(smp->pcnf, cfm_i, sizeof(smp->pcnf))) { return BT_SMP_ERR_CONFIRM_FAILED; } From 76ad82c6c0ca7425c3a232f9553a35b3cb3f3666 Mon Sep 17 00:00:00 2001 From: Szymon Janc Date: Wed, 11 Jun 2025 16:59:55 +0200 Subject: [PATCH 2/7] [nrf fromtree] Bluetooth: Host: Fix SMP Pairing failed code on invalid Public Key MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Core Specification 6.1 clarified exptected erro code in case peer sending invalid Public Key. In case pairing is aborted during or immediately after Public Key Exchange phase has completed (which is the case here) expected reason code is set to "DHKey Check Failed". This was affecting SM/CEN/KDU/BI-04-C and SM/PER/KDU/BI-04-C qualification test cases. Signed-off-by: Szymon Janc (cherry picked from commit 9b27a472f42e92fc290915e7529e1e9e91631a7b) Signed-off-by: Håvard Reierstad (cherry picked from commit 8720fcc41232657f771b09ec1627b1bbc279d471) --- subsys/bluetooth/host/smp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/subsys/bluetooth/host/smp.c b/subsys/bluetooth/host/smp.c index 805b18f33c48..ff920fb0f3a3 100644 --- a/subsys/bluetooth/host/smp.c +++ b/subsys/bluetooth/host/smp.c @@ -4499,7 +4499,7 @@ static uint8_t smp_public_key(struct bt_smp *smp, struct net_buf *buf) } } else if (!bt_pub_key_is_valid(smp->pkey)) { LOG_WRN("Received invalid public key"); - return BT_SMP_ERR_INVALID_PARAMS; + return BT_SMP_ERR_DHKEY_CHECK_FAILED; } if (IS_ENABLED(CONFIG_BT_CENTRAL) && From d2920ce215aa04487d38c9d03567c3ec9f9392d2 Mon Sep 17 00:00:00 2001 From: Nirav Agrawal Date: Fri, 13 Jun 2025 10:44:29 +0530 Subject: [PATCH 3/7] [nrf fromtree] bluetooth: host: gatt: fix null-ptr access if no include-svc userdata MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Issue: There is a bus-fault while accessing empty userdata structure pointer if application does not include any include service userdata instance (which consist of UUID list of included service) but service array has defined dummy entry for it assumed to be overridden by app during initial flow. - For example, the issue has happened in case of tmap-central sample without "CONFIG_BT_OTS" support. there are some MCS attributes dependent on OTS service because of that "BT_GATT_INCLUDE_SERVICE(NULL)" entry is added as part of service definition. The given entry does not have userdata handler defined and is expecting to be overriden by the app if it will be included. During "bt_mcs_init()" call, "mcs.attrs[i].user_data" is not populated with any attr-instance pointer. This makes CPU to access null-address during reading local-database include-service attribute which was not provided by the app but the include-service entry was added to the db. - Fix: Adding condition to check if user-data has null address, and returning back to avoid any hard-faults. Signed-off-by: Nirav Agrawal (cherry picked from commit 5a8189bf2afec221b925065854a714b1088ccddc) Signed-off-by: Håvard Reierstad (cherry picked from commit 57d8acee344a9fcc77116a02cee99624a548bf9a) --- include/zephyr/bluetooth/gatt.h | 1 + subsys/bluetooth/host/gatt.c | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/include/zephyr/bluetooth/gatt.h b/include/zephyr/bluetooth/gatt.h index 3597c7c5fdb1..2c7d36a67588 100644 --- a/include/zephyr/bluetooth/gatt.h +++ b/include/zephyr/bluetooth/gatt.h @@ -918,6 +918,7 @@ ssize_t bt_gatt_attr_read_service(struct bt_conn *conn, * Read include service attribute value from local database storing the result * into buffer after encoding it. * @note Only use this with attributes which user_data is a ``bt_gatt_include``. + * The function returns EINVAL if @p attr or @p attr->user_data is NULL. * * @param conn Connection object. * @param attr Attribute to read. diff --git a/subsys/bluetooth/host/gatt.c b/subsys/bluetooth/host/gatt.c index 3092801554a1..be642fec9fe7 100644 --- a/subsys/bluetooth/host/gatt.c +++ b/subsys/bluetooth/host/gatt.c @@ -1917,6 +1917,10 @@ ssize_t bt_gatt_attr_read_included(struct bt_conn *conn, const struct bt_gatt_attr *attr, void *buf, uint16_t len, uint16_t offset) { + if ((attr == NULL) || (attr->user_data == NULL)) { + return -EINVAL; + } + struct bt_gatt_attr *incl = attr->user_data; uint16_t handle = bt_gatt_attr_get_handle(incl); struct bt_uuid *uuid = incl->user_data; From 1f2144ca48bf504b6c5565a2e39bc6e778488ed0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5vard=20Reierstad?= Date: Tue, 15 Jul 2025 07:43:33 +0200 Subject: [PATCH 4/7] [nrf fromtree] Bluetooth: Host: l2cap: Fix MPS/MTU confusion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The implementation used BT_L2CAP_ECRED_MIN_MTU to check the lower limits of both MTU and MPS, instead of BT_L2CAP_ECRED_MIN_MPS for MPS. While these are the same here, confusion may arise. This commit fixes the confusion. Signed-off-by: Håvard Reierstad (cherry picked from commit d989188f29e6d19c9b7ec0458becc868632d939b) Signed-off-by: Håvard Reierstad (cherry picked from commit 01ec0ff5bd167631bc8078b88ac1767f9ac6d866) --- subsys/bluetooth/host/l2cap.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/subsys/bluetooth/host/l2cap.c b/subsys/bluetooth/host/l2cap.c index 9fa5638d9617..ac3bd8eeb2b1 100644 --- a/subsys/bluetooth/host/l2cap.c +++ b/subsys/bluetooth/host/l2cap.c @@ -46,6 +46,7 @@ LOG_MODULE_REGISTER(bt_l2cap, CONFIG_BT_L2CAP_LOG_LEVEL); #define CHAN_RX(_w) CONTAINER_OF(_w, struct bt_l2cap_le_chan, rx_work) #define L2CAP_LE_MIN_MTU 23 +#define L2CAP_LE_MIN_MPS 23 #define L2CAP_LE_MAX_CREDITS (BT_BUF_ACL_RX_COUNT - 1) @@ -1478,7 +1479,7 @@ static void le_conn_req(struct bt_l2cap *l2cap, uint8_t ident, LOG_DBG("psm 0x%02x scid 0x%04x mtu %u mps %u credits %u", psm, scid, mtu, mps, credits); - if (mtu < L2CAP_LE_MIN_MTU || mps < L2CAP_LE_MIN_MTU) { + if (mtu < L2CAP_LE_MIN_MTU || mps < L2CAP_LE_MIN_MPS) { LOG_ERR("Invalid LE-Conn Req params: mtu %u mps %u", mtu, mps); return; } @@ -1577,7 +1578,7 @@ static void le_ecred_conn_req(struct bt_l2cap *l2cap, uint8_t ident, LOG_DBG("psm 0x%02x mtu %u mps %u credits %u", psm, mtu, mps, credits); - if (mtu < BT_L2CAP_ECRED_MIN_MTU || mps < BT_L2CAP_ECRED_MIN_MTU) { + if (mtu < BT_L2CAP_ECRED_MIN_MTU || mps < BT_L2CAP_ECRED_MIN_MPS) { LOG_ERR("Invalid ecred conn req params. mtu %u mps %u", mtu, mps); result = BT_L2CAP_LE_ERR_INVALID_PARAMS; goto response; @@ -1685,7 +1686,7 @@ static void le_ecred_reconf_req(struct bt_l2cap *l2cap, uint8_t ident, mtu = sys_le16_to_cpu(req->mtu); mps = sys_le16_to_cpu(req->mps); - if (mps < BT_L2CAP_ECRED_MIN_MTU) { + if (mps < BT_L2CAP_ECRED_MIN_MPS) { result = BT_L2CAP_RECONF_OTHER_UNACCEPT; goto response; } From 8ce8aad8f102ab9f53f943526d87a3fab980d7a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5vard=20Reierstad?= Date: Tue, 22 Jul 2025 13:25:11 +0200 Subject: [PATCH 5/7] [nrf fromtree] Bluetooth: Host: Add l2cap credit param checks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds a missing requirement from Core Spec V6.0 Vol 3.A chapters 10.1 and 10.2 to ignore L2CAP_FLOW_CONTROL_CREDIT_IND packets with the credit value set to 0. Matches existing credit-related functions by checking that the CID is in the dynamic range (you can't add credits to fixed channels). Signed-off-by: Håvard Reierstad (cherry picked from commit 2b42a1d81d981d55e0a5c6e3cd96caee13b195ea) Signed-off-by: Håvard Reierstad (cherry picked from commit b07b1bba4aec25dc602f00301a2a8ed72bc4b0de) --- subsys/bluetooth/host/l2cap.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/subsys/bluetooth/host/l2cap.c b/subsys/bluetooth/host/l2cap.c index ac3bd8eeb2b1..9ecdc72c0aa7 100644 --- a/subsys/bluetooth/host/l2cap.c +++ b/subsys/bluetooth/host/l2cap.c @@ -2158,6 +2158,17 @@ static void le_credits(struct bt_l2cap *l2cap, uint8_t ident, cid = sys_le16_to_cpu(ev->cid); credits = sys_le16_to_cpu(ev->credits); + if (!L2CAP_LE_CID_IS_DYN(cid)) { + LOG_WRN("Can't add credits to non-dynamic channel %p (cid 0x%04x)", &l2cap->chan, + cid); + return; + } + + if (credits == 0U) { + LOG_WRN("Ignoring zero credit packet"); + return; + } + LOG_DBG("cid 0x%04x credits %u", cid, credits); chan = bt_l2cap_le_lookup_tx_cid(conn, cid); From 2a55f0500c2b64a7c4f2e953d25b82cdaef97cba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5vard=20Reierstad?= Date: Wed, 16 Jul 2025 08:55:06 +0200 Subject: [PATCH 6/7] [nrf fromtree] Bluetooth: Host: Add conn rsp param check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Check whether the connection response parameters both with and without ECRED are within the valid ranges from the Bluetooth Core Specification (part 3.A.4 v6.0). Changes validation checks in requests to match the same pattern. Signed-off-by: Håvard Reierstad (cherry picked from commit 7debc8a8f53d4832dfc9cd4dd9debff3d8648079) Signed-off-by: Håvard Reierstad (cherry picked from commit 4c976e3ff28fceec1203f5a86cd5399675795f0a) --- include/zephyr/bluetooth/l2cap.h | 14 +++++++ subsys/bluetooth/host/l2cap.c | 52 +++++++++++++++++++++----- subsys/bluetooth/host/l2cap_internal.h | 3 ++ 3 files changed, 60 insertions(+), 9 deletions(-) diff --git a/include/zephyr/bluetooth/l2cap.h b/include/zephyr/bluetooth/l2cap.h index f8781d791f01..380b52ad508d 100644 --- a/include/zephyr/bluetooth/l2cap.h +++ b/include/zephyr/bluetooth/l2cap.h @@ -111,6 +111,20 @@ extern "C" { */ #define BT_L2CAP_ECRED_MIN_MPS 64 +/** @brief L2CAP maximum MTU + * + * The maximum MTU for an L2CAP Based Connection. This is the same with or without ECRED. This + * requirement is taken from text in Core 3.A.4.22 and 3.A.4.26 v6.0. + */ +#define BT_L2CAP_MAX_MTU UINT16_MAX + +/** @brief L2CAP maximum MPS + * + * The maximum MPS for an L2CAP Based Connection. This is the same with or without ECRED. This + * requirement is taken from text in Core 3.A.4.22 and 3.A.4.26 v6.0. + */ +#define BT_L2CAP_MAX_MPS 65533 + /** @brief The maximum number of channels in ECRED L2CAP signaling PDUs * * Currently, this is the maximum number of channels referred to in the diff --git a/subsys/bluetooth/host/l2cap.c b/subsys/bluetooth/host/l2cap.c index 9ecdc72c0aa7..caf5424717bc 100644 --- a/subsys/bluetooth/host/l2cap.c +++ b/subsys/bluetooth/host/l2cap.c @@ -1479,11 +1479,6 @@ static void le_conn_req(struct bt_l2cap *l2cap, uint8_t ident, LOG_DBG("psm 0x%02x scid 0x%04x mtu %u mps %u credits %u", psm, scid, mtu, mps, credits); - if (mtu < L2CAP_LE_MIN_MTU || mps < L2CAP_LE_MIN_MPS) { - LOG_ERR("Invalid LE-Conn Req params: mtu %u mps %u", mtu, mps); - return; - } - buf = l2cap_create_le_sig_pdu(BT_L2CAP_LE_CONN_RSP, ident, sizeof(*rsp)); if (!buf) { @@ -1493,6 +1488,16 @@ static void le_conn_req(struct bt_l2cap *l2cap, uint8_t ident, rsp = net_buf_add(buf, sizeof(*rsp)); (void)memset(rsp, 0, sizeof(*rsp)); + /* Validate parameters. Requirements are from Core Spec v6.0, Vol 3.A.4.22. Valid credit + * range is from 0 to UINT16_MAX, thus no credit validation is needed. + */ + if (!IN_RANGE(mtu, L2CAP_LE_MIN_MTU, BT_L2CAP_MAX_MTU) || + !IN_RANGE(mps, L2CAP_LE_MIN_MPS, BT_L2CAP_MAX_MPS)) { + LOG_ERR("Invalid le conn req params: mtu %u mps %u", mtu, mps); + result = BT_L2CAP_LE_ERR_UNACCEPT_PARAMS; + goto rsp; + } + /* Check if there is a server registered */ server = bt_l2cap_server_lookup_psm(psm); if (!server) { @@ -1578,8 +1583,12 @@ static void le_ecred_conn_req(struct bt_l2cap *l2cap, uint8_t ident, LOG_DBG("psm 0x%02x mtu %u mps %u credits %u", psm, mtu, mps, credits); - if (mtu < BT_L2CAP_ECRED_MIN_MTU || mps < BT_L2CAP_ECRED_MIN_MPS) { - LOG_ERR("Invalid ecred conn req params. mtu %u mps %u", mtu, mps); + /* Validate parameters. Requirements are from Core Spec v6.0, Vol 3.A.4.25. */ + if (!IN_RANGE(mtu, BT_L2CAP_ECRED_MIN_MTU, BT_L2CAP_MAX_MTU) || + !IN_RANGE(mps, BT_L2CAP_ECRED_MIN_MPS, BT_L2CAP_MAX_MPS) || + !IN_RANGE(credits, BT_L2CAP_ECRED_CREDITS_MIN, BT_L2CAP_ECRED_CREDITS_MAX)) { + LOG_ERR("Invalid le ecred conn req params: mtu %u mps %u credits %u", mtu, mps, + credits); result = BT_L2CAP_LE_ERR_INVALID_PARAMS; goto response; } @@ -1982,13 +1991,24 @@ static void le_ecred_conn_rsp(struct bt_l2cap *l2cap, uint8_t ident, LOG_DBG("dcid 0x%04x", dcid); - /* If a Destination CID is 0x0000, the channel was not + /* Validate parameters before assignment. Requirements are from Core Spec + * v6.0, Vol 3.A.4.26. If a Destination CID is 0x0000, the channel was not * established. */ - if (!dcid) { + if (dcid == 0U) { bt_l2cap_chan_remove(conn, &chan->chan); bt_l2cap_chan_del(&chan->chan); continue; + } else if (!L2CAP_LE_CID_IS_DYN(dcid) || + !IN_RANGE(mtu, BT_L2CAP_ECRED_MIN_MTU, BT_L2CAP_MAX_MTU) || + !IN_RANGE(mps, BT_L2CAP_ECRED_MIN_MPS, BT_L2CAP_MAX_MPS) || + !IN_RANGE(credits, BT_L2CAP_ECRED_CREDITS_MIN, + BT_L2CAP_ECRED_CREDITS_MAX)) { + LOG_WRN("Invalid ecred conn rsp params: dcid 0x%04x mtu %u mps %u " + "credits %u. Disconnecting.", + dcid, mtu, mps, credits); + bt_conn_disconnect(conn, BT_HCI_ERR_UNACCEPT_CONN_PARAM); + return; } c = bt_l2cap_le_lookup_tx_cid(conn, dcid); @@ -2086,6 +2106,20 @@ static void le_conn_rsp(struct bt_l2cap *l2cap, uint8_t ident, switch (result) { case BT_L2CAP_LE_SUCCESS: + /* Validate parameters on successful connection. Requirements are from Core Spec + * v6.0, Vol 3.A.4.23. Valid credit range is from 0 to UINT16_MAX, thus no credit + * validation is needed. + */ + if ((!L2CAP_LE_CID_IS_DYN(dcid) || + !IN_RANGE(mtu, L2CAP_LE_MIN_MTU, BT_L2CAP_MAX_MTU) || + !IN_RANGE(mps, L2CAP_LE_MIN_MPS, BT_L2CAP_MAX_MPS))) { + LOG_WRN("Invalid conn rsp params: dcid 0x%04x mtu %u mps %u. " + "Disconnecting.", + dcid, mtu, mps); + bt_conn_disconnect(conn, BT_HCI_ERR_UNACCEPT_CONN_PARAM); + return; + } + chan->tx.cid = dcid; chan->tx.mtu = mtu; chan->tx.mps = mps; diff --git a/subsys/bluetooth/host/l2cap_internal.h b/subsys/bluetooth/host/l2cap_internal.h index 07bd428334ae..61fa7416dd5d 100644 --- a/subsys/bluetooth/host/l2cap_internal.h +++ b/subsys/bluetooth/host/l2cap_internal.h @@ -125,6 +125,9 @@ struct bt_l2cap_le_credits { uint16_t credits; } __packed; +#define BT_L2CAP_ECRED_CREDITS_MIN 1 +#define BT_L2CAP_ECRED_CREDITS_MAX UINT16_MAX + #define BT_L2CAP_ECRED_CONN_REQ 0x17 struct bt_l2cap_ecred_conn_req { uint16_t psm; From d13e96f48016c0c2c817b8ddf0813d0d2656c821 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5vard=20Reierstad?= Date: Tue, 22 Jul 2025 09:56:14 +0200 Subject: [PATCH 7/7] [nrf fromtree] Bluetooth: Host: Use MTU min for l2cap test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The many_conn l2cap test used a MTU lower than the minimum permitted value. This commit bumps it to the minimum (23). Signed-off-by: Håvard Reierstad (cherry picked from commit b799d1897b9695d4bea7d985509a6621ab9dcd85) Signed-off-by: Håvard Reierstad (cherry picked from commit 7de47337d2a38ff9e5221986072ce1e8c9f6aaaa) --- tests/bsim/bluetooth/host/l2cap/many_conns/src/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/bsim/bluetooth/host/l2cap/many_conns/src/main.c b/tests/bsim/bluetooth/host/l2cap/many_conns/src/main.c index ff9f3030d1eb..a4f45bc1890c 100644 --- a/tests/bsim/bluetooth/host/l2cap/many_conns/src/main.c +++ b/tests/bsim/bluetooth/host/l2cap/many_conns/src/main.c @@ -29,7 +29,7 @@ DEFINE_FLAG_STATIC(flag_l2cap_connected); #define NUM_PERIPHERALS CONFIG_BT_MAX_CONN #define L2CAP_CHANS NUM_PERIPHERALS #define SDU_NUM 1 -#define SDU_LEN 10 +#define SDU_LEN 23 /* Only one SDU per link will be transmitted */ NET_BUF_POOL_DEFINE(sdu_tx_pool,