FAILED: patch "[PATCH] Bluetooth: hci_sync: Fix advertising data UAFs" failed to apply to 6.1-stable tree
Subsystems (2)
[email protected]
maintainerThe patch below does not apply to the 6.1-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.
To reproduce the conflict and resubmit, you may use the following commands:
git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-6.1.y
git checkout FETCH_HEAD
git cherry-pick -x cdc36db204ffd97b947d64374cf23a210dc74777
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<[email protected]>' --in-reply-to '2026080501-hassle-thinning-e82d@gregkh' --subject-prefix 'PATCH 6.1.y' 'HEAD^..'
Possible dependencies:
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From cdc36db204ffd97b947d64374cf23a210dc74777 Mon Sep 17 00:00:00 2001
From: Chengfeng Ye <[email protected]>
Date: Thu, 23 Jul 2026 23:34:40 +0800
Subject: [PATCH] Bluetooth: hci_sync: Fix advertising data UAFs
hci_find_adv_instance() returns an adv_info pointer that is valid only while hdev->lock is held. The advertising command-sync paths perform instance lookups without that lock and, in some cases, retain the pointer while waiting for a controller response.
An advertising termination event can therefore interleave as follows:
hci_cmd_sync_work hci_rx_work
hci_find_adv_instance()
__hci_cmd_sync_status()
wait for controller reply hci_dev_lock()
hci_remove_adv_instance()
kfree(adv)
adv->scan_rsp_changed = false
KASAN reported:
BUG: KASAN: slab-use-after-free in hci_set_ext_scan_rsp_data_sync+0x2e1/0x300 Write of size 1 at addr ffff88810a45d21d by task kworker/u17:0/88 Workqueue: hci0 hci_cmd_sync_work Call Trace: hci_set_ext_scan_rsp_data_sync+0x2e1/0x300 hci_schedule_adv_instance_sync+0x390/0x4c0 hci_cmd_sync_work+0x173/0x300 Allocated by task 87: hci_add_adv_instance+0x538/0xac0 add_advertising+0x885/0x1160 Freed by task 89: kfree+0x131/0x3c0 hci_remove_adv_instance+0x1d8/0x3b0 hci_le_ext_adv_term_evt+0x17b/0x730
Protect the instance lookup and payload construction in the extended advertising, scan response, and periodic advertising data paths. Snapshot the advertising parameters under hdev->lock, but release the lock before waiting for the controller.
Clear advertising-data dirty bits before issuing their commands and restore them after a failure using a fresh lookup. Likewise, update the reported transmit power through a fresh lookup after the parameter command completes. No adv_info pointer then survives an HCI command wait.
net/bluetooth/hci_sync.c +99 -36
@@ -1233,10 +1233,11 @@ static int hci_set_adv_set_random_addr_sync(struct hci_dev *hdev, u8 instance, }static int -hci_set_ext_adv_params_sync (struct hci_dev *hdev,struct adv_info *adv, +hci_set_ext_adv_params_sync (struct hci_dev *hdev,u8 instance,const struct hci_cp_le_set_ext_adv_params *cp,struct hci_rp_le_set_ext_adv_params *rp) { +struct adv_info *adv;struct sk_buff *skb; skb = __hci_cmd_sync(hdev, HCI_OP_LE_SET_EXT_ADV_PARAMS, sizeof(*cp),
@@ -1264,11 +1265,15 @@ hci_set_ext_adv_params_sync(struct hci_dev *hdev, struct adv_info *adv,if (!rp->status) { hdev->adv_addr_type = cp->own_addr_type; -if (!cp->handle) { +if (!instance) {/* Store in hdev for instance 0 */ hdev->adv_tx_power = rp->tx_power; - }else if (adv) { - adv->tx_power = rp->tx_power; + }else { +hci_dev_lock (hdev); + adv =hci_find_adv_instance (hdev, instance); +if (adv) + adv->tx_power = rp->tx_power; +hci_dev_unlock (hdev); } }
@@ -1284,9 +1289,13 @@ static int hci_set_ext_adv_data_sync(struct hci_dev *hdev, u8 instance)int err;if (instance) { +hci_dev_lock (hdev); + adv =hci_find_adv_instance (hdev, instance); -if (!adv || !adv->adv_data_changed) +if (!adv || !adv->adv_data_changed) { +hci_dev_unlock (hdev);return 0 ; + } } len = eir_create_adv_data(hdev, instance, pdu->data,
@@ -1297,16 +1306,27 @@ static int hci_set_ext_adv_data_sync(struct hci_dev *hdev, u8 instance) pdu->operation =LE_SET_ADV_DATA_OP_COMPLETE ; pdu->frag_pref =LE_SET_ADV_DATA_NO_FRAG ; +if (adv) { + adv->adv_data_changed = false; +hci_dev_unlock (hdev); + } + err =__hci_cmd_sync_status (hdev,HCI_OP_LE_SET_EXT_ADV_DATA ,struct_size (pdu, data, len), pdu,HCI_CMD_TIMEOUT ); -if (err) -return err; +if (err) { +if (instance) { +hci_dev_lock (hdev); + adv =hci_find_adv_instance (hdev, instance); +if (adv) + adv->adv_data_changed = true; +hci_dev_unlock (hdev); + } -/* Update data if the command succeed */ -if (adv) { - adv->adv_data_changed = false; - }else { +return err; + } + +if (!instance) {memcpy (hdev->adv_data, pdu->data, len); hdev->adv_data_len = len; }
@@ -1360,22 +1380,22 @@ int hci_setup_ext_adv_instance_sync(struct hci_dev *hdev, u8 instance)struct adv_info *adv;bool secondary_adv; -if (instance >0 ) { - adv =hci_find_adv_instance (hdev, instance); -if (!adv) -return -EINVAL ; - }else { - adv =NULL ; - } -/* Updating parameters of an active instance will return a -* Command Disallowed error, so we must first disable the -* instance if it is active. +* Command Disallowed error, so disable it before taking a snapshot. */ -if (adv) { +if (instance >0 ) { err =hci_disable_ext_adv_instance_sync (hdev, instance);if (err)return err; + +hci_dev_lock (hdev); + adv =hci_find_adv_instance (hdev, instance); +if (!adv) { +hci_dev_unlock (hdev); +return -EINVAL ; + } + }else { + adv =NULL ; } flags =hci_adv_instance_flags (hdev, instance);
@@ -1386,8 +1406,11 @@ int hci_setup_ext_adv_instance_sync(struct hci_dev *hdev, u8 instance) connectable = (flags &MGMT_ADV_FLAG_CONNECTABLE ) ||mgmt_get_connectable (hdev); -if (!is_advertising_allowed (hdev, connectable)) +if (!is_advertising_allowed (hdev, connectable)) { +if (instance) +hci_dev_unlock (hdev);return -EPERM ; + }/* Set require_privacy to true only when non-connectable * advertising is used and it is not periodic.
@@ -1398,8 +1421,11 @@ int hci_setup_ext_adv_instance_sync(struct hci_dev *hdev, u8 instance) err =hci_get_random_address (hdev, require_privacy,adv_use_rpa (hdev, flags), adv, &own_addr_type, &random_addr); -if (err <0 ) +if (err <0 ) { +if (instance) +hci_dev_unlock (hdev);return err; + }memset (&cp,0 ,sizeof (cp));
@@ -1450,6 +1476,9 @@ int hci_setup_ext_adv_instance_sync(struct hci_dev *hdev, u8 instance) cp.channel_map = hdev->le_adv_channel_map; cp.handle = adv ? adv->handle : instance; +if (instance) +hci_dev_unlock (hdev); +if (flags &MGMT_ADV_FLAG_SEC_2M ) { cp.primary_phy =HCI_ADV_PHY_1M ; cp.secondary_phy =HCI_ADV_PHY_2M ;
@@ -1462,12 +1491,12 @@ int hci_setup_ext_adv_instance_sync(struct hci_dev *hdev, u8 instance) cp.secondary_phy =HCI_ADV_PHY_1M ; } - err =hci_set_ext_adv_params_sync (hdev, adv, &cp, &rp); + err =hci_set_ext_adv_params_sync (hdev, instance, &cp, &rp);if (err)return err;/* Update adv data as tx power is known now */ - err =hci_set_ext_adv_data_sync (hdev, cp.handle); + err =hci_set_ext_adv_data_sync (hdev, instance);if (err)return err;
@@ -1475,9 +1504,14 @@ int hci_setup_ext_adv_instance_sync(struct hci_dev *hdev, u8 instance) own_addr_type == ADDR_LE_DEV_RANDOM_RESOLVED) && bacmp(&random_addr, BDADDR_ANY)) {/* Check if random address need to be updated */ -if (adv) { -if (!bacmp (&random_addr, &adv->random_addr)) +if (instance) { +hci_dev_lock (hdev); + adv =hci_find_adv_instance (hdev, instance); +if (!adv || !bacmp (&random_addr, &adv->random_addr)) { +hci_dev_unlock (hdev);return 0 ; + } +hci_dev_unlock (hdev); }else {if (!bacmp (&random_addr, &hdev->random_addr))return 0 ;
@@ -1499,9 +1533,13 @@ static int hci_set_ext_scan_rsp_data_sync(struct hci_dev *hdev, u8 instance)int err;if (instance) { +hci_dev_lock (hdev); + adv =hci_find_adv_instance (hdev, instance); -if (!adv || !adv->scan_rsp_changed) +if (!adv || !adv->scan_rsp_changed) { +hci_dev_unlock (hdev);return 0 ; + } } len =eir_create_scan_rsp (hdev, instance, pdu->data);
@@ -1511,15 +1549,27 @@ static int hci_set_ext_scan_rsp_data_sync(struct hci_dev *hdev, u8 instance) pdu->operation =LE_SET_ADV_DATA_OP_COMPLETE ; pdu->frag_pref =LE_SET_ADV_DATA_NO_FRAG ; +if (adv) { + adv->scan_rsp_changed = false; +hci_dev_unlock (hdev); + } + err =__hci_cmd_sync_status (hdev,HCI_OP_LE_SET_EXT_SCAN_RSP_DATA ,struct_size (pdu, data, len), pdu,HCI_CMD_TIMEOUT ); -if (err) -return err; +if (err) { +if (instance) { +hci_dev_lock (hdev); + adv =hci_find_adv_instance (hdev, instance); +if (adv) + adv->scan_rsp_changed = true; +hci_dev_unlock (hdev); + } -if (adv) { - adv->scan_rsp_changed = false; - }else { +return err; + } + +if (!instance) {memcpy (hdev->scan_rsp_data, pdu->data, len); hdev->scan_rsp_data_len = len; }
@@ -1534,8 +1584,14 @@ static int __hci_set_scan_rsp_data_sync(struct hci_dev *hdev, u8 instance)memset (&cp,0 ,sizeof (cp)); +if (instance) +hci_dev_lock (hdev); + len =eir_create_scan_rsp (hdev, instance, cp.data); +if (instance) +hci_dev_unlock (hdev); +if (hdev->scan_rsp_data_len == len && !memcmp (cp.data, hdev->scan_rsp_data, len))return 0 ;
@@ -1670,9 +1726,13 @@ static int hci_set_per_adv_data_sync(struct hci_dev *hdev, u8 instance)struct adv_info *adv =NULL ;if (instance) { +hci_dev_lock (hdev); + adv =hci_find_adv_instance (hdev, instance); -if (!adv || !adv->periodic) +if (!adv || !adv->periodic) { +hci_dev_unlock (hdev);return 0 ; + } } len =eir_create_per_adv_data (hdev, instance, pdu->data);
@@ -1681,6 +1741,9 @@ static int hci_set_per_adv_data_sync(struct hci_dev *hdev, u8 instance) pdu->handle = adv ? adv->handle : instance; pdu->operation =LE_SET_ADV_DATA_OP_COMPLETE ; +if (adv) +hci_dev_unlock (hdev); +return __hci_cmd_sync_status (hdev,HCI_OP_LE_SET_PER_ADV_DATA ,struct_size (pdu, data, len), pdu,HCI_CMD_TIMEOUT );
@@ -6523,7 +6586,7 @@ static int hci_le_ext_directed_advertising_sync(struct hci_dev *hdev,if (err)return err; - err =hci_set_ext_adv_params_sync (hdev,NULL , &cp, &rp); + err =hci_set_ext_adv_params_sync (hdev,0 , &cp, &rp);if (err)return err;
Sasha Levin
maintainerPATCH 1/4 overflow: Change DEFINE_FLEX to take __counted_by member
From: Kees Cook <[email protected]>
[ Upstream commit d8e45f2929b94099913eb66c3ebb18b5063e9421 ]
The norm should be flexible array structures with __counted_by annotations, so DEFINE_FLEX() is updated to expect that. Rename the non-annotated version to DEFINE_RAW_FLEX(), and update the few existing users. Additionally add selftests for the macros.
Stable-dep-of: cdc36db204ff ("Bluetooth: hci_sync: Fix advertising data UAFs")
--- include/linux/overflow.h | 68 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+)
include/linux/overflow.h +68 -0
@@ -246,4 +246,72 @@ static inline size_t __must_check size_sub(size_t minuend, size_t subtrahend) sizeof(*(p)) + flex_array_size(p, member, count), \ size_add(sizeof(*(p)), flex_array_size(p, member, count))) +/** +* struct_size_t() - Calculate size of structure with trailing flexible array +* @type: structure type name. +* @member: Name of the array member. +* @count: Number of elements in the array. +* +* Calculates size of memory needed for structure @type followed by an +* array of @count number of @member elements. Prefer using struct_size() +* when possible instead, to keep calculations associated with a specific +* instance variable of type @type. +* +* Return: number of bytes needed or SIZE_MAX on overflow. +*/ +#define struct_size_t (type, member, count) \ + struct_size((type *)NULL, member, count) + +/** +* _DEFINE_FLEX() - helper macro for DEFINE_FLEX() family. +* Enables caller macro to pass (different) initializer. +* +* @type: structure type name, including "struct" keyword. +* @name: Name for a variable to define. +* @member: Name of the array member. +* @count: Number of elements in the array; must be compile-time const. +* @initializer: initializer expression (could be empty for no init). +*/ +#define _DEFINE_FLEX(type, name, member, count, initializer...) \ + _Static_assert(__builtin_constant_p(count), \ + "onstack flex array members require compile-time const count"); \ + union { \ + u8 bytes[struct_size_t(type, member, count)]; \ + type obj; \ + } name##_u initializer; \ + type *name = (type *)&name##_u + +/** +* DEFINE_RAW_FLEX() - Define an on-stack instance of structure with a trailing +* flexible array member, when it does not have a __counted_by annotation. +* +* @type: structure type name, including "struct" keyword. +* @name: Name for a variable to define. +* @member: Name of the array member. +* @count: Number of elements in the array; must be compile-time const. +* +* Define a zeroed, on-stack, instance of @type structure with a trailing +* flexible array member. +* Use __struct_size(@name) to get compile-time size of it afterwards. +*/ +#define DEFINE_RAW_FLEX (type, name, member, count) \ + _DEFINE_FLEX(type, name, member, count, = {}) + +/** +* DEFINE_FLEX() - Define an on-stack instance of structure with a trailing +* flexible array member. +* +* @TYPE: structure type name, including "struct" keyword. +* @NAME: Name for a variable to define. +* @MEMBER: Name of the array member. +* @COUNTER: Name of the __counted_by member. +* @COUNT: Number of elements in the array; must be compile-time const. +* +* Define a zeroed, on-stack, instance of @TYPE structure with a trailing +* flexible array member. +* Use __struct_size(@NAME) to get compile-time size of it afterwards. +*/ +#define DEFINE_FLEX (TYPE ,NAME ,MEMBER ,COUNTER ,COUNT ) \ + _DEFINE_FLEX(TYPE, NAME, MEMBER, COUNT, = { .obj.COUNTER = COUNT, }) +#endif /* __LINUX_OVERFLOW_H */
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 2/4 Bluetooth: hci_conn, hci_sync: Use __counted_by() to avoid -Wfamnae warnings
From: "Gustavo A. R. Silva" <[email protected]>
[ Upstream commit c4585edf708edb5277a3cc4b8581ccb833f3307d ]
Prepare for the coming implementation by GCC and Clang of the __counted_by attribute. Flexible array members annotated with __counted_by can have their accesses bounds-checked at run-time via CONFIG_UBSAN_BOUNDS (for array indexing) and CONFIG_FORTIFY_SOURCE (for strcpy/memcpy-family functions).
Also, -Wflex-array-member-not-at-end is coming in GCC-14, and we are getting ready to enable it globally.
So, use the DEFINE_FLEX() helper for multiple on-stack definitions of a flexible structure where the size of the flexible-array member is known at compile-time, and refactor the rest of the code, accordingly.
Notice that, due to the use of __counted_by() in struct hci_cp_le_create_cis, the for loop in function hci_cs_le_create_cis() had to be modified. Once the index i, through which cp->cis[i] is accessed, falls in the interval [0, cp->num_cis), cp->num_cis cannot be decremented all the way down to zero while accessing cp->cis[]:
net/bluetooth/hci_event.c:4310:
4310 for (i = 0; cp->num_cis; cp->num_cis--, i++) {
...4314 handle = __le16_to_cpu(cp->cis[i].cis_handle);
otherwise, only half (one iteration before cp->num_cis == i) or half plus one (one iteration before cp->num_cis < i) of the items in the array will be accessed before running into an out-of-bounds issue. So, in order to avoid this, set cp->num_cis to zero just after the for loop.
Also, make use of aux_num_cis variable to update cmd->num_cis after a list_for_each_entry_rcu() loop.
With these changes, fix the following warnings:net/bluetooth/hci_sync.c:1239:56: warning: structure containing a flexible array member is not at the end of another structure [-Wflex-array-member-not-at-end] net/bluetooth/hci_sync.c:1415:51: warning: structure containing a flexible array member is not at the end of another structure [-Wflex-array-member-not-at-end] net/bluetooth/hci_sync.c:1731:51: warning: structure containing a flexible array member is not at the end of another structure [-Wflex-array-member-not-at-end] net/bluetooth/hci_sync.c:6497:45: warning: structure containing a flexible array member is not at the end of another structure [-Wflex-array-member-not-at-end]
Stable-dep-of: cdc36db204ff ("Bluetooth: hci_sync: Fix advertising data UAFs")
--- net/bluetooth/hci_event.c | 3 +- net/bluetooth/hci_sync.c | 62 ++++++++++++++++----------------------- 2 files changed, 27 insertions(+), 38 deletions(-)
net/bluetooth/hci_event.c +2 -1
@@ -4272,7 +4272,7 @@ static void hci_cs_le_create_cis(struct hci_dev *hdev, u8 status)hci_dev_lock (hdev);/* Remove connection if command failed */ -for (i =0 ; cp->num_cis; cp->num_cis--, i++) { +for (i =0 ; i < cp->num_cis; i++) {struct hci_conn *conn;u16 handle;
@@ -4285,6 +4285,7 @@ static void hci_cs_le_create_cis(struct hci_dev *hdev, u8 status)hci_conn_del (conn); } } + cp->num_cis =0 ;hci_dev_unlock (hdev); }
net/bluetooth/hci_sync.c +25 -37
130 lines not on this page · raw message
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 3/4 Bluetooth: hci_core: Fix not handling hdev->le_num_of_adv_sets=1
In reply to Sasha LevinFrom: Luiz Augusto von Dentz <[email protected]>
[ Upstream commit e77f43d531af41e9ce299eab10dcae8fa5dbc293 ]
If hdev->le_num_of_adv_sets is set to 1 it means that only handle 0x00 can be used, but since the MGMT interface instances start from 1 (instance 0 means all instances in case of MGMT_OP_REMOVE_ADVERTISING) the code needs to map the instance to handle otherwise users will not be able to advertise as instance 1 would attempt to use handle 0x01.
Stable-dep-of: cdc36db204ff ("Bluetooth: hci_sync: Fix advertising data UAFs")
--- include/net/bluetooth/hci_core.h | 1 + net/bluetooth/hci_core.c | 9 +++++++++ net/bluetooth/hci_sync.c | 17 ++++++++--------- 3 files changed, 18 insertions(+), 9 deletions(-)
include/net/bluetooth/hci_core.h +1 -0
@@ -243,6 +243,7 @@ struct adv_info {bool periodic;__u8 mesh;__u8 instance; +__u8 handle;__u32 flags;__u16 timeout;__u16 remaining_time;
net/bluetooth/hci_core.c +9 -0
@@ -1717,6 +1717,15 @@ struct adv_info *hci_add_adv_instance(struct hci_dev *hdev, u8 instance, adv->pending = true; adv->instance = instance; + +/* If controller support only one set and the instance is set to +* 1 then there is no option other than using handle 0x00. +*/ +if (hdev->le_num_of_adv_sets ==1 && instance ==1 ) + adv->handle =0x00 ; +else + adv->handle = instance; +list_add (&adv->list, &hdev->adv_instances); hdev->adv_instance_cnt++; }
net/bluetooth/hci_sync.c +8 -9
@@ -1136,11 +1136,10 @@ static int hci_disable_ext_adv_instance_sync(struct hci_dev *hdev, u8 instance)struct hci_cp_ext_adv_set *set;u8 data[sizeof (*cp) +sizeof (*set) *1 ];u8 size; +struct adv_info *adv =NULL ;/* If request specifies an instance that doesn't exist, fail */ if (instance >0 ) { -struct adv_info *adv; - adv =hci_find_adv_instance (hdev, instance);if (!adv)return -EINVAL ;
@@ -1159,7 +1158,7 @@ static int hci_disable_ext_adv_instance_sync(struct hci_dev *hdev, u8 instance) cp->num_of_sets = !!instance; cp->enable =0x00 ; - set->handle = instance; + set->handle = adv ? adv->handle : instance; size =sizeof (*cp) +sizeof (*set) * cp->num_of_sets;
@@ -1254,7 +1253,7 @@ static int hci_set_ext_adv_data_sync(struct hci_dev *hdev, u8 instance) HCI_MAX_EXT_AD_LENGTH); pdu->length = len; - pdu->handle = instance; + pdu->handle = adv ? adv->handle : instance; pdu->operation =LE_SET_ADV_DATA_OP_COMPLETE ; pdu->frag_pref =LE_SET_ADV_DATA_NO_FRAG ;
@@ -1465,7 +1464,7 @@ static int hci_set_ext_scan_rsp_data_sync(struct hci_dev *hdev, u8 instance) len =eir_create_scan_rsp (hdev, instance, pdu->data); - pdu->handle = instance; + pdu->handle = adv ? adv->handle : instance; pdu->length = len; pdu->operation =LE_SET_ADV_DATA_OP_COMPLETE ; pdu->frag_pref =LE_SET_ADV_DATA_NO_FRAG ;
@@ -1547,7 +1546,7 @@ int hci_enable_ext_advertising_sync(struct hci_dev *hdev, u8 instance)memset (set,0 ,sizeof (*set)); - set->handle = instance; + set->handle = adv ? adv->handle : instance;/* Set duration per instance since controller is responsible for * scheduling it.
@@ -1624,10 +1623,10 @@ static int hci_set_per_adv_data_sync(struct hci_dev *hdev, u8 instance) DEFINE_FLEX(struct hci_cp_le_set_per_adv_data, pdu, data, length, HCI_MAX_PER_AD_LENGTH);u8 len; +struct adv_info *adv =NULL ;if (instance) { -struct adv_info *adv =hci_find_adv_instance (hdev, instance); - + adv =hci_find_adv_instance (hdev, instance);if (!adv || !adv->periodic)return 0 ; }
@@ -1635,7 +1634,7 @@ static int hci_set_per_adv_data_sync(struct hci_dev *hdev, u8 instance) len =eir_create_per_adv_data (hdev, instance, pdu->data); pdu->length = len; - pdu->handle = instance; + pdu->handle = adv ? adv->handle : instance; pdu->operation =LE_SET_ADV_DATA_OP_COMPLETE ; return __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_PER_ADV_DATA,
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 4/4 Bluetooth: hci_sync: Fix advertising data UAFs
In reply to Sasha LevinFrom: Chengfeng Ye <[email protected]>
[ Upstream commit cdc36db204ffd97b947d64374cf23a210dc74777 ]
hci_find_adv_instance() returns an adv_info pointer that is valid only while hdev->lock is held. The advertising command-sync paths perform instance lookups without that lock and, in some cases, retain the pointer while waiting for a controller response.
An advertising termination event can therefore interleave as follows:
hci_cmd_sync_work hci_rx_work
hci_find_adv_instance()
__hci_cmd_sync_status()
wait for controller reply hci_dev_lock()
hci_remove_adv_instance()
kfree(adv)
adv->scan_rsp_changed = false
KASAN reported:
BUG: KASAN: slab-use-after-free in hci_set_ext_scan_rsp_data_sync+0x2e1/0x300 Write of size 1 at addr ffff88810a45d21d by task kworker/u17:0/88 Workqueue: hci0 hci_cmd_sync_work Call Trace: hci_set_ext_scan_rsp_data_sync+0x2e1/0x300 hci_schedule_adv_instance_sync+0x390/0x4c0 hci_cmd_sync_work+0x173/0x300 Allocated by task 87: hci_add_adv_instance+0x538/0xac0 add_advertising+0x885/0x1160 Freed by task 89: kfree+0x131/0x3c0 hci_remove_adv_instance+0x1d8/0x3b0 hci_le_ext_adv_term_evt+0x17b/0x730
Protect the instance lookup and payload construction in the extended advertising, scan response, and periodic advertising data paths. Snapshot the advertising parameters under hdev->lock, but release the lock before waiting for the controller.
Clear advertising-data dirty bits before issuing their commands and restore them after a failure using a fresh lookup. Likewise, update the reported transmit power through a fresh lookup after the parameter command completes. No adv_info pointer then survives an HCI command wait.
--- net/bluetooth/hci_sync.c | 131 +++++++++++++++++++++++++++++---------- 1 file changed, 97 insertions(+), 34 deletions(-)
net/bluetooth/hci_sync.c +97 -34
@@ -1193,10 +1193,11 @@ static int hci_set_adv_set_random_addr_sync(struct hci_dev *hdev, u8 instance, }static int -hci_set_ext_adv_params_sync (struct hci_dev *hdev,struct adv_info *adv, +hci_set_ext_adv_params_sync (struct hci_dev *hdev,u8 instance,const struct hci_cp_le_set_ext_adv_params *cp,struct hci_rp_le_set_ext_adv_params *rp) { +struct adv_info *adv;struct sk_buff *skb; skb = __hci_cmd_sync(hdev, HCI_OP_LE_SET_EXT_ADV_PARAMS, sizeof(*cp),
@@ -1224,11 +1225,15 @@ hci_set_ext_adv_params_sync(struct hci_dev *hdev, struct adv_info *adv,if (!rp->status) { hdev->adv_addr_type = cp->own_addr_type; -if (!cp->handle) { +if (!instance) {/* Store in hdev for instance 0 */ hdev->adv_tx_power = rp->tx_power; - }else if (adv) { - adv->tx_power = rp->tx_power; + }else { +hci_dev_lock (hdev); + adv =hci_find_adv_instance (hdev, instance); +if (adv) + adv->tx_power = rp->tx_power; +hci_dev_unlock (hdev); } }
@@ -1244,9 +1249,13 @@ static int hci_set_ext_adv_data_sync(struct hci_dev *hdev, u8 instance)int err;if (instance) { +hci_dev_lock (hdev); + adv =hci_find_adv_instance (hdev, instance); -if (!adv || !adv->adv_data_changed) +if (!adv || !adv->adv_data_changed) { +hci_dev_unlock (hdev);return 0 ; + } } len = eir_create_adv_data(hdev, instance, pdu->data,
@@ -1257,16 +1266,27 @@ static int hci_set_ext_adv_data_sync(struct hci_dev *hdev, u8 instance) pdu->operation =LE_SET_ADV_DATA_OP_COMPLETE ; pdu->frag_pref =LE_SET_ADV_DATA_NO_FRAG ; +if (adv) { + adv->adv_data_changed = false; +hci_dev_unlock (hdev); + } + err =__hci_cmd_sync_status (hdev,HCI_OP_LE_SET_EXT_ADV_DATA ,struct_size (pdu, data, len), pdu,HCI_CMD_TIMEOUT ); -if (err) +if (err) { +if (instance) { +hci_dev_lock (hdev); + adv =hci_find_adv_instance (hdev, instance); +if (adv) + adv->adv_data_changed = true; +hci_dev_unlock (hdev); + } +return err; + } -/* Update data if the command succeed */ -if (adv) { - adv->adv_data_changed = false; - }else { +if (!instance) {memcpy (hdev->adv_data, pdu->data, len); hdev->adv_data_len = len; }
@@ -1320,22 +1340,22 @@ int hci_setup_ext_adv_instance_sync(struct hci_dev *hdev, u8 instance)struct adv_info *adv;bool secondary_adv; -if (instance >0 ) { - adv =hci_find_adv_instance (hdev, instance); -if (!adv) -return -EINVAL ; - }else { - adv =NULL ; - } -/* Updating parameters of an active instance will return a -* Command Disallowed error, so we must first disable the -* instance if it is active. +* Command Disallowed error, so disable it before taking a snapshot. */ -if (adv) { +if (instance >0 ) { err =hci_disable_ext_adv_instance_sync (hdev, instance);if (err)return err; + +hci_dev_lock (hdev); + adv =hci_find_adv_instance (hdev, instance); +if (!adv) { +hci_dev_unlock (hdev); +return -EINVAL ; + } + }else { + adv =NULL ; } flags =hci_adv_instance_flags (hdev, instance);
@@ -1346,8 +1366,11 @@ int hci_setup_ext_adv_instance_sync(struct hci_dev *hdev, u8 instance) connectable = (flags &MGMT_ADV_FLAG_CONNECTABLE ) ||mgmt_get_connectable (hdev); -if (!is_advertising_allowed (hdev, connectable)) +if (!is_advertising_allowed (hdev, connectable)) { +if (instance) +hci_dev_unlock (hdev);return -EPERM ; + }/* Set require_privacy to true only when non-connectable * advertising is used and it is not periodic.
@@ -1358,8 +1381,11 @@ int hci_setup_ext_adv_instance_sync(struct hci_dev *hdev, u8 instance) err =hci_get_random_address (hdev, require_privacy,adv_use_rpa (hdev, flags), adv, &own_addr_type, &random_addr); -if (err <0 ) +if (err <0 ) { +if (instance) +hci_dev_unlock (hdev);return err; + }memset (&cp,0 ,sizeof (cp));
@@ -1408,6 +1434,9 @@ int hci_setup_ext_adv_instance_sync(struct hci_dev *hdev, u8 instance) cp.channel_map = hdev->le_adv_channel_map; cp.handle = instance; +if (instance) +hci_dev_unlock (hdev); +if (flags &MGMT_ADV_FLAG_SEC_2M ) { cp.primary_phy =HCI_ADV_PHY_1M ; cp.secondary_phy =HCI_ADV_PHY_2M ;
@@ -1420,12 +1449,12 @@ int hci_setup_ext_adv_instance_sync(struct hci_dev *hdev, u8 instance) cp.secondary_phy =HCI_ADV_PHY_1M ; } - err =hci_set_ext_adv_params_sync (hdev, adv, &cp, &rp); + err =hci_set_ext_adv_params_sync (hdev, instance, &cp, &rp);if (err)return err;/* Update adv data as tx power is known now */ - err =hci_set_ext_adv_data_sync (hdev, cp.handle); + err =hci_set_ext_adv_data_sync (hdev, instance);if (err)return err;
@@ -1433,9 +1462,14 @@ int hci_setup_ext_adv_instance_sync(struct hci_dev *hdev, u8 instance) own_addr_type == ADDR_LE_DEV_RANDOM_RESOLVED) && bacmp(&random_addr, BDADDR_ANY)) {/* Check if random address need to be updated */ -if (adv) { -if (!bacmp (&random_addr, &adv->random_addr)) +if (instance) { +hci_dev_lock (hdev); + adv =hci_find_adv_instance (hdev, instance); +if (!adv || !bacmp (&random_addr, &adv->random_addr)) { +hci_dev_unlock (hdev);return 0 ; + } +hci_dev_unlock (hdev); }else {if (!bacmp (&random_addr, &hdev->random_addr))return 0 ;
@@ -1457,9 +1491,13 @@ static int hci_set_ext_scan_rsp_data_sync(struct hci_dev *hdev, u8 instance)int err;if (instance) { +hci_dev_lock (hdev); + adv =hci_find_adv_instance (hdev, instance); -if (!adv || !adv->scan_rsp_changed) +if (!adv || !adv->scan_rsp_changed) { +hci_dev_unlock (hdev);return 0 ; + } } len =eir_create_scan_rsp (hdev, instance, pdu->data);
@@ -1469,15 +1507,27 @@ static int hci_set_ext_scan_rsp_data_sync(struct hci_dev *hdev, u8 instance) pdu->operation =LE_SET_ADV_DATA_OP_COMPLETE ; pdu->frag_pref =LE_SET_ADV_DATA_NO_FRAG ; +if (adv) { + adv->scan_rsp_changed = false; +hci_dev_unlock (hdev); + } + err =__hci_cmd_sync_status (hdev,HCI_OP_LE_SET_EXT_SCAN_RSP_DATA ,struct_size (pdu, data, len), pdu,HCI_CMD_TIMEOUT ); -if (err) +if (err) { +if (instance) { +hci_dev_lock (hdev); + adv =hci_find_adv_instance (hdev, instance); +if (adv) + adv->scan_rsp_changed = true; +hci_dev_unlock (hdev); + } +return err; + } -if (adv) { - adv->scan_rsp_changed = false; - }else { +if (!instance) {memcpy (hdev->scan_rsp_data, pdu->data, len); hdev->scan_rsp_data_len = len; }
@@ -1492,8 +1542,14 @@ static int __hci_set_scan_rsp_data_sync(struct hci_dev *hdev, u8 instance)memset (&cp,0 ,sizeof (cp)); +if (instance) +hci_dev_lock (hdev); + len =eir_create_scan_rsp (hdev, instance, cp.data); +if (instance) +hci_dev_unlock (hdev); +if (hdev->scan_rsp_data_len == len && !memcmp (cp.data, hdev->scan_rsp_data, len))return 0 ;
@@ -1626,9 +1682,13 @@ static int hci_set_per_adv_data_sync(struct hci_dev *hdev, u8 instance)struct adv_info *adv =NULL ;if (instance) { +hci_dev_lock (hdev); + adv =hci_find_adv_instance (hdev, instance); -if (!adv || !adv->periodic) +if (!adv || !adv->periodic) { +hci_dev_unlock (hdev);return 0 ; + } } len =eir_create_per_adv_data (hdev, instance, pdu->data);
@@ -1637,6 +1697,9 @@ static int hci_set_per_adv_data_sync(struct hci_dev *hdev, u8 instance) pdu->handle = adv ? adv->handle : instance; pdu->operation =LE_SET_ADV_DATA_OP_COMPLETE ; +if (adv) +hci_dev_unlock (hdev); +return __hci_cmd_sync_status (hdev,HCI_OP_LE_SET_PER_ADV_DATA ,struct_size (pdu, data, len), pdu,HCI_CMD_TIMEOUT );
@@ -6272,7 +6335,7 @@ static int hci_le_ext_directed_advertising_sync(struct hci_dev *hdev,if (err)return err; - err =hci_set_ext_adv_params_sync (hdev,NULL , &cp, &rp); + err =hci_set_ext_adv_params_sync (hdev,0 , &cp, &rp);if (err)return err;
Signature · 1 line
2.53.0