Skip to content

Commit 5969c3b

Browse files
alwa-nordiccfriedt
authored andcommitted
Bluetooth: Host: Fix resource leak in bt_gatt_unsubscribe
There are two simmilar functions for unsubscribing from GATT handles. - `gatt_sub_remove`, which is called on disconnect for every subscription will free the `subscriptions` entry when the entry represents no subscriptions. - `bt_gatt_unsubscribe`, called by the application, which forgets to free the `subscriptions` entry. If all subscriptions grouped in a `subscriptions` entry are removed using `bt_gatt_unsubscribe` before disconnect, there are no subscriptions left to call `gatt_sub_remove` on. The `subscriptions` entry is then never freed. The above results in a resource leak of a `subscriptions` entry. This fix makes explicit and enforces the invariant that there should not be entries in `subscriptions` with an empty subscription list. Fixes #38688 Signed-off-by: Aleksander Wasaznik <[email protected]>
1 parent 6db7778 commit 5969c3b

File tree

1 file changed

+24
-3
lines changed

1 file changed

+24
-3
lines changed

subsys/bluetooth/host/gatt.c

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,11 @@ struct gatt_sub {
7070
#define SUB_MAX 0
7171
#endif /* CONFIG_BT_GATT_CLIENT */
7272

73+
/**
74+
* Entry x is free for reuse whenever (subscriptions[x].peer == BT_ADDR_LE_ANY).
75+
* Invariant: (sys_slist_is_empty(subscriptions[x].list))
76+
* <=> (subscriptions[x].peer == BT_ADDR_LE_ANY).
77+
*/
7378
static struct gatt_sub subscriptions[SUB_MAX];
7479
static sys_slist_t callback_list;
7580

@@ -2699,6 +2704,19 @@ bool bt_gatt_is_subscribed(struct bt_conn *conn,
26992704
return false;
27002705
}
27012706

2707+
static bool gatt_sub_is_empty(struct gatt_sub *sub)
2708+
{
2709+
return sys_slist_is_empty(&sub->list);
2710+
}
2711+
2712+
/** @brief Free sub for reuse.
2713+
*/
2714+
static void gatt_sub_free(struct gatt_sub *sub)
2715+
{
2716+
__ASSERT_NO_MSG(gatt_sub_is_empty(sub));
2717+
bt_addr_le_copy(&sub->peer, BT_ADDR_LE_ANY);
2718+
}
2719+
27022720
static void gatt_sub_remove(struct bt_conn *conn, struct gatt_sub *sub,
27032721
sys_snode_t *prev,
27042722
struct bt_gatt_subscribe_params *params)
@@ -2710,9 +2728,8 @@ static void gatt_sub_remove(struct bt_conn *conn, struct gatt_sub *sub,
27102728
params->notify(conn, params, NULL, 0);
27112729
}
27122730

2713-
if (sys_slist_is_empty(&sub->list)) {
2714-
/* Reset address if there are no subscription left */
2715-
bt_addr_le_copy(&sub->peer, BT_ADDR_LE_ANY);
2731+
if (gatt_sub_is_empty(sub)) {
2732+
gatt_sub_free(sub);
27162733
}
27172734
}
27182735

@@ -4597,6 +4614,10 @@ int bt_gatt_unsubscribe(struct bt_conn *conn,
45974614
return -EINVAL;
45984615
}
45994616

4617+
if (gatt_sub_is_empty(sub)) {
4618+
gatt_sub_free(sub);
4619+
}
4620+
46004621
if (has_subscription) {
46014622
/* Notify with NULL data to complete unsubscribe */
46024623
params->notify(conn, params, NULL, 0);

0 commit comments

Comments
 (0)