mirror of
https://github.com/torvalds/linux.git
synced 2026-05-12 16:18:45 +02:00
bluetooth pull request for net:
- hci_conn: fix potential UAF in create_big_sync - hci_event: fix memset typo - hci_event: Fix OOB read and infinite loop in hci_le_create_big_complete_evt - L2CAP: fix MPS check in l2cap_ecred_reconf_req - L2CAP: defer conn param update to avoid conn->lock/hdev->lock inversion - L2CAP: Fix null-ptr-deref in l2cap_sock_state_change_cb() - L2CAP: Fix null-ptr-deref in l2cap_sock_get_sndtimeo_cb() - L2CAP: Fix null-ptr-deref in l2cap_sock_new_connection_cb() - RFCOMM: pull credit byte with skb_pull_data() - SCO: fix sleeping under spinlock in sco_conn_ready - SCO: hold sk properly in sco_conn_ready - ISO: Fix data-race on dst in iso_sock_connect() - ISO: Fix data-race on iso_pi(sk) in socket and HCI event paths - bnep: fix incorrect length parsing in bnep_rx_frame() extension handling - hci_uart: Fix NULL deref in recv callbacks when priv is uninitialized - virtio_bt: clamp rx length before skb_put - virtio_bt: validate rx pkt_type header length - HIDP: serialise l2cap_unregister_user via hidp_session_sem - btintel_pcie: treat boot stage bit 12 as warning - btmtk: validate WMT event SKB length before struct access -----BEGIN PGP SIGNATURE----- iQJNBAABCgA3FiEE7E6oRXp8w05ovYr/9JCA4xAyCykFAmn7qCwZHGx1aXoudm9u LmRlbnR6QGludGVsLmNvbQAKCRD0kIDjEDILKdbuD/wIj4GwiCd/vWz6qEdbK3Xl naw2i1HH4W3cLSDbEREQ7pJos+Uti6VqdzgW3yldzpKG3rZRjCx5hh3HxqmpuWmI LbCv4cI13ZfPgjfRqyjmX2AhpY8zkeOVy5wFiIVQsqsRm6s30g7lqxPkMPYG0K2G FDjS06iZsoRGXRFp2+lqpSk1H/90Bcz78yDyEr0qoHxpxUace2lx5gVmoZQxWasx Y5dcuNSVUvnftHMd4Lv2pehllpJDbmuyll1aVrhqEueRqdmyocjINXZRyYTdrECz 8WR4tiax1zvl/eYgJ6zdVLJ1Iva1HyiTVN5tY0uSM03+u1P/OxSInkoo2VSZoIIK bQUFQ92Xml1J0qL6g0rwEHESEYzaJXz9Ai+XdAFzHv1RkziiYRkDqvPFjivqh/JG QeOuNosSKGfG9V5m02Ym/GVTdE59xonukNr+RaIdpt6djsybv4go+E8RpnxVyQvy 5CMKchOvE6TnW3JRcaaXtC9cdMfOAjgBiebnWTguFBLutpPf1z8EhiKNFyYlt1yb r8tNhci6jimoD9hKzemEuKwyP07HnBo8B361kHByFYfhBHs+XZANtcVyMo6HtbqE 94eRdWvKBvG3ixAP5/ujqrmp9HFyMBhZPc3XimZxkBx71/JCqcSpsQHGtQRvAiXy 4FKJXqDINoOtpkggRQfkyQ== =KbxG -----END PGP SIGNATURE----- Merge tag 'for-net-2026-05-06' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth Luiz Augusto von Dentz says: ==================== bluetooth pull request for net: - hci_conn: fix potential UAF in create_big_sync - hci_event: fix memset typo - hci_event: Fix OOB read and infinite loop in hci_le_create_big_complete_evt - L2CAP: fix MPS check in l2cap_ecred_reconf_req - L2CAP: defer conn param update to avoid conn->lock/hdev->lock inversion - L2CAP: Fix null-ptr-deref in l2cap_sock_state_change_cb() - L2CAP: Fix null-ptr-deref in l2cap_sock_get_sndtimeo_cb() - L2CAP: Fix null-ptr-deref in l2cap_sock_new_connection_cb() - RFCOMM: pull credit byte with skb_pull_data() - SCO: fix sleeping under spinlock in sco_conn_ready - SCO: hold sk properly in sco_conn_ready - ISO: Fix data-race on dst in iso_sock_connect() - ISO: Fix data-race on iso_pi(sk) in socket and HCI event paths - bnep: fix incorrect length parsing in bnep_rx_frame() extension handling - hci_uart: Fix NULL deref in recv callbacks when priv is uninitialized - virtio_bt: clamp rx length before skb_put - virtio_bt: validate rx pkt_type header length - HIDP: serialise l2cap_unregister_user via hidp_session_sem - btintel_pcie: treat boot stage bit 12 as warning - btmtk: validate WMT event SKB length before struct access * tag 'for-net-2026-05-06' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth: Bluetooth: HIDP: serialise l2cap_unregister_user via hidp_session_sem Bluetooth: hci_event: fix memset typo Bluetooth: RFCOMM: pull credit byte with skb_pull_data() Bluetooth: virtio_bt: validate rx pkt_type header length Bluetooth: virtio_bt: clamp rx length before skb_put Bluetooth: btmtk: validate WMT event SKB length before struct access Bluetooth: ISO: Fix data-race on iso_pi(sk) in socket and HCI event paths Bluetooth: ISO: Fix data-race on dst in iso_sock_connect() Bluetooth: hci_uart: Fix NULL deref in recv callbacks when priv is uninitialized Bluetooth: btintel_pcie: treat boot stage bit 12 as warning Bluetooth: SCO: hold sk properly in sco_conn_ready Bluetooth: L2CAP: Fix null-ptr-deref in l2cap_sock_new_connection_cb() Bluetooth: L2CAP: Fix null-ptr-deref in l2cap_sock_get_sndtimeo_cb() Bluetooth: L2CAP: Fix null-ptr-deref in l2cap_sock_state_change_cb() Bluetooth: l2cap: defer conn param update to avoid conn->lock/hdev->lock inversion Bluetooth: l2cap: fix MPS check in l2cap_ecred_reconf_req Bluetooth: bnep: fix incorrect length parsing in bnep_rx_frame() extension handling Bluetooth: hci_event: Fix OOB read and infinite loop in hci_le_create_big_complete_evt Bluetooth: hci_conn: fix potential UAF in create_big_sync Bluetooth: SCO: fix sleeping under spinlock in sco_conn_ready ==================== Link: https://patch.msgid.link/20260506204553.58686-1-luiz.dentz@gmail.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
commit
bd75e1003d
|
|
@ -289,6 +289,9 @@ static inline void btintel_pcie_dump_debug_registers(struct hci_dev *hdev)
|
|||
skb_put_data(skb, buf, strlen(buf));
|
||||
data->boot_stage_cache = reg;
|
||||
|
||||
if (reg & BTINTEL_PCIE_CSR_BOOT_STAGE_DEVICE_WARNING)
|
||||
bt_dev_warn(hdev, "Controller device warning (boot_stage: 0x%8.8x)", reg);
|
||||
|
||||
reg = btintel_pcie_rd_reg32(data, BTINTEL_PCIE_CSR_IPC_STATUS_REG);
|
||||
snprintf(buf, sizeof(buf), "ipc status: 0x%8.8x", reg);
|
||||
skb_put_data(skb, buf, strlen(buf));
|
||||
|
|
@ -880,8 +883,11 @@ static inline bool btintel_pcie_in_lockdown(struct btintel_pcie_data *data)
|
|||
|
||||
static inline bool btintel_pcie_in_error(struct btintel_pcie_data *data)
|
||||
{
|
||||
return (data->boot_stage_cache & BTINTEL_PCIE_CSR_BOOT_STAGE_DEVICE_ERR) ||
|
||||
(data->boot_stage_cache & BTINTEL_PCIE_CSR_BOOT_STAGE_ABORT_HANDLER);
|
||||
if (data->boot_stage_cache & BTINTEL_PCIE_CSR_BOOT_STAGE_DEVICE_WARNING)
|
||||
bt_dev_warn(data->hdev, "Controller device warning (boot_stage: 0x%8.8x)",
|
||||
data->boot_stage_cache);
|
||||
|
||||
return data->boot_stage_cache & BTINTEL_PCIE_CSR_BOOT_STAGE_ABORT_HANDLER;
|
||||
}
|
||||
|
||||
static void btintel_pcie_msix_gp1_handler(struct btintel_pcie_data *data)
|
||||
|
|
@ -914,7 +920,8 @@ static void btintel_pcie_msix_gp0_handler(struct btintel_pcie_data *data)
|
|||
data->img_resp_cache = reg;
|
||||
|
||||
if (btintel_pcie_in_error(data)) {
|
||||
bt_dev_err(data->hdev, "Controller in error state");
|
||||
bt_dev_err(data->hdev, "Controller in error state (boot_stage: 0x%8.8x)",
|
||||
data->boot_stage_cache);
|
||||
btintel_pcie_dump_debug_registers(data->hdev);
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@
|
|||
#define BTINTEL_PCIE_CSR_BOOT_STAGE_OPFW (BIT(2))
|
||||
#define BTINTEL_PCIE_CSR_BOOT_STAGE_ROM_LOCKDOWN (BIT(10))
|
||||
#define BTINTEL_PCIE_CSR_BOOT_STAGE_IML_LOCKDOWN (BIT(11))
|
||||
#define BTINTEL_PCIE_CSR_BOOT_STAGE_DEVICE_ERR (BIT(12))
|
||||
#define BTINTEL_PCIE_CSR_BOOT_STAGE_DEVICE_WARNING (BIT(12))
|
||||
#define BTINTEL_PCIE_CSR_BOOT_STAGE_ABORT_HANDLER (BIT(13))
|
||||
#define BTINTEL_PCIE_CSR_BOOT_STAGE_DEVICE_HALTED (BIT(14))
|
||||
#define BTINTEL_PCIE_CSR_BOOT_STAGE_MAC_ACCESS_ON (BIT(16))
|
||||
|
|
|
|||
|
|
@ -695,8 +695,13 @@ static int btmtk_usb_hci_wmt_sync(struct hci_dev *hdev,
|
|||
if (data->evt_skb == NULL)
|
||||
goto err_free_wc;
|
||||
|
||||
/* Parse and handle the return WMT event */
|
||||
wmt_evt = (struct btmtk_hci_wmt_evt *)data->evt_skb->data;
|
||||
wmt_evt = skb_pull_data(data->evt_skb, sizeof(*wmt_evt));
|
||||
if (!wmt_evt) {
|
||||
bt_dev_err(hdev, "WMT event too short (%u bytes)",
|
||||
data->evt_skb->len);
|
||||
err = -EINVAL;
|
||||
goto err_free_skb;
|
||||
}
|
||||
if (wmt_evt->whdr.op != hdr->op) {
|
||||
bt_dev_err(hdev, "Wrong op received %d expected %d",
|
||||
wmt_evt->whdr.op, hdr->op);
|
||||
|
|
@ -712,6 +717,12 @@ static int btmtk_usb_hci_wmt_sync(struct hci_dev *hdev,
|
|||
status = BTMTK_WMT_PATCH_DONE;
|
||||
break;
|
||||
case BTMTK_WMT_FUNC_CTRL:
|
||||
if (!skb_pull_data(data->evt_skb,
|
||||
sizeof(wmt_evt_funcc->status))) {
|
||||
err = -EINVAL;
|
||||
goto err_free_skb;
|
||||
}
|
||||
|
||||
wmt_evt_funcc = (struct btmtk_hci_wmt_evt_funcc *)wmt_evt;
|
||||
if (be16_to_cpu(wmt_evt_funcc->status) == 0x404)
|
||||
status = BTMTK_WMT_ON_DONE;
|
||||
|
|
|
|||
|
|
@ -191,6 +191,9 @@ static int ath_recv(struct hci_uart *hu, const void *data, int count)
|
|||
{
|
||||
struct ath_struct *ath = hu->priv;
|
||||
|
||||
if (!ath)
|
||||
return -ENODEV;
|
||||
|
||||
ath->rx_skb = h4_recv_buf(hu, ath->rx_skb, data, count,
|
||||
ath_recv_pkts, ARRAY_SIZE(ath_recv_pkts));
|
||||
if (IS_ERR(ath->rx_skb)) {
|
||||
|
|
|
|||
|
|
@ -585,6 +585,9 @@ static int bcsp_recv(struct hci_uart *hu, const void *data, int count)
|
|||
if (!test_bit(HCI_UART_REGISTERED, &hu->flags))
|
||||
return -EUNATCH;
|
||||
|
||||
if (!bcsp)
|
||||
return -ENODEV;
|
||||
|
||||
BT_DBG("hu %p count %d rx_state %d rx_count %ld",
|
||||
hu, count, bcsp->rx_state, bcsp->rx_count);
|
||||
|
||||
|
|
|
|||
|
|
@ -109,6 +109,9 @@ static int h4_recv(struct hci_uart *hu, const void *data, int count)
|
|||
{
|
||||
struct h4_struct *h4 = hu->priv;
|
||||
|
||||
if (!h4)
|
||||
return -ENODEV;
|
||||
|
||||
h4->rx_skb = h4_recv_buf(hu, h4->rx_skb, data, count,
|
||||
h4_recv_pkts, ARRAY_SIZE(h4_recv_pkts));
|
||||
if (IS_ERR(h4->rx_skb)) {
|
||||
|
|
|
|||
|
|
@ -587,6 +587,9 @@ static int h5_recv(struct hci_uart *hu, const void *data, int count)
|
|||
struct h5 *h5 = hu->priv;
|
||||
const unsigned char *ptr = data;
|
||||
|
||||
if (!h5)
|
||||
return -ENODEV;
|
||||
|
||||
BT_DBG("%s pending %zu count %d", hu->hdev->name, h5->rx_pending,
|
||||
count);
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
#include <net/bluetooth/hci_core.h>
|
||||
|
||||
#define VERSION "0.1"
|
||||
#define VIRTBT_RX_BUF_SIZE 1000
|
||||
|
||||
enum {
|
||||
VIRTBT_VQ_TX,
|
||||
|
|
@ -33,11 +34,11 @@ static int virtbt_add_inbuf(struct virtio_bluetooth *vbt)
|
|||
struct sk_buff *skb;
|
||||
int err;
|
||||
|
||||
skb = alloc_skb(1000, GFP_KERNEL);
|
||||
skb = alloc_skb(VIRTBT_RX_BUF_SIZE, GFP_KERNEL);
|
||||
if (!skb)
|
||||
return -ENOMEM;
|
||||
|
||||
sg_init_one(sg, skb->data, 1000);
|
||||
sg_init_one(sg, skb->data, VIRTBT_RX_BUF_SIZE);
|
||||
|
||||
err = virtqueue_add_inbuf(vq, sg, 1, skb, GFP_KERNEL);
|
||||
if (err < 0) {
|
||||
|
|
@ -197,6 +198,7 @@ static int virtbt_shutdown_generic(struct hci_dev *hdev)
|
|||
|
||||
static void virtbt_rx_handle(struct virtio_bluetooth *vbt, struct sk_buff *skb)
|
||||
{
|
||||
size_t min_hdr;
|
||||
__u8 pkt_type;
|
||||
|
||||
pkt_type = *((__u8 *) skb->data);
|
||||
|
|
@ -204,16 +206,32 @@ static void virtbt_rx_handle(struct virtio_bluetooth *vbt, struct sk_buff *skb)
|
|||
|
||||
switch (pkt_type) {
|
||||
case HCI_EVENT_PKT:
|
||||
min_hdr = sizeof(struct hci_event_hdr);
|
||||
break;
|
||||
case HCI_ACLDATA_PKT:
|
||||
min_hdr = sizeof(struct hci_acl_hdr);
|
||||
break;
|
||||
case HCI_SCODATA_PKT:
|
||||
min_hdr = sizeof(struct hci_sco_hdr);
|
||||
break;
|
||||
case HCI_ISODATA_PKT:
|
||||
hci_skb_pkt_type(skb) = pkt_type;
|
||||
hci_recv_frame(vbt->hdev, skb);
|
||||
min_hdr = sizeof(struct hci_iso_hdr);
|
||||
break;
|
||||
default:
|
||||
kfree_skb(skb);
|
||||
break;
|
||||
return;
|
||||
}
|
||||
|
||||
if (skb->len < min_hdr) {
|
||||
bt_dev_err_ratelimited(vbt->hdev,
|
||||
"rx pkt_type 0x%02x payload %u < hdr %zu\n",
|
||||
pkt_type, skb->len, min_hdr);
|
||||
kfree_skb(skb);
|
||||
return;
|
||||
}
|
||||
|
||||
hci_skb_pkt_type(skb) = pkt_type;
|
||||
hci_recv_frame(vbt->hdev, skb);
|
||||
}
|
||||
|
||||
static void virtbt_rx_work(struct work_struct *work)
|
||||
|
|
@ -227,8 +245,15 @@ static void virtbt_rx_work(struct work_struct *work)
|
|||
if (!skb)
|
||||
return;
|
||||
|
||||
skb_put(skb, len);
|
||||
virtbt_rx_handle(vbt, skb);
|
||||
if (!len || len > VIRTBT_RX_BUF_SIZE) {
|
||||
bt_dev_err_ratelimited(vbt->hdev,
|
||||
"rx reply len %u outside [1, %u]\n",
|
||||
len, VIRTBT_RX_BUF_SIZE);
|
||||
kfree_skb(skb);
|
||||
} else {
|
||||
skb_put(skb, len);
|
||||
virtbt_rx_handle(vbt, skb);
|
||||
}
|
||||
|
||||
if (virtbt_add_inbuf(vbt) < 0)
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -2495,7 +2495,7 @@ void mgmt_adv_monitor_device_lost(struct hci_dev *hdev, u16 handle,
|
|||
bdaddr_t *bdaddr, u8 addr_type);
|
||||
|
||||
int hci_abort_conn(struct hci_conn *conn, u8 reason);
|
||||
u8 hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max, u16 latency,
|
||||
void hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max, u16 latency,
|
||||
u16 to_multiplier);
|
||||
void hci_le_start_enc(struct hci_conn *conn, __le16 ediv, __le64 rand,
|
||||
__u8 ltk[16], __u8 key_size);
|
||||
|
|
|
|||
|
|
@ -330,11 +330,18 @@ static int bnep_rx_frame(struct bnep_session *s, struct sk_buff *skb)
|
|||
goto badframe;
|
||||
break;
|
||||
case BNEP_FILTER_MULTI_ADDR_SET:
|
||||
case BNEP_FILTER_NET_TYPE_SET:
|
||||
/* Pull: ctrl type (1 b), len (2 b), data (len bytes) */
|
||||
if (!skb_pull(skb, 3 + *(u16 *)(skb->data + 1) * 2))
|
||||
case BNEP_FILTER_NET_TYPE_SET: {
|
||||
u8 *hdr;
|
||||
|
||||
/* Pull ctrl type (1 b) + len (2 b) */
|
||||
hdr = skb_pull_data(skb, 3);
|
||||
if (!hdr)
|
||||
goto badframe;
|
||||
/* Pull data (len bytes); length is big-endian */
|
||||
if (!skb_pull(skb, get_unaligned_be16(&hdr[1])))
|
||||
goto badframe;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
kfree_skb(skb);
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -480,40 +480,107 @@ bool hci_setup_sync(struct hci_conn *conn, __u16 handle)
|
|||
return hci_setup_sync_conn(conn, handle);
|
||||
}
|
||||
|
||||
u8 hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max, u16 latency,
|
||||
u16 to_multiplier)
|
||||
struct le_conn_update_data {
|
||||
struct hci_conn *conn;
|
||||
u16 min;
|
||||
u16 max;
|
||||
u16 latency;
|
||||
u16 to_multiplier;
|
||||
};
|
||||
|
||||
static int le_conn_update_sync(struct hci_dev *hdev, void *data)
|
||||
{
|
||||
struct hci_dev *hdev = conn->hdev;
|
||||
struct le_conn_update_data *d = data;
|
||||
struct hci_conn *conn = d->conn;
|
||||
struct hci_conn_params *params;
|
||||
struct hci_cp_le_conn_update cp;
|
||||
u16 timeout;
|
||||
u8 store_hint;
|
||||
int err;
|
||||
|
||||
/* Verify connection is still alive and read conn fields under
|
||||
* the same lock to prevent a concurrent disconnect from freeing
|
||||
* or reusing the connection while we build the HCI command.
|
||||
*/
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
if (!hci_conn_valid(hdev, conn)) {
|
||||
hci_dev_unlock(hdev);
|
||||
return -ECANCELED;
|
||||
}
|
||||
|
||||
memset(&cp, 0, sizeof(cp));
|
||||
cp.handle = cpu_to_le16(conn->handle);
|
||||
cp.conn_interval_min = cpu_to_le16(d->min);
|
||||
cp.conn_interval_max = cpu_to_le16(d->max);
|
||||
cp.conn_latency = cpu_to_le16(d->latency);
|
||||
cp.supervision_timeout = cpu_to_le16(d->to_multiplier);
|
||||
cp.min_ce_len = cpu_to_le16(0x0000);
|
||||
cp.max_ce_len = cpu_to_le16(0x0000);
|
||||
timeout = conn->conn_timeout;
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
|
||||
err = __hci_cmd_sync_status_sk(hdev, HCI_OP_LE_CONN_UPDATE,
|
||||
sizeof(cp), &cp,
|
||||
HCI_EV_LE_CONN_UPDATE_COMPLETE,
|
||||
timeout, NULL);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* Update stored connection parameters after the controller has
|
||||
* confirmed the update via the LE Connection Update Complete event.
|
||||
*/
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
params = hci_conn_params_lookup(hdev, &conn->dst, conn->dst_type);
|
||||
if (params) {
|
||||
params->conn_min_interval = min;
|
||||
params->conn_max_interval = max;
|
||||
params->conn_latency = latency;
|
||||
params->supervision_timeout = to_multiplier;
|
||||
params->conn_min_interval = d->min;
|
||||
params->conn_max_interval = d->max;
|
||||
params->conn_latency = d->latency;
|
||||
params->supervision_timeout = d->to_multiplier;
|
||||
store_hint = 0x01;
|
||||
} else {
|
||||
store_hint = 0x00;
|
||||
}
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
|
||||
memset(&cp, 0, sizeof(cp));
|
||||
cp.handle = cpu_to_le16(conn->handle);
|
||||
cp.conn_interval_min = cpu_to_le16(min);
|
||||
cp.conn_interval_max = cpu_to_le16(max);
|
||||
cp.conn_latency = cpu_to_le16(latency);
|
||||
cp.supervision_timeout = cpu_to_le16(to_multiplier);
|
||||
cp.min_ce_len = cpu_to_le16(0x0000);
|
||||
cp.max_ce_len = cpu_to_le16(0x0000);
|
||||
mgmt_new_conn_param(hdev, &conn->dst, conn->dst_type, store_hint,
|
||||
d->min, d->max, d->latency, d->to_multiplier);
|
||||
|
||||
hci_send_cmd(hdev, HCI_OP_LE_CONN_UPDATE, sizeof(cp), &cp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (params)
|
||||
return 0x01;
|
||||
static void le_conn_update_complete(struct hci_dev *hdev, void *data, int err)
|
||||
{
|
||||
struct le_conn_update_data *d = data;
|
||||
|
||||
return 0x00;
|
||||
hci_conn_put(d->conn);
|
||||
kfree(d);
|
||||
}
|
||||
|
||||
void hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max, u16 latency,
|
||||
u16 to_multiplier)
|
||||
{
|
||||
struct le_conn_update_data *d;
|
||||
|
||||
d = kzalloc_obj(*d);
|
||||
if (!d)
|
||||
return;
|
||||
|
||||
hci_conn_get(conn);
|
||||
d->conn = conn;
|
||||
d->min = min;
|
||||
d->max = max;
|
||||
d->latency = latency;
|
||||
d->to_multiplier = to_multiplier;
|
||||
|
||||
if (hci_cmd_sync_queue(conn->hdev, le_conn_update_sync, d,
|
||||
le_conn_update_complete) < 0) {
|
||||
hci_conn_put(conn);
|
||||
kfree(d);
|
||||
}
|
||||
}
|
||||
|
||||
void hci_le_start_enc(struct hci_conn *conn, __le16 ediv, __le64 rand,
|
||||
|
|
@ -2130,6 +2197,9 @@ static int create_big_sync(struct hci_dev *hdev, void *data)
|
|||
u32 flags = 0;
|
||||
int err;
|
||||
|
||||
if (!hci_conn_valid(hdev, conn))
|
||||
return -ECANCELED;
|
||||
|
||||
if (qos->bcast.out.phys == BIT(1))
|
||||
flags |= MGMT_ADV_FLAG_SEC_2M;
|
||||
|
||||
|
|
@ -2204,11 +2274,24 @@ static void create_big_complete(struct hci_dev *hdev, void *data, int err)
|
|||
|
||||
bt_dev_dbg(hdev, "conn %p", conn);
|
||||
|
||||
if (err == -ECANCELED)
|
||||
goto done;
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
if (!hci_conn_valid(hdev, conn))
|
||||
goto unlock;
|
||||
|
||||
if (err) {
|
||||
bt_dev_err(hdev, "Unable to create BIG: %d", err);
|
||||
hci_connect_cfm(conn, err);
|
||||
hci_conn_del(conn);
|
||||
}
|
||||
|
||||
unlock:
|
||||
hci_dev_unlock(hdev);
|
||||
done:
|
||||
hci_conn_put(conn);
|
||||
}
|
||||
|
||||
struct hci_conn *hci_bind_bis(struct hci_dev *hdev, bdaddr_t *dst, __u8 sid,
|
||||
|
|
@ -2336,10 +2419,11 @@ struct hci_conn *hci_connect_bis(struct hci_dev *hdev, bdaddr_t *dst,
|
|||
BT_BOUND, &data);
|
||||
|
||||
/* Queue start periodic advertising and create BIG */
|
||||
err = hci_cmd_sync_queue(hdev, create_big_sync, conn,
|
||||
err = hci_cmd_sync_queue(hdev, create_big_sync, hci_conn_get(conn),
|
||||
create_big_complete);
|
||||
if (err < 0) {
|
||||
hci_conn_drop(conn);
|
||||
hci_conn_put(conn);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7118,9 +7118,29 @@ static void hci_le_create_big_complete_evt(struct hci_dev *hdev, void *data,
|
|||
continue;
|
||||
}
|
||||
|
||||
if (hci_conn_set_handle(conn,
|
||||
__le16_to_cpu(ev->bis_handle[i++])))
|
||||
if (ev->num_bis <= i) {
|
||||
bt_dev_err(hdev,
|
||||
"Not enough BIS handles for BIG 0x%2.2x",
|
||||
ev->handle);
|
||||
ev->status = HCI_ERROR_UNSPECIFIED;
|
||||
hci_connect_cfm(conn, ev->status);
|
||||
hci_conn_del(conn);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (hci_conn_set_handle(conn,
|
||||
__le16_to_cpu(ev->bis_handle[i++]))) {
|
||||
bt_dev_err(hdev,
|
||||
"Failed to set BIS handle for BIG 0x%2.2x",
|
||||
ev->handle);
|
||||
/* Force error so BIG gets terminated as not all BIS
|
||||
* could be connected.
|
||||
*/
|
||||
ev->status = HCI_ERROR_UNSPECIFIED;
|
||||
hci_connect_cfm(conn, ev->status);
|
||||
hci_conn_del(conn);
|
||||
continue;
|
||||
}
|
||||
|
||||
conn->state = BT_CONNECTED;
|
||||
set_bit(HCI_CONN_BIG_CREATED, &conn->flags);
|
||||
|
|
@ -7129,7 +7149,10 @@ static void hci_le_create_big_complete_evt(struct hci_dev *hdev, void *data,
|
|||
hci_iso_setup_path(conn);
|
||||
}
|
||||
|
||||
if (!ev->status && !i)
|
||||
/* If there is an unexpected error or if no BISes have been connected
|
||||
* for the BIG, terminate it.
|
||||
*/
|
||||
if (ev->status == HCI_ERROR_UNSPECIFIED || (!ev->status && !i))
|
||||
/* If no BISes have been connected for the BIG,
|
||||
* terminate. This is in case all bound connections
|
||||
* have been closed before the BIG creation
|
||||
|
|
@ -7168,7 +7191,7 @@ static void hci_le_big_sync_established_evt(struct hci_dev *hdev, void *data,
|
|||
clear_bit(HCI_CONN_CREATE_BIG_SYNC, &conn->flags);
|
||||
|
||||
conn->num_bis = 0;
|
||||
memset(conn->bis, 0, sizeof(conn->num_bis));
|
||||
memset(conn->bis, 0, sizeof(conn->bis));
|
||||
|
||||
for (i = 0; i < ev->num_bis; i++) {
|
||||
u16 handle = le16_to_cpu(ev->bis[i]);
|
||||
|
|
|
|||
|
|
@ -1035,6 +1035,28 @@ static struct hidp_session *hidp_session_find(const bdaddr_t *bdaddr)
|
|||
return session;
|
||||
}
|
||||
|
||||
/*
|
||||
* Consume session->conn: clear the member under hidp_session_sem, then
|
||||
* l2cap_unregister_user() and l2cap_conn_put() the snapshot outside the
|
||||
* sem. At most one caller wins; later callers see NULL and skip. The
|
||||
* reference is the one hidp_session_new() took via l2cap_conn_get().
|
||||
*/
|
||||
static void hidp_session_unregister_conn(struct hidp_session *session)
|
||||
{
|
||||
struct l2cap_conn *conn;
|
||||
|
||||
down_write(&hidp_session_sem);
|
||||
conn = session->conn;
|
||||
if (conn)
|
||||
session->conn = NULL;
|
||||
up_write(&hidp_session_sem);
|
||||
|
||||
if (conn) {
|
||||
l2cap_unregister_user(conn, &session->user);
|
||||
l2cap_conn_put(conn);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Start session synchronously
|
||||
* This starts a session thread and waits until initialization
|
||||
|
|
@ -1311,8 +1333,7 @@ static int hidp_session_thread(void *arg)
|
|||
* Instead, this call has the same semantics as if user-space tried to
|
||||
* delete the session.
|
||||
*/
|
||||
if (session->conn)
|
||||
l2cap_unregister_user(session->conn, &session->user);
|
||||
hidp_session_unregister_conn(session);
|
||||
|
||||
hidp_session_put(session);
|
||||
|
||||
|
|
@ -1418,7 +1439,7 @@ int hidp_connection_del(struct hidp_conndel_req *req)
|
|||
HIDP_CTRL_VIRTUAL_CABLE_UNPLUG,
|
||||
NULL, 0);
|
||||
else
|
||||
l2cap_unregister_user(session->conn, &session->user);
|
||||
hidp_session_unregister_conn(session);
|
||||
|
||||
hidp_session_put(session);
|
||||
|
||||
|
|
|
|||
|
|
@ -347,6 +347,7 @@ static int iso_connect_bis(struct sock *sk)
|
|||
return -EHOSTUNREACH;
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
lock_sock(sk);
|
||||
|
||||
if (!bis_capable(hdev)) {
|
||||
err = -EOPNOTSUPP;
|
||||
|
|
@ -399,13 +400,9 @@ static int iso_connect_bis(struct sock *sk)
|
|||
goto unlock;
|
||||
}
|
||||
|
||||
lock_sock(sk);
|
||||
|
||||
err = iso_chan_add(conn, sk, NULL);
|
||||
if (err) {
|
||||
release_sock(sk);
|
||||
if (err)
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
/* Update source addr of the socket */
|
||||
bacpy(&iso_pi(sk)->src, &hcon->src);
|
||||
|
|
@ -421,9 +418,8 @@ static int iso_connect_bis(struct sock *sk)
|
|||
iso_sock_set_timer(sk, READ_ONCE(sk->sk_sndtimeo));
|
||||
}
|
||||
|
||||
release_sock(sk);
|
||||
|
||||
unlock:
|
||||
release_sock(sk);
|
||||
hci_dev_unlock(hdev);
|
||||
hci_dev_put(hdev);
|
||||
return err;
|
||||
|
|
@ -444,6 +440,7 @@ static int iso_connect_cis(struct sock *sk)
|
|||
return -EHOSTUNREACH;
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
lock_sock(sk);
|
||||
|
||||
if (!cis_central_capable(hdev)) {
|
||||
err = -EOPNOTSUPP;
|
||||
|
|
@ -498,13 +495,9 @@ static int iso_connect_cis(struct sock *sk)
|
|||
goto unlock;
|
||||
}
|
||||
|
||||
lock_sock(sk);
|
||||
|
||||
err = iso_chan_add(conn, sk, NULL);
|
||||
if (err) {
|
||||
release_sock(sk);
|
||||
if (err)
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
/* Update source addr of the socket */
|
||||
bacpy(&iso_pi(sk)->src, &hcon->src);
|
||||
|
|
@ -520,9 +513,8 @@ static int iso_connect_cis(struct sock *sk)
|
|||
iso_sock_set_timer(sk, READ_ONCE(sk->sk_sndtimeo));
|
||||
}
|
||||
|
||||
release_sock(sk);
|
||||
|
||||
unlock:
|
||||
release_sock(sk);
|
||||
hci_dev_unlock(hdev);
|
||||
hci_dev_put(hdev);
|
||||
return err;
|
||||
|
|
@ -1193,7 +1185,7 @@ static int iso_sock_connect(struct socket *sock, struct sockaddr_unsized *addr,
|
|||
|
||||
release_sock(sk);
|
||||
|
||||
if (bacmp(&iso_pi(sk)->dst, BDADDR_ANY))
|
||||
if (bacmp(&sa->iso_bdaddr, BDADDR_ANY))
|
||||
err = iso_connect_cis(sk);
|
||||
else
|
||||
err = iso_connect_bis(sk);
|
||||
|
|
@ -2256,8 +2248,10 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
|
|||
sk = iso_get_sock(hdev, &hdev->bdaddr, bdaddr, BT_LISTEN,
|
||||
iso_match_sid, ev1);
|
||||
if (sk && !ev1->status) {
|
||||
lock_sock(sk);
|
||||
iso_pi(sk)->sync_handle = le16_to_cpu(ev1->handle);
|
||||
iso_pi(sk)->bc_sid = ev1->sid;
|
||||
release_sock(sk);
|
||||
}
|
||||
|
||||
goto done;
|
||||
|
|
@ -2268,8 +2262,10 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
|
|||
sk = iso_get_sock(hdev, &hdev->bdaddr, bdaddr, BT_LISTEN,
|
||||
iso_match_sid_past, ev1a);
|
||||
if (sk && !ev1a->status) {
|
||||
lock_sock(sk);
|
||||
iso_pi(sk)->sync_handle = le16_to_cpu(ev1a->sync_handle);
|
||||
iso_pi(sk)->bc_sid = ev1a->sid;
|
||||
release_sock(sk);
|
||||
}
|
||||
|
||||
goto done;
|
||||
|
|
@ -2296,27 +2292,35 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
|
|||
ev2);
|
||||
|
||||
if (sk) {
|
||||
int err;
|
||||
struct hci_conn *hcon = iso_pi(sk)->conn->hcon;
|
||||
int err = 0;
|
||||
bool big_sync;
|
||||
struct hci_conn *hcon;
|
||||
|
||||
lock_sock(sk);
|
||||
|
||||
hcon = iso_pi(sk)->conn->hcon;
|
||||
iso_pi(sk)->qos.bcast.encryption = ev2->encryption;
|
||||
|
||||
if (ev2->num_bis < iso_pi(sk)->bc_num_bis)
|
||||
iso_pi(sk)->bc_num_bis = ev2->num_bis;
|
||||
|
||||
if (!test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags) &&
|
||||
!test_and_set_bit(BT_SK_BIG_SYNC, &iso_pi(sk)->flags)) {
|
||||
big_sync = !test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags) &&
|
||||
!test_and_set_bit(BT_SK_BIG_SYNC, &iso_pi(sk)->flags);
|
||||
|
||||
if (big_sync)
|
||||
err = hci_conn_big_create_sync(hdev, hcon,
|
||||
&iso_pi(sk)->qos,
|
||||
iso_pi(sk)->sync_handle,
|
||||
iso_pi(sk)->bc_num_bis,
|
||||
iso_pi(sk)->bc_bis);
|
||||
if (err) {
|
||||
bt_dev_err(hdev, "hci_le_big_create_sync: %d",
|
||||
err);
|
||||
sock_put(sk);
|
||||
sk = NULL;
|
||||
}
|
||||
|
||||
release_sock(sk);
|
||||
|
||||
if (big_sync && err) {
|
||||
bt_dev_err(hdev, "hci_le_big_create_sync: %d",
|
||||
err);
|
||||
sock_put(sk);
|
||||
sk = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2370,8 +2374,10 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
|
|||
if (!base || base_len > BASE_MAX_LENGTH)
|
||||
goto done;
|
||||
|
||||
lock_sock(sk);
|
||||
memcpy(iso_pi(sk)->base, base, base_len);
|
||||
iso_pi(sk)->base_len = base_len;
|
||||
release_sock(sk);
|
||||
} else {
|
||||
/* This is a PA data fragment. Keep pa_data_len set to 0
|
||||
* until all data has been reassembled.
|
||||
|
|
|
|||
|
|
@ -4706,16 +4706,8 @@ static inline int l2cap_conn_param_update_req(struct l2cap_conn *conn,
|
|||
l2cap_send_cmd(conn, cmd->ident, L2CAP_CONN_PARAM_UPDATE_RSP,
|
||||
sizeof(rsp), &rsp);
|
||||
|
||||
if (!err) {
|
||||
u8 store_hint;
|
||||
|
||||
store_hint = hci_le_conn_update(hcon, min, max, latency,
|
||||
to_multiplier);
|
||||
mgmt_new_conn_param(hcon->hdev, &hcon->dst, hcon->dst_type,
|
||||
store_hint, min, max, latency,
|
||||
to_multiplier);
|
||||
|
||||
}
|
||||
if (!err)
|
||||
hci_le_conn_update(hcon, min, max, latency, to_multiplier);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -5428,7 +5420,7 @@ static inline int l2cap_ecred_reconf_req(struct l2cap_conn *conn,
|
|||
* configured, the MPS field may be less than the current MPS
|
||||
* of that channel.
|
||||
*/
|
||||
if (chan[i]->remote_mps >= mps && i) {
|
||||
if (chan[i]->remote_mps > mps && num_scid > 1) {
|
||||
BT_ERR("chan %p decreased MPS %u -> %u", chan[i],
|
||||
chan[i]->remote_mps, mps);
|
||||
result = L2CAP_RECONF_INVALID_MPS;
|
||||
|
|
|
|||
|
|
@ -1498,6 +1498,9 @@ static struct l2cap_chan *l2cap_sock_new_connection_cb(struct l2cap_chan *chan)
|
|||
{
|
||||
struct sock *sk, *parent = chan->data;
|
||||
|
||||
if (!parent)
|
||||
return NULL;
|
||||
|
||||
lock_sock(parent);
|
||||
|
||||
/* Check for backlog size */
|
||||
|
|
@ -1657,6 +1660,9 @@ static void l2cap_sock_state_change_cb(struct l2cap_chan *chan, int state,
|
|||
{
|
||||
struct sock *sk = chan->data;
|
||||
|
||||
if (!sk)
|
||||
return;
|
||||
|
||||
sk->sk_state = state;
|
||||
|
||||
if (err)
|
||||
|
|
@ -1758,6 +1764,9 @@ static long l2cap_sock_get_sndtimeo_cb(struct l2cap_chan *chan)
|
|||
{
|
||||
struct sock *sk = chan->data;
|
||||
|
||||
if (!sk)
|
||||
return 0;
|
||||
|
||||
return READ_ONCE(sk->sk_sndtimeo);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1715,9 +1715,12 @@ static int rfcomm_recv_data(struct rfcomm_session *s, u8 dlci, int pf, struct sk
|
|||
}
|
||||
|
||||
if (pf && d->cfc) {
|
||||
u8 credits = *(u8 *) skb->data; skb_pull(skb, 1);
|
||||
u8 *credits = skb_pull_data(skb, 1);
|
||||
|
||||
d->tx_credits += credits;
|
||||
if (!credits)
|
||||
goto drop;
|
||||
|
||||
d->tx_credits += *credits;
|
||||
if (d->tx_credits)
|
||||
clear_bit(RFCOMM_TX_THROTTLED, &d->flags);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -472,9 +472,13 @@ static struct sock *sco_get_sock_listen(bdaddr_t *src)
|
|||
sk1 = sk;
|
||||
}
|
||||
|
||||
sk = sk ? sk : sk1;
|
||||
if (sk)
|
||||
sock_hold(sk);
|
||||
|
||||
read_unlock(&sco_sk_list.lock);
|
||||
|
||||
return sk ? sk : sk1;
|
||||
return sk;
|
||||
}
|
||||
|
||||
static void sco_sock_destruct(struct sock *sk)
|
||||
|
|
@ -515,11 +519,13 @@ static void sco_sock_kill(struct sock *sk)
|
|||
BT_DBG("sk %p state %d", sk, sk->sk_state);
|
||||
|
||||
/* Sock is dead, so set conn->sk to NULL to avoid possible UAF */
|
||||
lock_sock(sk);
|
||||
if (sco_pi(sk)->conn) {
|
||||
sco_conn_lock(sco_pi(sk)->conn);
|
||||
sco_pi(sk)->conn->sk = NULL;
|
||||
sco_conn_unlock(sco_pi(sk)->conn);
|
||||
}
|
||||
release_sock(sk);
|
||||
|
||||
/* Kill poor orphan */
|
||||
bt_sock_unlink(&sco_sk_list, sk);
|
||||
|
|
@ -1365,40 +1371,51 @@ static int sco_sock_release(struct socket *sock)
|
|||
|
||||
static void sco_conn_ready(struct sco_conn *conn)
|
||||
{
|
||||
struct sock *parent;
|
||||
struct sock *sk = conn->sk;
|
||||
struct sock *parent, *sk;
|
||||
|
||||
sco_conn_lock(conn);
|
||||
sk = sco_sock_hold(conn);
|
||||
sco_conn_unlock(conn);
|
||||
|
||||
BT_DBG("conn %p", conn);
|
||||
|
||||
if (sk) {
|
||||
lock_sock(sk);
|
||||
sco_sock_clear_timer(sk);
|
||||
sk->sk_state = BT_CONNECTED;
|
||||
sk->sk_state_change(sk);
|
||||
release_sock(sk);
|
||||
} else {
|
||||
sco_conn_lock(conn);
|
||||
|
||||
if (!conn->hcon) {
|
||||
sco_conn_unlock(conn);
|
||||
return;
|
||||
/* conn->sk may have become NULL if racing with sk close, but
|
||||
* due to held hdev->lock, it can't become different sk.
|
||||
*/
|
||||
if (conn->sk) {
|
||||
sco_sock_clear_timer(sk);
|
||||
sk->sk_state = BT_CONNECTED;
|
||||
sk->sk_state_change(sk);
|
||||
}
|
||||
|
||||
release_sock(sk);
|
||||
sock_put(sk);
|
||||
} else {
|
||||
if (!conn->hcon)
|
||||
return;
|
||||
|
||||
lockdep_assert_held(&conn->hcon->hdev->lock);
|
||||
|
||||
parent = sco_get_sock_listen(&conn->hcon->src);
|
||||
if (!parent) {
|
||||
sco_conn_unlock(conn);
|
||||
if (!parent)
|
||||
return;
|
||||
}
|
||||
|
||||
lock_sock(parent);
|
||||
|
||||
sco_conn_lock(conn);
|
||||
|
||||
/* hdev->lock guarantees conn->sk == NULL still here */
|
||||
|
||||
if (parent->sk_state != BT_LISTEN)
|
||||
goto release;
|
||||
|
||||
sk = sco_sock_alloc(sock_net(parent), NULL,
|
||||
BTPROTO_SCO, GFP_ATOMIC, 0);
|
||||
if (!sk) {
|
||||
release_sock(parent);
|
||||
sco_conn_unlock(conn);
|
||||
return;
|
||||
}
|
||||
if (!sk)
|
||||
goto release;
|
||||
|
||||
sco_sock_init(sk, parent);
|
||||
|
||||
|
|
@ -1417,9 +1434,10 @@ static void sco_conn_ready(struct sco_conn *conn)
|
|||
/* Wake up parent */
|
||||
parent->sk_data_ready(parent);
|
||||
|
||||
release_sock(parent);
|
||||
|
||||
release:
|
||||
sco_conn_unlock(conn);
|
||||
release_sock(parent);
|
||||
sock_put(parent);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user