Skip to content

Commit b6a5e6e

Browse files
KyraLengfelddkalowsk
authored andcommitted
Bluetooth: Host: Fix bt_conn reservation leak for extended advertising
This fixes a leak for the following scenario: A bonded peer connects via undirected adv set while directed connectable extended advertisement runs on the same identity. The enhanced connection complete is cached, not yet processed and the undirected set is terminated, which clears the `BT_ADV_ENABLED` flag. During processing of the cached enhanced connection complete event, the `find_pending_connect()` function then finds the directed set instead of the undirected set (as until now there was no distinction of the advertising set type in the lookup function). This leads to a reservation leak, as the directed set is not terminated (but the `BT_ADV_ENABLED` flag is cleared). This commits adds the advertising set context to the `bt_hci_le_enh_conn_complete()` and `find_pending_connect()` functions, so that the correct advertising set is used for the lookup. Note: This change has been locally tested with a test case created by AI that reproduces the above scenario. Signed-off-by: Kyra Lengfeld <kyra.lengfeld@nordicsemi.no>
1 parent 1f49157 commit b6a5e6e

3 files changed

Lines changed: 54 additions & 19 deletions

File tree

subsys/bluetooth/host/adv.c

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2159,14 +2159,15 @@ void bt_hci_le_adv_set_terminated(struct net_buf *buf)
21592159
if (bt_dev.cached_conn_complete[i].valid &&
21602160
bt_dev.cached_conn_complete[i].evt.handle == evt->conn_handle) {
21612161
if (was_adv_enabled) {
2162-
/* Process the cached connection complete event
2163-
* now that the corresponding advertising set is known.
2162+
/* Process the cached connection complete event with the
2163+
* advertising set context.
21642164
*
21652165
* If the advertiser has been stopped before the connection
21662166
* complete event has been raised to the application, we
21672167
* discard the event.
21682168
*/
2169-
bt_hci_le_enh_conn_complete(&bt_dev.cached_conn_complete[i].evt);
2169+
bt_hci_le_enh_conn_complete(&bt_dev.cached_conn_complete[i].evt,
2170+
adv);
21702171
}
21712172
bt_dev.cached_conn_complete[i].valid = false;
21722173
}

subsys/bluetooth/host/hci_core.c

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1258,7 +1258,8 @@ int bt_le_set_phy(struct bt_conn *conn, uint8_t all_phys,
12581258
return bt_hci_cmd_send_sync(BT_HCI_OP_LE_SET_PHY, buf, NULL);
12591259
}
12601260

1261-
static struct bt_conn *find_pending_connect(uint8_t role, bt_addr_le_t *peer_addr)
1261+
static struct bt_conn *find_pending_connect(uint8_t role, const bt_addr_le_t *peer_addr,
1262+
const struct bt_le_ext_adv *ext_adv)
12621263
{
12631264
struct bt_conn *conn;
12641265

@@ -1279,6 +1280,33 @@ static struct bt_conn *find_pending_connect(uint8_t role, bt_addr_le_t *peer_add
12791280
}
12801281

12811282
if (IS_ENABLED(CONFIG_BT_PERIPHERAL) && role == BT_HCI_ROLE_PERIPHERAL) {
1283+
/* Do not fall back between directed and undirected pending connections
1284+
* when the terminating advertising set is known. Such a fallback can
1285+
* consume a reservation belonging to another active set.
1286+
*/
1287+
if (ext_adv != NULL) {
1288+
if (bt_addr_le_eq(&ext_adv->target_addr, BT_ADDR_LE_ANY)) {
1289+
/* Having multiple same-identity undirected reservations and
1290+
* finding the first one that might not have been the one that
1291+
* was used to initiate the connection is not a problem.
1292+
* Undirected reservations have no advertising-set association
1293+
* or per-set state, so any matching reservation can
1294+
* be consumed; one remains for each other enabled set.
1295+
*/
1296+
return bt_conn_lookup_state_le(ext_adv->id, BT_ADDR_LE_NONE,
1297+
BT_CONN_ADV_CONNECTABLE);
1298+
}
1299+
1300+
return bt_conn_lookup_state_le(ext_adv->id, &ext_adv->target_addr,
1301+
BT_CONN_ADV_DIR_CONNECTABLE);
1302+
}
1303+
1304+
/* In case there is no advertising handle, there can be at most one
1305+
* relevant peripheral advertiser. This is the case for legacy
1306+
* advertising, or when the controller does not support extended
1307+
* advertising. In this case, we can fall back to the legacy lookup
1308+
* behaviour.
1309+
*/
12821310
conn = bt_conn_lookup_state_le(bt_dev.adv_conn_id, peer_addr,
12831311
BT_CONN_ADV_DIR_CONNECTABLE);
12841312
if (!conn) {
@@ -1303,7 +1331,7 @@ static void le_conn_complete_cancel(uint8_t err)
13031331
* There is no need to check ID address as only one
13041332
* connection in central role can be in pending state.
13051333
*/
1306-
conn = find_pending_connect(BT_HCI_ROLE_CENTRAL, NULL);
1334+
conn = find_pending_connect(BT_HCI_ROLE_CENTRAL, NULL, NULL);
13071335
if (!conn) {
13081336
LOG_ERR("No pending central connection");
13091337
return;
@@ -1363,7 +1391,7 @@ static void le_conn_complete_adv_timeout(void)
13631391
/* There is no need to check ID address as only one
13641392
* connection in peripheral role can be in pending state.
13651393
*/
1366-
conn = find_pending_connect(BT_HCI_ROLE_PERIPHERAL, NULL);
1394+
conn = find_pending_connect(BT_HCI_ROLE_PERIPHERAL, NULL, NULL);
13671395
if (!conn) {
13681396
LOG_ERR("No pending peripheral connection");
13691397
return;
@@ -1404,7 +1432,7 @@ static void enh_conn_complete(struct bt_hci_evt_le_enh_conn_complete *evt)
14041432
return;
14051433
}
14061434
#endif
1407-
bt_hci_le_enh_conn_complete(evt);
1435+
bt_hci_le_enh_conn_complete(evt, NULL);
14081436
}
14091437

14101438
static void translate_addrs(bt_addr_le_t *peer_addr, bt_addr_le_t *id_addr,
@@ -1442,7 +1470,8 @@ static void update_conn(struct bt_conn *conn, const bt_addr_le_t *id_addr,
14421470
#endif
14431471
}
14441472

1445-
void bt_hci_le_enh_conn_complete(struct bt_hci_evt_le_enh_conn_complete *evt)
1473+
void bt_hci_le_enh_conn_complete(struct bt_hci_evt_le_enh_conn_complete *evt,
1474+
const struct bt_le_ext_adv *ext_adv)
14461475
{
14471476
__ASSERT_NO_MSG(evt->status == BT_HCI_ERR_SUCCESS);
14481477

@@ -1461,10 +1490,13 @@ void bt_hci_le_enh_conn_complete(struct bt_hci_evt_le_enh_conn_complete *evt)
14611490
bt_id_pending_keys_update();
14621491
#endif
14631492

1464-
id = evt->role == BT_HCI_ROLE_PERIPHERAL ? bt_dev.adv_conn_id : BT_ID_DEFAULT;
1493+
id = BT_ID_DEFAULT;
1494+
if (evt->role == BT_HCI_ROLE_PERIPHERAL) {
1495+
id = ext_adv != NULL ? ext_adv->id : bt_dev.adv_conn_id;
1496+
}
14651497
translate_addrs(&peer_addr, &id_addr, evt, id);
14661498

1467-
conn = find_pending_connect(evt->role, &id_addr);
1499+
conn = find_pending_connect(evt->role, &id_addr, ext_adv);
14681500

14691501
if (IS_ENABLED(CONFIG_BT_CENTRAL) &&
14701502
evt->role == BT_HCI_ROLE_CENTRAL) {

subsys/bluetooth/host/hci_core.h

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -342,14 +342,15 @@ struct bt_dev {
342342
/* Pointer to reserved advertising set */
343343
struct bt_le_ext_adv *adv;
344344
#if defined(CONFIG_BT_CONN) && (CONFIG_BT_EXT_ADV_MAX_ADV_SET > 1)
345-
/* When supporting multiple concurrent connectable advertising sets
346-
* with multiple identities, we need to know the identity of
347-
* the terminating advertising set to identify the connection object.
348-
* The identity of the advertising set is determined by its
349-
* advertising handle, which is part of the
350-
* LE Set Advertising Set Terminated event which is always sent
351-
* _after_ the LE Enhanced Connection complete event.
352-
* Therefore we need cache this event until its identity is known.
345+
/* When supporting multiple concurrent connectable advertising sets,
346+
* we need to know the identity of the terminating advertising set to
347+
* identify the connection object. If multiple sets share an identity,
348+
* we also need to know whether the terminating set is directed or
349+
* undirected to select the corresponding connection reservation. The
350+
* advertising set is identified by its advertising handle, which is part
351+
* of the LE Advertising Set Terminated event which is always sent _after_
352+
* the LE Enhanced Connection Complete event. Therefore we need to cache
353+
* this event until its advertising set is known.
353354
*/
354355
struct {
355356
bool valid;
@@ -516,7 +517,8 @@ void bt_hci_user_passkey_req(struct net_buf *buf);
516517
void bt_hci_auth_complete(struct net_buf *buf);
517518

518519
/* Common HCI event handlers */
519-
void bt_hci_le_enh_conn_complete(struct bt_hci_evt_le_enh_conn_complete *evt);
520+
void bt_hci_le_enh_conn_complete(struct bt_hci_evt_le_enh_conn_complete *evt,
521+
const struct bt_le_ext_adv *ext_adv);
520522

521523
/* Scan HCI event handlers */
522524
void bt_hci_le_adv_report(struct net_buf *buf);

0 commit comments

Comments
 (0)