bluetooth pull request for net:

Core:
 
  - hci_core: Serialize ACL scheduling with channel deletion
  - hci_core: Serialize SCO and ISO scheduling with teardown
  - hci_core: Serialize fragmented ISO packet queueing
  - hci_core: Fix inquiry cache timestamps on 64-bit systems
  - hci_core: free the HCI ID if naming fails
  - hci_conn: Lock parent access during enhanced SCO setup
  - hci_sync: Fix inquiry cache use-after-free
  - hci_sync: don't drain cmd_sync backlog on unregister
  - RFCOMM: Fix initial port reference race
  - SMP: Serialize SMP remote OOB data access
 
 Drivers:
 
  - btintel: fix buffer over-read in btintel_hw_error()
  - btintel: validate DDC record lengths
  - btintel_pcie: fix plen overflow in btintel_pcie_recv_frame()
  - btintel_pcie: reject oversized TX packets in send_frame()
 -----BEGIN PGP SIGNATURE-----
 
 iQJNBAABCgA3FiEE7E6oRXp8w05ovYr/9JCA4xAyCykFAmq6h1YZHGx1aXoudm9u
 LmRlbnR6QGludGVsLmNvbQAKCRD0kIDjEDILKZEND/42wPb5lDOUU7VgMG6THiU/
 G6r4LMsjHkpGfkZox4OIAzXEbWvby9hZgilO2M8uXvHUqHrZ9IcfsA8rXgK+Bort
 EcoQGzq0X6RSysoJ3XOXj1QW6jXR8XqN9SongdaCR6I6xlGtXkMkbD8+APSII3oI
 xo5oU3MrsDPqIL+ow6GmYtAA1aGoD95QZL5S1tBaYwUB8mThDJQchE0DTzNk2jZ0
 gjgvfNBXkFjeIzrlVw/zhs7hJVebCgAZvrSlriCK1rvgU/3D2UKYYQPOu8ztjM18
 ITtKniIddPmALaXmJHiR/zt6nruw3/CUEkIdjd3a/XiP1n6xGCsGd9tte98Qr/8X
 Mr9ID7i7Old2WZc5nx+pOf+Bliy2omluzIsiqGO3KaoSCht2wZOwjKMEaIS1cS/m
 YdMTZ+nQshHRpO0w8nVObIC1lGLYBPjnwVv5IFXWiepT90vBij7pgDZHUgx2HO2n
 a7ejJcgDyHkGWI7loXAs1qZPNjcTRFO9IQ84wpARe94Hc7NaRcGBUCz3b3HH/5+H
 TuihQbVGVkV96jzeF7cIDifcZRky8yoUW/AoEfAuZzkQjonn9NBZ063ulUdZhEFc
 W8Psa1e/zrYqKgH7U9l4gfDn5yu1mEIFBKgAqZ2e0CbsJAklt3yY08xj0KI1kgaB
 FMRl+SFuGtDThQUk1ifFng==
 =ToWl
 -----END PGP SIGNATURE-----

Merge tag 'for-net-2026-09-28' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth

Luiz Augusto von Dentz says:

====================
bluetooth pull request for net:

Core:

 - hci_core: Serialize ACL scheduling with channel deletion
 - hci_core: Serialize SCO and ISO scheduling with teardown
 - hci_core: Serialize fragmented ISO packet queueing
 - hci_core: Fix inquiry cache timestamps on 64-bit systems
 - hci_core: free the HCI ID if naming fails
 - hci_conn: Lock parent access during enhanced SCO setup
 - hci_sync: Fix inquiry cache use-after-free
 - hci_sync: don't drain cmd_sync backlog on unregister
 - RFCOMM: Fix initial port reference race
 - SMP: Serialize SMP remote OOB data access

Drivers:

 - btintel: fix buffer over-read in btintel_hw_error()
 - btintel: validate DDC record lengths
 - btintel_pcie: fix plen overflow in btintel_pcie_recv_frame()
 - btintel_pcie: reject oversized TX packets in send_frame()

* tag 'for-net-2026-09-28' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth:
  Bluetooth: hci_core: Serialize SCO and ISO scheduling with teardown
  Bluetooth: hci_core: Serialize ACL scheduling with channel deletion
  Bluetooth: Serialize SMP remote OOB data access
  Bluetooth: RFCOMM: Fix initial port reference race
  Bluetooth: hci_sync: Fix inquiry cache use-after-free
  Bluetooth: hci_sync: don't drain cmd_sync backlog on unregister
  Bluetooth: hci_core: Serialize fragmented ISO packet queueing
  Bluetooth: hci_conn: Lock parent access during enhanced SCO setup
  Bluetooth: btintel: validate DDC record lengths
  Bluetooth: hci_core: free the HCI ID if naming fails
  Bluetooth: hci_core: Fix inquiry cache timestamps on 64-bit systems
  Bluetooth: btintel_pcie: reject oversized TX packets in send_frame()
  Bluetooth: btintel_pcie: fix plen overflow in btintel_pcie_recv_frame()
  Bluetooth: btintel: fix buffer over-read in btintel_hw_error()
====================

Link: https://patch.msgid.link/20260928152919.942973-1-luiz.dentz@gmail.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jakub Kicinski 2026-09-28 17:54:53 -07:00
commit 382cf97689
9 changed files with 106 additions and 28 deletions

View File

@ -289,7 +289,8 @@ void btintel_hw_error(struct hci_dev *hdev, u8 code)
goto unlock;
}
bt_dev_err(hdev, "Exception info %s", (char *)(skb->data + 1));
bt_dev_err(hdev, "Exception info %.*s", (int)(skb->len - 1),
(char *)(skb->data + 1));
kfree_skb(skb);
@ -408,8 +409,16 @@ int btintel_load_ddc_config(struct hci_dev *hdev, const char *ddc_name)
/* DDC file contains one or more DDC structure which has
* Length (1 byte), DDC ID (2 bytes), and DDC value (Length - 2).
*/
while (fw->size > fw_ptr - fw->data) {
u8 cmd_plen = fw_ptr[0] + sizeof(u8);
while (fw->size > (size_t)(fw_ptr - fw->data)) {
size_t remaining = fw->size - (fw_ptr - fw->data);
unsigned int cmd_plen = fw_ptr[0] + 1U;
if (cmd_plen < 3 || cmd_plen > U8_MAX || cmd_plen > remaining) {
bt_dev_err(hdev, "Malformed DDC record (plen=%u, remaining=%zu)",
cmd_plen, remaining);
release_firmware(fw);
return -EINVAL;
}
skb = __hci_cmd_sync(hdev, 0xfc8b, cmd_plen, fw_ptr,
HCI_INIT_TIMEOUT);

View File

@ -1197,7 +1197,7 @@ static int btintel_pcie_recv_frame(struct btintel_pcie_data *data,
{
int ret;
u8 pkt_type;
u16 plen;
u32 plen;
u32 pcie_pkt_type;
void *pdata;
struct hci_dev *hdev = data->hdev;
@ -2219,6 +2219,16 @@ static int btintel_pcie_send_frame(struct hci_dev *hdev,
if (test_bit(BTINTEL_PCIE_RECOVERY_IN_PROGRESS, &data->flags))
return -ENODEV;
/* Account for the 4-byte PCIe type header prepended before the
* DMA copy. Written as a subtraction to avoid wrap-around on
* attacker-controlled skb->len.
*/
if (skb->len > BTINTEL_PCIE_BUFFER_SIZE - BTINTEL_PCIE_HCI_TYPE_LEN) {
bt_dev_err(hdev, "Packet too large: %u > %u", skb->len,
BTINTEL_PCIE_BUFFER_SIZE - BTINTEL_PCIE_HCI_TYPE_LEN);
return -EMSGSIZE;
}
/* Due to the fw limitation, the type header of the packet should be
* 4 bytes unlike 1 byte for UART. In UART, the firmware can read
* the first byte to get the packet type and redirect the rest of data

View File

@ -62,7 +62,7 @@ struct inquiry_entry {
NAME_PENDING,
NAME_KNOWN,
} name_state;
__u32 timestamp;
unsigned long timestamp;
struct inquiry_data data;
};
@ -78,7 +78,7 @@ struct discovery_state {
struct list_head all; /* All devices found during inquiry */
struct list_head unknown; /* Name state not known */
struct list_head resolve; /* Name needs to be resolved */
__u32 timestamp;
unsigned long timestamp;
bdaddr_t last_adv_addr;
u8 last_adv_addr_type;
s8 last_adv_rssi;
@ -562,6 +562,7 @@ struct hci_dev {
struct list_head link_keys;
struct list_head long_term_keys;
struct list_head identity_resolving_keys;
struct mutex remote_oob_lock;
struct list_head remote_oob_data;
struct list_head le_accept_list;
struct list_head le_resolv_list;

View File

@ -302,11 +302,13 @@ static int hci_enhanced_setup_sync(struct hci_dev *hdev, void *data)
cp.tx_bandwidth = cpu_to_le32(0x00001f40);
cp.rx_bandwidth = cpu_to_le32(0x00001f40);
hci_dev_lock(hdev);
switch (conn->codec.id) {
case BT_CODEC_MSBC:
if (!find_next_esco_param(conn, esco_param_msbc,
ARRAY_SIZE(esco_param_msbc)))
return -EINVAL;
goto unlock;
param = &esco_param_msbc[conn->attempt - 1];
cp.tx_coding_format.id = 0x05;
@ -332,7 +334,7 @@ static int hci_enhanced_setup_sync(struct hci_dev *hdev, void *data)
case BT_CODEC_TRANSPARENT:
if (!find_next_esco_param(conn, esco_param_msbc,
ARRAY_SIZE(esco_param_msbc)))
return -EINVAL;
goto unlock;
param = &esco_param_msbc[conn->attempt - 1];
cp.tx_coding_format.id = 0x03;
@ -359,11 +361,11 @@ static int hci_enhanced_setup_sync(struct hci_dev *hdev, void *data)
if (conn->parent && lmp_esco_capable(conn->parent)) {
if (!find_next_esco_param(conn, esco_param_cvsd,
ARRAY_SIZE(esco_param_cvsd)))
return -EINVAL;
goto unlock;
param = &esco_param_cvsd[conn->attempt - 1];
} else {
if (conn->attempt > ARRAY_SIZE(sco_param_cvsd))
return -EINVAL;
goto unlock;
param = &sco_param_cvsd[conn->attempt - 1];
}
cp.tx_coding_format.id = 2;
@ -386,9 +388,11 @@ static int hci_enhanced_setup_sync(struct hci_dev *hdev, void *data)
cp.out_transport_unit_size = 16;
break;
default:
return -EINVAL;
goto unlock;
}
hci_dev_unlock(hdev);
cp.retrans_effort = param->retrans_effort;
cp.pkt_type = __cpu_to_le16(param->pkt_type);
cp.max_latency = __cpu_to_le16(param->max_latency);
@ -397,6 +401,10 @@ static int hci_enhanced_setup_sync(struct hci_dev *hdev, void *data)
return -EIO;
return 0;
unlock:
hci_dev_unlock(hdev);
return -EINVAL;
}
static bool hci_setup_sync_conn(struct hci_conn *conn, __u16 handle)

View File

@ -1489,8 +1489,10 @@ int hci_remove_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr,
BT_DBG("%s removing %pMR (%u)", hdev->name, bdaddr, bdaddr_type);
mutex_lock(&hdev->remote_oob_lock);
list_del(&data->list);
kfree(data);
mutex_unlock(&hdev->remote_oob_lock);
return 0;
}
@ -1499,10 +1501,12 @@ void hci_remote_oob_data_clear(struct hci_dev *hdev)
{
struct oob_data *data, *n;
mutex_lock(&hdev->remote_oob_lock);
list_for_each_entry_safe(data, n, &hdev->remote_oob_data, list) {
list_del(&data->list);
kfree(data);
}
mutex_unlock(&hdev->remote_oob_lock);
}
int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr,
@ -1511,11 +1515,14 @@ int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr,
{
struct oob_data *data;
mutex_lock(&hdev->remote_oob_lock);
data = hci_find_remote_oob_data(hdev, bdaddr, bdaddr_type);
if (!data) {
data = kmalloc_obj(*data);
if (!data)
if (!data) {
mutex_unlock(&hdev->remote_oob_lock);
return -ENOMEM;
}
bacpy(&data->bdaddr, bdaddr);
data->bdaddr_type = bdaddr_type;
@ -1548,6 +1555,8 @@ int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr,
BT_DBG("%s for %pMR", hdev->name, bdaddr);
mutex_unlock(&hdev->remote_oob_lock);
return 0;
}
@ -2485,6 +2494,7 @@ struct hci_dev *hci_alloc_dev_priv(int sizeof_priv)
mutex_init(&hdev->lock);
mutex_init(&hdev->req_lock);
mutex_init(&hdev->mgmt_pending_lock);
mutex_init(&hdev->remote_oob_lock);
ida_init(&hdev->unset_handle_ida);
@ -2557,8 +2567,10 @@ int hci_register_dev(struct hci_dev *hdev)
return id;
error = dev_set_name(&hdev->dev, "hci%u", id);
if (error)
if (error) {
ida_free(&hci_index_ida, id);
return error;
}
hdev->name = dev_name(&hdev->dev);
hdev->id = id;
@ -3325,6 +3337,8 @@ static void hci_queue_iso(struct hci_conn *conn, struct sk_buff_head *queue,
skb_shinfo(skb)->frag_list = NULL;
spin_lock_bh(&queue->lock);
__skb_queue_tail(queue, skb);
do {
@ -3339,6 +3353,8 @@ static void hci_queue_iso(struct hci_conn *conn, struct sk_buff_head *queue,
__skb_queue_tail(queue, skb);
} while (list);
spin_unlock_bh(&queue->lock);
}
bt_dev_dbg(hdev, "hcon %p queued %d", conn, skb_queue_len(queue));
@ -3402,9 +3418,6 @@ static struct hci_conn *hci_low_sent(struct hci_dev *hdev, __u8 type,
struct hci_conn *conn = NULL, *c;
unsigned int num = 0, min = ~0;
/* We don't have to lock device here. Connections are always
* added and removed with TX task disabled. */
rcu_read_lock();
list_for_each_entry_rcu(c, &h->list, list) {
@ -3609,13 +3622,15 @@ static void __check_timeout(struct hci_dev *hdev, unsigned int cnt, u8 type)
}
/* Schedule SCO */
static void hci_sched_sco(struct hci_dev *hdev, __u8 type)
static void __hci_sched_sco(struct hci_dev *hdev, __u8 type)
{
struct hci_conn *conn;
struct sk_buff *skb;
int quote, *cnt;
unsigned int pkts = hdev->sco_pkts;
lockdep_assert_held(&hdev->lock);
bt_dev_dbg(hdev, "type %u", type);
if (!hci_conn_num(hdev, type) || !pkts)
@ -3650,6 +3665,13 @@ static void hci_sched_sco(struct hci_dev *hdev, __u8 type)
queue_work(hdev->workqueue, &hdev->tx_work);
}
static void hci_sched_sco(struct hci_dev *hdev, __u8 type)
{
hci_dev_lock(hdev);
__hci_sched_sco(hdev, type);
hci_dev_unlock(hdev);
}
static void hci_sched_acl_pkt(struct hci_dev *hdev)
{
unsigned int cnt = hdev->acl_cnt;
@ -3659,6 +3681,8 @@ static void hci_sched_acl_pkt(struct hci_dev *hdev)
__check_timeout(hdev, cnt, ACL_LINK);
hci_dev_lock(hdev);
while (hdev->acl_cnt &&
(chan = hci_chan_sent(hdev, ACL_LINK, &quote))) {
u32 priority = (skb_peek(&chan->data_q))->priority;
@ -3683,13 +3707,15 @@ static void hci_sched_acl_pkt(struct hci_dev *hdev)
chan->conn->sent++;
/* Send pending SCO packets right away */
hci_sched_sco(hdev, SCO_LINK);
hci_sched_sco(hdev, ESCO_LINK);
__hci_sched_sco(hdev, SCO_LINK);
__hci_sched_sco(hdev, ESCO_LINK);
}
}
if (cnt != hdev->acl_cnt)
hci_prio_recalculate(hdev, ACL_LINK);
hci_dev_unlock(hdev);
}
static void hci_sched_acl(struct hci_dev *hdev)
@ -3718,6 +3744,8 @@ static void hci_sched_le(struct hci_dev *hdev)
__check_timeout(hdev, *cnt, LE_LINK);
hci_dev_lock(hdev);
tmp = *cnt;
while (*cnt && (chan = hci_chan_sent(hdev, LE_LINK, &quote))) {
u32 priority = (skb_peek(&chan->data_q))->priority;
@ -3739,13 +3767,15 @@ static void hci_sched_le(struct hci_dev *hdev)
chan->conn->sent++;
/* Send pending SCO packets right away */
hci_sched_sco(hdev, SCO_LINK);
hci_sched_sco(hdev, ESCO_LINK);
__hci_sched_sco(hdev, SCO_LINK);
__hci_sched_sco(hdev, ESCO_LINK);
}
}
if (*cnt != tmp)
hci_prio_recalculate(hdev, LE_LINK);
hci_dev_unlock(hdev);
}
/* Schedule iso */
@ -3764,6 +3794,8 @@ static void hci_sched_iso(struct hci_dev *hdev, __u8 type)
__check_timeout(hdev, *cnt, type);
hci_dev_lock(hdev);
while (*cnt && (conn = hci_low_sent(hdev, type, &quote))) {
while (quote-- && (skb = skb_dequeue(&conn->data_q))) {
BT_DBG("skb %p len %d", skb, skb->len);
@ -3777,6 +3809,8 @@ static void hci_sched_iso(struct hci_dev *hdev, __u8 type)
(*cnt)--;
}
}
hci_dev_unlock(hdev);
}
static void hci_tx_work(struct work_struct *work)

View File

@ -364,7 +364,7 @@ static int inquiry_cache_show(struct seq_file *f, void *p)
list_for_each_entry(e, &cache->all, all) {
struct inquiry_data *data = &e->data;
seq_printf(f, "%pMR %d %d %d 0x%.2x%.2x%.2x 0x%.4x %d %d %u\n",
seq_printf(f, "%pMR %d %d %d 0x%.2x%.2x%.2x 0x%.4x %d %d %lu\n",
&data->bdaddr,
data->pscan_rep_mode, data->pscan_period_mode,
data->pscan_mode, data->dev_class[2],

View File

@ -312,6 +312,10 @@ static void hci_cmd_sync_work(struct work_struct *work)
while (1) {
struct hci_cmd_sync_work_entry *entry;
/* Leave the backlog to hci_cmd_sync_clear() */
if (hci_dev_test_flag(hdev, HCI_UNREGISTER))
break;
mutex_lock(&hdev->cmd_sync_work_lock);
entry = list_first_entry_or_null(&hdev->cmd_sync_work_list,
struct hci_cmd_sync_work_entry,
@ -658,6 +662,8 @@ void hci_cmd_sync_clear(struct hci_dev *hdev)
{
struct hci_cmd_sync_work_entry *entry, *tmp;
/* cmd_work is disabled, the pending request can only time out */
hci_cmd_sync_cancel_sync(hdev, ENODEV);
cancel_work_sync(&hdev->cmd_sync_work);
cancel_work_sync(&hdev->reenable_adv_work);
@ -5764,6 +5770,7 @@ int hci_stop_discovery_sync(struct hci_dev *hdev)
{
struct discovery_state *d = &hdev->discovery;
struct inquiry_entry *e;
bdaddr_t addr;
int err;
bt_dev_dbg(hdev, "state %u", hdev->discovery.state);
@ -5799,15 +5806,21 @@ int hci_stop_discovery_sync(struct hci_dev *hdev)
return 0;
if (d->state == DISCOVERY_RESOLVING || d->state == DISCOVERY_STOPPING) {
hci_dev_lock(hdev);
e = hci_inquiry_cache_lookup_resolve(hdev, BDADDR_ANY,
NAME_PENDING);
if (!e)
if (!e) {
hci_dev_unlock(hdev);
return 0;
}
bacpy(&addr, &e->data.bdaddr);
hci_dev_unlock(hdev);
/* Ignore cancel errors since it should interfere with stopping
* of the discovery.
*/
hci_remote_name_cancel_sync(hdev, &e->data.bdaddr);
hci_remote_name_cancel_sync(hdev, &addr);
}
return 0;
@ -7236,6 +7249,7 @@ static int hci_acl_create_conn_sync(struct hci_dev *hdev, void *data)
bacpy(&cp.bdaddr, &conn->dst);
cp.pscan_rep_mode = 0x02;
hci_dev_lock(hdev);
ie = hci_inquiry_cache_lookup(hdev, &conn->dst);
if (ie) {
if (inquiry_entry_age(ie) <= INQUIRY_ENTRY_AGE_MAX) {
@ -7247,6 +7261,7 @@ static int hci_acl_create_conn_sync(struct hci_dev *hdev, void *data)
memcpy(conn->dev_class, ie->data.dev_class, 3);
}
hci_dev_unlock(hdev);
cp.pkt_type = cpu_to_le16(conn->pkt_type);
if (lmp_rswitch_capable(hdev) && !(hdev->link_mode & HCI_LM_MASTER))

View File

@ -462,7 +462,7 @@ static int __rfcomm_release_dev(void __user *arg)
/* Shut down TTY synchronously before freeing rfcomm_dev */
tty_port_tty_vhangup(&dev->port);
if (!test_bit(RFCOMM_TTY_OWNED, &dev->status))
if (!test_and_set_bit(RFCOMM_TTY_OWNED, &dev->status))
tty_port_put(&dev->port);
tty_port_put(&dev->port);
@ -724,10 +724,9 @@ static int rfcomm_tty_install(struct tty_driver *driver, struct tty_struct *tty)
* when the last process closes the tty. The behaviour is expected by
* userspace.
*/
if (test_bit(RFCOMM_RELEASE_ONHUP, &dev->flags)) {
set_bit(RFCOMM_TTY_OWNED, &dev->status);
if (test_bit(RFCOMM_RELEASE_ONHUP, &dev->flags) &&
!test_and_set_bit(RFCOMM_TTY_OWNED, &dev->status))
tty_port_put(&dev->port);
}
return 0;
}

View File

@ -662,6 +662,7 @@ static void build_pairing_cmd(struct l2cap_conn *conn,
else
bdaddr_type = BDADDR_LE_RANDOM;
mutex_lock(&hdev->remote_oob_lock);
oob_data = hci_find_remote_oob_data(hdev, &hcon->dst,
bdaddr_type);
if (oob_data && oob_data->present) {
@ -672,6 +673,7 @@ static void build_pairing_cmd(struct l2cap_conn *conn,
SMP_DBG("OOB Remote Confirmation: %16phN", smp->pcnf);
SMP_DBG("OOB Remote Random: %16phN", smp->rr);
}
mutex_unlock(&hdev->remote_oob_lock);
} else {
authreq &= ~SMP_AUTH_SC;