Skip to content

Commit a517113

Browse files
committed
Bluetooth: Host: Fix possible inconsistent access to connection state
An assertion in bt_conn_unref() accesses the connection's state after decrementing its reference count. This is not consistent since, if we removed the last reference, the Bluetooth Host stack may reuse the connection object before the assertion is checked. Instead, retrieve the connection property tested by the assertion before decrementing the counter, as we do for other properties. Simplify the code path by returning early when we did not remove the last reference. Remind that automatic advertiser resumption is deprecated. Signed-off-by: Christophe Dufaza <[email protected]>
1 parent 0ff85c1 commit a517113

File tree

1 file changed

+34
-15
lines changed

1 file changed

+34
-15
lines changed

subsys/bluetooth/host/conn.c

Lines changed: 34 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1500,38 +1500,57 @@ void bt_conn_unref(struct bt_conn *conn)
15001500
enum bt_conn_type conn_type;
15011501
uint8_t conn_role;
15021502
uint16_t conn_handle;
1503+
/* Used only if CONFIG_ASSERT and CONFIG_BT_CONN_TX. */
1504+
__maybe_unused bool conn_tx_is_pending;
15031505

15041506
__ASSERT(conn, "Invalid connection reference");
15051507

1506-
/* Storing parameters of interest so we don't access the object
1507-
* after decrementing its ref-count
1508+
/* If we're removing the last reference, the connection object will be
1509+
* considered freed and possibly re-allocated by the Bluetooth Host stack
1510+
* as soon as we decrement the counter.
1511+
* To prevent inconsistencies when accessing the connection's state,
1512+
* we store its properties of interest before decrementing the ref-count,
1513+
* then unset the local pointer.
15081514
*/
15091515
conn_type = conn->type;
15101516
conn_role = conn->role;
15111517
conn_handle = conn->handle;
1512-
1518+
if (IS_ENABLED(CONFIG_ASSERT) && IS_ENABLED(CONFIG_BT_CONN_TX)) {
1519+
conn_tx_is_pending = k_work_is_pending(&conn->tx_complete_work);
1520+
}
15131521
old = atomic_dec(&conn->ref);
1514-
/* Whether we removed the last reference. */
1515-
deallocated = (old == 1);
1516-
IF_ENABLED(CONFIG_BT_CONN_TX,
1517-
(__ASSERT(!(deallocated && k_work_is_pending(&conn->tx_complete_work)),
1518-
"tx_complete_work is pending when conn is deallocated")));
15191522
conn = NULL;
15201523

15211524
LOG_DBG("handle %u ref %ld -> %ld", conn_handle, old, (old - 1));
15221525

15231526
__ASSERT(old > 0, "Conn reference counter is 0");
15241527

1525-
/* Slot has been freed and can be taken. No guarantees are made on requests
1526-
* to claim connection object as only the first claim will be served.
1527-
*/
1528-
if (deallocated) {
1529-
k_sem_give(&pending_recycled_events);
1530-
k_work_submit(&recycled_work);
1528+
/* Whether we removed the last reference. */
1529+
deallocated = (old == 1);
1530+
if (!deallocated) {
1531+
return;
15311532
}
15321533

1534+
IF_ENABLED(CONFIG_BT_CONN_TX,
1535+
(__ASSERT(!conn_tx_is_pending,
1536+
"tx_complete_work is pending when conn is deallocated");))
1537+
1538+
/* Notify listeners that a slot has been freed and can be taken.
1539+
* No guarantees are made on requests to claim connection object
1540+
* as only the first claim will be served.
1541+
*/
1542+
k_sem_give(&pending_recycled_events);
1543+
k_work_submit(&recycled_work);
1544+
1545+
/* Use the freed slot to automatically resume LE peripheral advertising.
1546+
*
1547+
* This behavior is deprecated:
1548+
* - 8cfad44: Bluetooth: Deprecate adv auto-resume
1549+
* - #72567: Bluetooth: Advertising resume functionality is broken
1550+
* - Migration guide to Zephyr v4.0.0, Automatic advertiser resumption is deprecated
1551+
*/
15331552
if (IS_ENABLED(CONFIG_BT_PERIPHERAL) && conn_type == BT_CONN_TYPE_LE &&
1534-
conn_role == BT_CONN_ROLE_PERIPHERAL && deallocated) {
1553+
conn_role == BT_CONN_ROLE_PERIPHERAL) {
15351554
bt_le_adv_resume();
15361555
}
15371556
}

0 commit comments

Comments
 (0)