mirror of
https://github.com/torvalds/linux.git
synced 2026-09-22 12:44:03 +02:00
bluetooth pull request for net:
- hci_sync: Fix advertising data UAFs - hci_conn: hold conn reference fixes - L2CAP: fix UAF in l2cap_le_connect_rsp - RFCOMM: validate skb length in rfcomm_recv_frame - ISO: Locking fixes - SCO: give the socket its own sco_conn reference - MGMT: fix UAF in pair command cancellation - MGMT: fix pending command UAF in EIR updates - HIDP: reject frames without a transaction header - HIDP: validate numbered report payloads - btmtk: Fix short read errors in btmtk_usb_uhw_reg_read() - btmtk: Fix short read errors in btmtk_usb_reg_read() - btusb: Fix short read errors in btusb_qca_send_vendor_req() - btintel: Validate length before parsing diagnostics TLV -----BEGIN PGP SIGNATURE----- iQJNBAABCgA3FiEE7E6oRXp8w05ovYr/9JCA4xAyCykFAmppDZ8ZHGx1aXoudm9u LmRlbnR6QGludGVsLmNvbQAKCRD0kIDjEDILKS7GD/9DHDHg9Jv+uJGHTBkEG1A0 ChNTMdMlo5k9/yuhXzChkJHzn6NeACZHRGo7gKle32/0iA2COUJuKsGOoYs4dMhS NFfTVlSWuiU/HFOREY6GLI5o6/kssjfS7gnLz0QsVdCLULZLI68VUcwaE1M2sCbU d6g7C+xKr989ZnnvXyhu8lYAPO+kgW8r6SETu3sTOpTQcyLcCycGAKoI8paZrVYm o6V3YtNPUl0hk1wZ4HsJUfPpOKPZgK0mXVmUXwkTqbI7ugoDGnnge0qSIx2fy15X QEaq9a+tTbxQXEycYN7yuXY1KynCqiePyRujgS/15Nzu+8jtXShkujBUo+FvzRf/ rXi14ht74z5dkPXH1AHHJ5pexSoSqhz/WFBRYYw6bH4vOQQJSMcW2PXHBBnSd3tp wbQzZy5BbAOmE0UyoZAe+yhfDnHbcCU47NZlL61S+5EDuczGl6qrVk5SpV7RB+EC AxgJfWp9fty0sTNw9vmuD7x17uzTv7cAXGDalP1/WgONWLnCFdOUv6pC/2kqwQfQ cgleMM6oVLKea1aGweLcl5WYpJ396sntxbpNUL2u8XDZ9kUKemqwjDYs878hjoKT +xkqFmNtVJTA8PqAm2+uTLskPJPeLKR2TbCDvWikoB8iIWdJ8k7D0ih2nc2ITuyL /dCkv+LVKZ1qyCQCt1XP5w== =3LzD -----END PGP SIGNATURE----- Merge tag 'for-net-2026-07-28' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth Luiz Augusto von Dentz says: ==================== bluetooth pull request for net: - hci_sync: Fix advertising data UAFs - hci_conn: hold conn reference fixes - L2CAP: fix UAF in l2cap_le_connect_rsp - RFCOMM: validate skb length in rfcomm_recv_frame - ISO: Locking fixes - SCO: give the socket its own sco_conn reference - MGMT: fix UAF in pair command cancellation - MGMT: fix pending command UAF in EIR updates - HIDP: reject frames without a transaction header - HIDP: validate numbered report payloads - btmtk: Fix short read errors in btmtk_usb_uhw_reg_read() - btmtk: Fix short read errors in btmtk_usb_reg_read() - btusb: Fix short read errors in btusb_qca_send_vendor_req() - btintel: Validate length before parsing diagnostics TLV * tag 'for-net-2026-07-28' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth: (31 commits) Bluetooth: SCO: give the socket its own sco_conn reference Bluetooth: btusb: Fix short read errors in btusb_qca_send_vendor_req() Bluetooth: btmtk: Fix short read errors in btmtk_usb_reg_read() Bluetooth: btmtk: Fix short read errors in btmtk_usb_uhw_reg_read() Bluetooth: hci_sync: remove unnecessary hci_conn_get in create_conn_sync Bluetooth: hci_sync: fix hci_conn_del() use in hci_le_create_conn_sync Bluetooth: hci_sync: hold conn in hci_past_sync() callback Bluetooth: hci_sync: hold conn in hci_connect_pa_sync() callback Bluetooth: hci_sync: hold conn in hci_connect_big_sync() callback Bluetooth: hci_sync: hold conn in hci_connect_acl/le_sync() callbacks Bluetooth: hci_conn: hold conn reference in abort_conn_sync() Bluetooth: btintel: Validate length before parsing diagnostics TLV Bluetooth: ISO: fix race of kfree vs kref_get_unless_zero Bluetooth: ISO: fix refcounting of iso_conn Bluetooth: ISO: ensure no dangling hcon references in iso_conn Bluetooth: ISO: avoid deadlocks in iso_sock_timeout Bluetooth: ISO: fix leaking sk after socket release Bluetooth: ISO: hold sk properly in iso_conn_ready Bluetooth: ISO: validate sockaddr_iso first in iso_sock_rebind_bis() Bluetooth: ISO: fix timeout vs sync_timeout typo in check_bcast_qos ... ==================== Link: https://patch.msgid.link/20260728201527.2456032-1-luiz.dentz@gmail.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
commit
0408e7f6f6
|
|
@ -3771,6 +3771,9 @@ static int btintel_diagnostics(struct hci_dev *hdev, struct sk_buff *skb)
|
|||
{
|
||||
struct intel_tlv *tlv = (void *)&skb->data[5];
|
||||
|
||||
if (skb->len < 5 + sizeof(*tlv) + sizeof(tlv->val[0]))
|
||||
goto recv_frame;
|
||||
|
||||
/* The first event is always an event type TLV */
|
||||
if (tlv->type != INTEL_TLV_TYPE_ID)
|
||||
goto recv_frame;
|
||||
|
|
|
|||
|
|
@ -804,56 +804,44 @@ static int btmtk_usb_uhw_reg_write(struct hci_dev *hdev, u32 reg, u32 val)
|
|||
static int btmtk_usb_uhw_reg_read(struct hci_dev *hdev, u32 reg, u32 *val)
|
||||
{
|
||||
struct btmtk_data *data = hci_get_priv(hdev);
|
||||
int pipe, err;
|
||||
void *buf;
|
||||
u8 buf[sizeof(u32)];
|
||||
int err;
|
||||
|
||||
buf = kzalloc(4, GFP_KERNEL);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
pipe = usb_rcvctrlpipe(data->udev, 0);
|
||||
err = usb_control_msg(data->udev, pipe, 0x01,
|
||||
0xDE,
|
||||
reg >> 16, reg & 0xffff,
|
||||
buf, 4, USB_CTRL_GET_TIMEOUT);
|
||||
if (err < 0) {
|
||||
*val = 0;
|
||||
err = usb_control_msg_recv(data->udev, 0, 0x01,
|
||||
0xDE,
|
||||
reg >> 16, reg & 0xffff,
|
||||
buf, sizeof(buf), USB_CTRL_GET_TIMEOUT,
|
||||
GFP_KERNEL);
|
||||
if (err) {
|
||||
bt_dev_err(hdev, "Failed to read uhw reg(%d)", err);
|
||||
goto err_free_buf;
|
||||
return err;
|
||||
}
|
||||
|
||||
*val = get_unaligned_le32(buf);
|
||||
bt_dev_dbg(hdev, "reg=%x, value=0x%08x", reg, *val);
|
||||
|
||||
err_free_buf:
|
||||
kfree(buf);
|
||||
|
||||
return err;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int btmtk_usb_reg_read(struct hci_dev *hdev, u32 reg, u32 *val)
|
||||
{
|
||||
struct btmtk_data *data = hci_get_priv(hdev);
|
||||
int pipe, err, size = sizeof(u32);
|
||||
void *buf;
|
||||
u8 buf[sizeof(u32)];
|
||||
int err;
|
||||
|
||||
buf = kzalloc(size, GFP_KERNEL);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
pipe = usb_rcvctrlpipe(data->udev, 0);
|
||||
err = usb_control_msg(data->udev, pipe, 0x63,
|
||||
USB_TYPE_VENDOR | USB_DIR_IN,
|
||||
reg >> 16, reg & 0xffff,
|
||||
buf, size, USB_CTRL_GET_TIMEOUT);
|
||||
*val = 0;
|
||||
err = usb_control_msg_recv(data->udev, 0, 0x63,
|
||||
USB_TYPE_VENDOR | USB_DIR_IN,
|
||||
reg >> 16, reg & 0xffff,
|
||||
buf, sizeof(buf), USB_CTRL_GET_TIMEOUT,
|
||||
GFP_KERNEL);
|
||||
if (err < 0)
|
||||
goto err_free_buf;
|
||||
return err;
|
||||
|
||||
*val = get_unaligned_le32(buf);
|
||||
|
||||
err_free_buf:
|
||||
kfree(buf);
|
||||
|
||||
return err;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int btmtk_usb_id_get(struct hci_dev *hdev, u32 reg, u32 *id)
|
||||
|
|
@ -877,7 +865,7 @@ int btmtk_usb_subsys_reset(struct hci_dev *hdev, u32 dev_id)
|
|||
|
||||
if (dev_id == 0x7922) {
|
||||
err = btmtk_usb_uhw_reg_read(hdev, MTK_BT_SUBSYS_RST, &val);
|
||||
if (err < 0)
|
||||
if (err)
|
||||
return err;
|
||||
val |= 0x00002020;
|
||||
err = btmtk_usb_uhw_reg_write(hdev, MTK_BT_SUBSYS_RST, val);
|
||||
|
|
@ -887,7 +875,7 @@ int btmtk_usb_subsys_reset(struct hci_dev *hdev, u32 dev_id)
|
|||
if (err < 0)
|
||||
return err;
|
||||
err = btmtk_usb_uhw_reg_read(hdev, MTK_BT_SUBSYS_RST, &val);
|
||||
if (err < 0)
|
||||
if (err)
|
||||
return err;
|
||||
val |= BIT(0);
|
||||
err = btmtk_usb_uhw_reg_write(hdev, MTK_BT_SUBSYS_RST, val);
|
||||
|
|
@ -896,14 +884,14 @@ int btmtk_usb_subsys_reset(struct hci_dev *hdev, u32 dev_id)
|
|||
msleep(100);
|
||||
} else if (dev_id == 0x7925 || dev_id == 0x6639) {
|
||||
err = btmtk_usb_uhw_reg_read(hdev, MTK_BT_RESET_REG_CONNV3, &val);
|
||||
if (err < 0)
|
||||
if (err)
|
||||
return err;
|
||||
val |= (1 << 5);
|
||||
err = btmtk_usb_uhw_reg_write(hdev, MTK_BT_RESET_REG_CONNV3, val);
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = btmtk_usb_uhw_reg_read(hdev, MTK_BT_RESET_REG_CONNV3, &val);
|
||||
if (err < 0)
|
||||
if (err)
|
||||
return err;
|
||||
val &= 0xFFFF00FF;
|
||||
val |= (1 << 13);
|
||||
|
|
@ -914,7 +902,7 @@ int btmtk_usb_subsys_reset(struct hci_dev *hdev, u32 dev_id)
|
|||
if (err < 0)
|
||||
return err;
|
||||
err = btmtk_usb_uhw_reg_read(hdev, MTK_BT_RESET_REG_CONNV3, &val);
|
||||
if (err < 0)
|
||||
if (err)
|
||||
return err;
|
||||
val |= (1 << 0);
|
||||
err = btmtk_usb_uhw_reg_write(hdev, MTK_BT_RESET_REG_CONNV3, val);
|
||||
|
|
@ -924,13 +912,13 @@ int btmtk_usb_subsys_reset(struct hci_dev *hdev, u32 dev_id)
|
|||
if (err < 0)
|
||||
return err;
|
||||
err = btmtk_usb_uhw_reg_read(hdev, MTK_UDMA_INT_STA_BT, &val);
|
||||
if (err < 0)
|
||||
if (err)
|
||||
return err;
|
||||
err = btmtk_usb_uhw_reg_write(hdev, MTK_UDMA_INT_STA_BT1, 0x000000FF);
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = btmtk_usb_uhw_reg_read(hdev, MTK_UDMA_INT_STA_BT1, &val);
|
||||
if (err < 0)
|
||||
if (err)
|
||||
return err;
|
||||
msleep(100);
|
||||
} else {
|
||||
|
|
@ -940,7 +928,7 @@ int btmtk_usb_subsys_reset(struct hci_dev *hdev, u32 dev_id)
|
|||
if (err < 0)
|
||||
return err;
|
||||
err = btmtk_usb_uhw_reg_read(hdev, MTK_BT_WDT_STATUS, &val);
|
||||
if (err < 0)
|
||||
if (err)
|
||||
return err;
|
||||
/* Reset the bluetooth chip via USB interface. */
|
||||
err = btmtk_usb_uhw_reg_write(hdev, MTK_BT_SUBSYS_RST, 1);
|
||||
|
|
@ -950,13 +938,13 @@ int btmtk_usb_subsys_reset(struct hci_dev *hdev, u32 dev_id)
|
|||
if (err < 0)
|
||||
return err;
|
||||
err = btmtk_usb_uhw_reg_read(hdev, MTK_UDMA_INT_STA_BT, &val);
|
||||
if (err < 0)
|
||||
if (err)
|
||||
return err;
|
||||
err = btmtk_usb_uhw_reg_write(hdev, MTK_UDMA_INT_STA_BT1, 0x000000FF);
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = btmtk_usb_uhw_reg_read(hdev, MTK_UDMA_INT_STA_BT1, &val);
|
||||
if (err < 0)
|
||||
if (err)
|
||||
return err;
|
||||
/* MT7921 need to delay 20ms between toggle reset bit */
|
||||
msleep(20);
|
||||
|
|
@ -964,7 +952,7 @@ int btmtk_usb_subsys_reset(struct hci_dev *hdev, u32 dev_id)
|
|||
if (err < 0)
|
||||
return err;
|
||||
err = btmtk_usb_uhw_reg_read(hdev, MTK_BT_SUBSYS_RST, &val);
|
||||
if (err < 0)
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
|
|
@ -980,7 +968,7 @@ int btmtk_usb_subsys_reset(struct hci_dev *hdev, u32 dev_id)
|
|||
}
|
||||
|
||||
err = btmtk_usb_id_get(hdev, 0x70010200, &val);
|
||||
if (err < 0 || (!val && dev_id != 0x6639))
|
||||
if (err || (!val && dev_id != 0x6639))
|
||||
bt_dev_err(hdev, "Can't get device id, subsys reset fail.");
|
||||
|
||||
return err;
|
||||
|
|
@ -1324,24 +1312,24 @@ int btmtk_usb_setup(struct hci_dev *hdev)
|
|||
calltime = ktime_get();
|
||||
|
||||
err = btmtk_usb_id_get(hdev, 0x80000008, &dev_id);
|
||||
if (err < 0) {
|
||||
if (err) {
|
||||
bt_dev_err(hdev, "Failed to get device id (%d)", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
if (!dev_id || dev_id != 0x7663) {
|
||||
err = btmtk_usb_id_get(hdev, 0x70010200, &dev_id);
|
||||
if (err < 0) {
|
||||
if (err) {
|
||||
bt_dev_err(hdev, "Failed to get device id (%d)", err);
|
||||
return err;
|
||||
}
|
||||
err = btmtk_usb_id_get(hdev, 0x80021004, &fw_version);
|
||||
if (err < 0) {
|
||||
if (err) {
|
||||
bt_dev_err(hdev, "Failed to get fw version (%d)", err);
|
||||
return err;
|
||||
}
|
||||
err = btmtk_usb_id_get(hdev, 0x70010020, &fw_flavor);
|
||||
if (err < 0) {
|
||||
if (err) {
|
||||
bt_dev_err(hdev, "Failed to get fw flavor (%d)", err);
|
||||
return err;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3424,28 +3424,16 @@ static const char *qca_get_fw_subdirectory(const struct qca_version *ver)
|
|||
static int btusb_qca_send_vendor_req(struct usb_device *udev, u8 request,
|
||||
void *data, u16 size)
|
||||
{
|
||||
int pipe, err;
|
||||
u8 *buf;
|
||||
|
||||
buf = kmalloc(size, GFP_KERNEL);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
int err;
|
||||
|
||||
/* Found some of USB hosts have IOT issues with ours so that we should
|
||||
* not wait until HCI layer is ready.
|
||||
*/
|
||||
pipe = usb_rcvctrlpipe(udev, 0);
|
||||
err = usb_control_msg(udev, pipe, request, USB_TYPE_VENDOR | USB_DIR_IN,
|
||||
0, 0, buf, size, USB_CTRL_GET_TIMEOUT);
|
||||
if (err < 0) {
|
||||
err = usb_control_msg_recv(udev, 0, request, USB_TYPE_VENDOR | USB_DIR_IN,
|
||||
0, 0, data, size, USB_CTRL_GET_TIMEOUT,
|
||||
GFP_KERNEL);
|
||||
if (err)
|
||||
dev_err(&udev->dev, "Failed to access otp area (%d)", err);
|
||||
goto done;
|
||||
}
|
||||
|
||||
memcpy(data, buf, size);
|
||||
|
||||
done:
|
||||
kfree(buf);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
|
@ -3652,7 +3640,7 @@ static bool btusb_qca_need_patch(struct usb_device *udev)
|
|||
struct qca_version ver;
|
||||
|
||||
if (btusb_qca_send_vendor_req(udev, QCA_GET_TARGET_VERSION, &ver,
|
||||
sizeof(ver)) < 0)
|
||||
sizeof(ver)))
|
||||
return false;
|
||||
/* only low ROM versions need patches */
|
||||
return !(le32_to_cpu(ver.rom_version) & ~0xffffU);
|
||||
|
|
@ -3670,7 +3658,7 @@ static int btusb_setup_qca(struct hci_dev *hdev)
|
|||
|
||||
err = btusb_qca_send_vendor_req(udev, QCA_GET_TARGET_VERSION, &ver,
|
||||
sizeof(ver));
|
||||
if (err < 0)
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
ver_rom = le32_to_cpu(ver.rom_version);
|
||||
|
|
@ -3693,7 +3681,7 @@ static int btusb_setup_qca(struct hci_dev *hdev)
|
|||
|
||||
err = btusb_qca_send_vendor_req(udev, QCA_CHECK_STATUS, &status,
|
||||
sizeof(status));
|
||||
if (err < 0)
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (!(status & QCA_PATCH_UPDATED)) {
|
||||
|
|
@ -3704,7 +3692,7 @@ static int btusb_setup_qca(struct hci_dev *hdev)
|
|||
|
||||
err = btusb_qca_send_vendor_req(udev, QCA_GET_TARGET_VERSION, &ver,
|
||||
sizeof(ver));
|
||||
if (err < 0)
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
btdata->qca_dump.fw_version = le32_to_cpu(ver.patch_version);
|
||||
|
|
|
|||
|
|
@ -767,9 +767,11 @@ struct hci_conn {
|
|||
struct dentry *debugfs;
|
||||
|
||||
struct hci_dev *hdev;
|
||||
|
||||
spinlock_t proto_lock; /* lock guarding protocol data */
|
||||
void *l2cap_data;
|
||||
void *sco_data;
|
||||
void *iso_data;
|
||||
void *iso_data __guarded_by(&proto_lock);
|
||||
|
||||
struct list_head link_list;
|
||||
struct hci_conn *parent;
|
||||
|
|
|
|||
|
|
@ -1123,6 +1123,8 @@ static struct hci_conn *__hci_conn_add(struct hci_dev *hdev, int type,
|
|||
INIT_DELAYED_WORK(&conn->idle_work, hci_conn_idle);
|
||||
INIT_DELAYED_WORK(&conn->le_conn_timeout, le_conn_timeout);
|
||||
|
||||
spin_lock_init(&conn->proto_lock);
|
||||
|
||||
atomic_set(&conn->refcnt, 0);
|
||||
|
||||
hci_dev_hold(hdev);
|
||||
|
|
@ -3163,6 +3165,13 @@ static int abort_conn_sync(struct hci_dev *hdev, void *data)
|
|||
return hci_abort_conn_sync(hdev, conn, conn->abort_reason);
|
||||
}
|
||||
|
||||
static void abort_conn_destroy(struct hci_dev *hdev, void *data, int err)
|
||||
{
|
||||
struct hci_conn *conn = data;
|
||||
|
||||
hci_conn_put(conn);
|
||||
}
|
||||
|
||||
int hci_abort_conn(struct hci_conn *conn, u8 reason)
|
||||
{
|
||||
struct hci_dev *hdev = conn->hdev;
|
||||
|
|
@ -3188,7 +3197,10 @@ int hci_abort_conn(struct hci_conn *conn, u8 reason)
|
|||
* as a result to MGMT_OP_DISCONNECT/MGMT_OP_UNPAIR which does
|
||||
* already queue its callback on cmd_sync_work.
|
||||
*/
|
||||
err = hci_cmd_sync_run_once(hdev, abort_conn_sync, conn, NULL);
|
||||
err = hci_cmd_sync_run_once(hdev, abort_conn_sync, hci_conn_get(conn),
|
||||
abort_conn_destroy);
|
||||
if (err)
|
||||
hci_conn_put(conn);
|
||||
return (err == -EEXIST) ? 0 : err;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
@ -6678,11 +6741,6 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data)
|
|||
|
||||
bt_dev_dbg(hdev, "conn %p", conn);
|
||||
|
||||
/* Hold a reference so conn stays valid for the HCI_CONN_CREATE
|
||||
* clear_bit() at done.
|
||||
*/
|
||||
hci_conn_get(conn);
|
||||
|
||||
clear_bit(HCI_CONN_SCANNING, &conn->flags);
|
||||
conn->state = BT_CONNECT;
|
||||
|
||||
|
|
@ -6694,8 +6752,9 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data)
|
|||
if (hci_dev_test_flag(hdev, HCI_LE_SCAN) &&
|
||||
hdev->le_scan_type == LE_SCAN_ACTIVE &&
|
||||
!hci_dev_test_flag(hdev, HCI_LE_SIMULTANEOUS_ROLES)) {
|
||||
hci_conn_del(conn);
|
||||
hci_conn_put(conn);
|
||||
conn->state = BT_OPEN;
|
||||
hci_abort_conn_sync(hdev, conn,
|
||||
HCI_ERROR_REJ_LIMITED_RESOURCES);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
|
|
@ -6793,7 +6852,6 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data)
|
|||
|
||||
/* Re-enable advertising after the connection attempt is finished. */
|
||||
hci_resume_advertising_sync(hdev);
|
||||
hci_conn_put(conn);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
|
@ -7068,11 +7126,6 @@ static int hci_acl_create_conn_sync(struct hci_dev *hdev, void *data)
|
|||
else
|
||||
cp.role_switch = 0x00;
|
||||
|
||||
/* Hold a reference so conn stays valid for the HCI_CONN_CREATE
|
||||
* clear_bit() below.
|
||||
*/
|
||||
hci_conn_get(conn);
|
||||
|
||||
/* Mark create connection in flight so hci_cancel_connect_sync() can
|
||||
* cancel it while blocking on the connection complete event.
|
||||
*/
|
||||
|
|
@ -7084,17 +7137,27 @@ static int hci_acl_create_conn_sync(struct hci_dev *hdev, void *data)
|
|||
conn->conn_timeout, NULL);
|
||||
|
||||
clear_bit(HCI_CONN_CREATE, &conn->flags);
|
||||
hci_conn_put(conn);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void hci_acl_create_conn_sync_complete(struct hci_dev *hdev, void *data,
|
||||
int err)
|
||||
{
|
||||
struct hci_conn *conn = data;
|
||||
|
||||
hci_conn_put(conn);
|
||||
}
|
||||
|
||||
int hci_connect_acl_sync(struct hci_dev *hdev, struct hci_conn *conn)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = hci_cmd_sync_queue_once(hdev, hci_acl_create_conn_sync, conn,
|
||||
NULL);
|
||||
err = hci_cmd_sync_queue_once(hdev, hci_acl_create_conn_sync,
|
||||
hci_conn_get(conn),
|
||||
hci_acl_create_conn_sync_complete);
|
||||
if (err)
|
||||
hci_conn_put(conn);
|
||||
return (err == -EEXIST) ? 0 : err;
|
||||
}
|
||||
|
||||
|
|
@ -7105,36 +7168,41 @@ static void create_le_conn_complete(struct hci_dev *hdev, void *data, int err)
|
|||
bt_dev_dbg(hdev, "err %d", err);
|
||||
|
||||
if (err == -ECANCELED)
|
||||
return;
|
||||
goto done;
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
if (!hci_conn_valid(hdev, conn))
|
||||
goto done;
|
||||
goto unlock;
|
||||
|
||||
if (!err) {
|
||||
hci_connect_le_scan_cleanup(conn, 0x00);
|
||||
goto done;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
/* Check if connection is still pending */
|
||||
if (conn != hci_lookup_le_connect(hdev))
|
||||
goto done;
|
||||
goto unlock;
|
||||
|
||||
/* Flush to make sure we send create conn cancel command if needed */
|
||||
flush_delayed_work(&conn->le_conn_timeout);
|
||||
hci_conn_failed(conn, bt_status(err));
|
||||
|
||||
done:
|
||||
unlock:
|
||||
hci_dev_unlock(hdev);
|
||||
done:
|
||||
hci_conn_put(conn);
|
||||
}
|
||||
|
||||
int hci_connect_le_sync(struct hci_dev *hdev, struct hci_conn *conn)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = hci_cmd_sync_queue_once(hdev, hci_le_create_conn_sync, conn,
|
||||
err = hci_cmd_sync_queue_once(hdev, hci_le_create_conn_sync,
|
||||
hci_conn_get(conn),
|
||||
create_le_conn_complete);
|
||||
if (err)
|
||||
hci_conn_put(conn);
|
||||
return (err == -EEXIST) ? 0 : err;
|
||||
}
|
||||
|
||||
|
|
@ -7257,7 +7325,7 @@ static void create_pa_complete(struct hci_dev *hdev, void *data, int err)
|
|||
bt_dev_dbg(hdev, "err %d", err);
|
||||
|
||||
if (err == -ECANCELED)
|
||||
return;
|
||||
goto done;
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
|
|
@ -7281,6 +7349,8 @@ static void create_pa_complete(struct hci_dev *hdev, void *data, int err)
|
|||
|
||||
unlock:
|
||||
hci_dev_unlock(hdev);
|
||||
done:
|
||||
hci_conn_put(conn);
|
||||
}
|
||||
|
||||
static int hci_le_past_params_sync(struct hci_dev *hdev, struct hci_conn *conn,
|
||||
|
|
@ -7431,8 +7501,11 @@ int hci_connect_pa_sync(struct hci_dev *hdev, struct hci_conn *conn)
|
|||
{
|
||||
int err;
|
||||
|
||||
err = hci_cmd_sync_queue_once(hdev, hci_le_pa_create_sync, conn,
|
||||
err = hci_cmd_sync_queue_once(hdev, hci_le_pa_create_sync,
|
||||
hci_conn_get(conn),
|
||||
create_pa_complete);
|
||||
if (err)
|
||||
hci_conn_put(conn);
|
||||
return (err == -EEXIST) ? 0 : err;
|
||||
}
|
||||
|
||||
|
|
@ -7443,10 +7516,12 @@ static void create_big_complete(struct hci_dev *hdev, void *data, int err)
|
|||
bt_dev_dbg(hdev, "err %d", err);
|
||||
|
||||
if (err == -ECANCELED)
|
||||
return;
|
||||
goto done;
|
||||
|
||||
if (hci_conn_valid(hdev, conn))
|
||||
clear_bit(HCI_CONN_CREATE_BIG_SYNC, &conn->flags);
|
||||
clear_bit(HCI_CONN_CREATE_BIG_SYNC, &conn->flags);
|
||||
|
||||
done:
|
||||
hci_conn_put(conn);
|
||||
}
|
||||
|
||||
static int hci_le_big_create_sync(struct hci_dev *hdev, void *data)
|
||||
|
|
@ -7498,8 +7573,14 @@ int hci_connect_big_sync(struct hci_dev *hdev, struct hci_conn *conn)
|
|||
{
|
||||
int err;
|
||||
|
||||
err = hci_cmd_sync_queue_once(hdev, hci_le_big_create_sync, conn,
|
||||
if (!conn)
|
||||
return 0;
|
||||
|
||||
err = hci_cmd_sync_queue_once(hdev, hci_le_big_create_sync,
|
||||
hci_conn_get(conn),
|
||||
create_big_complete);
|
||||
if (err)
|
||||
hci_conn_put(conn);
|
||||
return (err == -EEXIST) ? 0 : err;
|
||||
}
|
||||
|
||||
|
|
@ -7514,6 +7595,8 @@ static void past_complete(struct hci_dev *hdev, void *data, int err)
|
|||
|
||||
bt_dev_dbg(hdev, "err %d", err);
|
||||
|
||||
hci_conn_put(past->conn);
|
||||
hci_conn_put(past->le);
|
||||
kfree(past);
|
||||
}
|
||||
|
||||
|
|
@ -7578,8 +7661,8 @@ int hci_past_sync(struct hci_conn *conn, struct hci_conn *le)
|
|||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
data->conn = conn;
|
||||
data->le = le;
|
||||
data->conn = hci_conn_get(conn);
|
||||
data->le = hci_conn_get(le);
|
||||
|
||||
if (conn->role == HCI_ROLE_MASTER)
|
||||
err = hci_cmd_sync_queue_once(conn->hdev,
|
||||
|
|
@ -7589,8 +7672,11 @@ int hci_past_sync(struct hci_conn *conn, struct hci_conn *le)
|
|||
err = hci_cmd_sync_queue_once(conn->hdev, hci_le_past_sync,
|
||||
data, past_complete);
|
||||
|
||||
if (err)
|
||||
if (err) {
|
||||
hci_conn_put(data->conn);
|
||||
hci_conn_put(data->le);
|
||||
kfree(data);
|
||||
}
|
||||
|
||||
return (err == -EEXIST) ? 0 : err;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -543,9 +543,10 @@ static int hidp_process_data(struct hidp_session *session, struct sk_buff *skb,
|
|||
}
|
||||
|
||||
if (test_bit(HIDP_WAITING_FOR_RETURN, &session->flags) &&
|
||||
param == session->waiting_report_type) {
|
||||
param == session->waiting_report_type) {
|
||||
if (session->waiting_report_number < 0 ||
|
||||
session->waiting_report_number == skb->data[0]) {
|
||||
(skb->len &&
|
||||
session->waiting_report_number == skb->data[0])) {
|
||||
/* hidp_get_raw_report() is waiting on this report. */
|
||||
session->report_return = skb;
|
||||
done_with_skb = 0;
|
||||
|
|
@ -560,16 +561,18 @@ static int hidp_process_data(struct hidp_session *session, struct sk_buff *skb,
|
|||
static void hidp_recv_ctrl_frame(struct hidp_session *session,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
unsigned char hdr, type, param;
|
||||
unsigned char type, param;
|
||||
u8 *hdr;
|
||||
int free_skb = 1;
|
||||
|
||||
BT_DBG("session %p skb %p len %u", session, skb, skb->len);
|
||||
|
||||
hdr = skb->data[0];
|
||||
skb_pull(skb, 1);
|
||||
hdr = skb_pull_data(skb, 1);
|
||||
if (!hdr)
|
||||
goto free;
|
||||
|
||||
type = hdr & HIDP_HEADER_TRANS_MASK;
|
||||
param = hdr & HIDP_HEADER_PARAM_MASK;
|
||||
type = *hdr & HIDP_HEADER_TRANS_MASK;
|
||||
param = *hdr & HIDP_HEADER_PARAM_MASK;
|
||||
|
||||
switch (type) {
|
||||
case HIDP_TRANS_HANDSHAKE:
|
||||
|
|
@ -590,6 +593,7 @@ static void hidp_recv_ctrl_frame(struct hidp_session *session,
|
|||
break;
|
||||
}
|
||||
|
||||
free:
|
||||
if (free_skb)
|
||||
kfree_skb(skb);
|
||||
}
|
||||
|
|
@ -597,14 +601,15 @@ static void hidp_recv_ctrl_frame(struct hidp_session *session,
|
|||
static void hidp_recv_intr_frame(struct hidp_session *session,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
unsigned char hdr;
|
||||
u8 *hdr;
|
||||
|
||||
BT_DBG("session %p skb %p len %u", session, skb, skb->len);
|
||||
|
||||
hdr = skb->data[0];
|
||||
skb_pull(skb, 1);
|
||||
hdr = skb_pull_data(skb, 1);
|
||||
if (!hdr)
|
||||
goto free;
|
||||
|
||||
if (hdr == (HIDP_TRANS_DATA | HIDP_DATA_RTYPE_INPUT)) {
|
||||
if (*hdr == (HIDP_TRANS_DATA | HIDP_DATA_RTYPE_INPUT)) {
|
||||
hidp_set_timer(session);
|
||||
|
||||
if (session->input)
|
||||
|
|
@ -616,9 +621,10 @@ static void hidp_recv_intr_frame(struct hidp_session *session,
|
|||
BT_DBG("report len %d", skb->len);
|
||||
}
|
||||
} else {
|
||||
BT_DBG("Unsupported protocol header 0x%02x", hdr);
|
||||
BT_DBG("Unsupported protocol header 0x%02x", *hdr);
|
||||
}
|
||||
|
||||
free:
|
||||
kfree_skb(skb);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -24,15 +24,19 @@ static struct bt_sock_list iso_sk_list = {
|
|||
};
|
||||
|
||||
/* ---- ISO connections ---- */
|
||||
enum {
|
||||
ISO_CONN_DROPPED,
|
||||
__ISO_CONN_NUM_FLAGS
|
||||
};
|
||||
|
||||
struct iso_conn {
|
||||
struct hci_conn *hcon;
|
||||
DECLARE_BITMAP(flags, __ISO_CONN_NUM_FLAGS);
|
||||
|
||||
/* @lock: spinlock protecting changes to iso_conn fields */
|
||||
spinlock_t lock;
|
||||
struct sock *sk;
|
||||
|
||||
struct delayed_work timeout_work;
|
||||
|
||||
struct sk_buff *rx_skb;
|
||||
__u32 rx_len;
|
||||
__u16 tx_sn;
|
||||
|
|
@ -56,6 +60,7 @@ static void iso_sock_kill(struct sock *sk);
|
|||
enum {
|
||||
BT_SK_BIG_SYNC,
|
||||
BT_SK_PA_SYNC,
|
||||
BT_SK_KILLED,
|
||||
};
|
||||
|
||||
struct iso_pinfo {
|
||||
|
|
@ -74,6 +79,7 @@ struct iso_pinfo {
|
|||
__u8 base_len;
|
||||
__u8 base[BASE_MAX_LENGTH];
|
||||
struct iso_conn *conn;
|
||||
struct delayed_work timeout_work;
|
||||
};
|
||||
|
||||
static struct bt_iso_qos default_qos;
|
||||
|
|
@ -102,16 +108,18 @@ static void iso_conn_free(struct kref *ref)
|
|||
|
||||
BT_DBG("conn %p", conn);
|
||||
|
||||
if (conn->sk)
|
||||
iso_pi(conn->sk)->conn = NULL;
|
||||
|
||||
if (conn->hcon) {
|
||||
conn->hcon->iso_data = NULL;
|
||||
hci_conn_drop(conn->hcon);
|
||||
}
|
||||
spin_lock(&conn->hcon->proto_lock);
|
||||
|
||||
/* Ensure no more work items will run since hci_conn has been dropped */
|
||||
disable_delayed_work_sync(&conn->timeout_work);
|
||||
/* Check we are not racing with iso_conn_add */
|
||||
if (conn->hcon->iso_data == conn) {
|
||||
conn->hcon->iso_data = NULL;
|
||||
if (!test_and_set_bit(ISO_CONN_DROPPED, conn->flags))
|
||||
hci_conn_drop(conn->hcon);
|
||||
}
|
||||
|
||||
spin_unlock(&conn->hcon->proto_lock);
|
||||
}
|
||||
|
||||
kfree_skb(conn->rx_skb);
|
||||
|
||||
|
|
@ -125,7 +133,21 @@ static void iso_conn_put(struct iso_conn *conn)
|
|||
|
||||
BT_DBG("conn %p refcnt %d", conn, kref_read(&conn->ref));
|
||||
|
||||
/* The following race vs. iso_conn_del() is possible:
|
||||
*
|
||||
* 1. conn->hcon != NULL here
|
||||
* 2. kref_put puts the last reference
|
||||
* 3. concurrent iso_conn_del() gets iso_conn_hold_unless_zero() -> NULL
|
||||
* and returns immediately, so conn->hcon is not cleared
|
||||
* 4. iso_conn_free() dereferences conn->hcon
|
||||
*
|
||||
* To avoid UAF in step 4, take RCU before decrementing the refcount.
|
||||
*/
|
||||
rcu_read_lock();
|
||||
|
||||
kref_put(&conn->ref, iso_conn_free);
|
||||
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
static struct iso_conn *iso_conn_hold_unless_zero(struct iso_conn *conn)
|
||||
|
|
@ -141,6 +163,14 @@ static struct iso_conn *iso_conn_hold_unless_zero(struct iso_conn *conn)
|
|||
return conn;
|
||||
}
|
||||
|
||||
static struct iso_conn *iso_conn_hold(struct iso_conn *conn)
|
||||
{
|
||||
BT_DBG("conn %p refcnt %u", conn, kref_read(&conn->ref));
|
||||
|
||||
kref_get(&conn->ref);
|
||||
return conn;
|
||||
}
|
||||
|
||||
static struct sock *iso_sock_hold(struct iso_conn *conn)
|
||||
{
|
||||
if (!conn || !bt_sock_linked(&iso_sk_list, conn->sk))
|
||||
|
|
@ -153,78 +183,81 @@ static struct sock *iso_sock_hold(struct iso_conn *conn)
|
|||
|
||||
static void iso_sock_timeout(struct work_struct *work)
|
||||
{
|
||||
struct iso_conn *conn = container_of(work, struct iso_conn,
|
||||
timeout_work.work);
|
||||
struct sock *sk;
|
||||
|
||||
conn = iso_conn_hold_unless_zero(conn);
|
||||
if (!conn)
|
||||
return;
|
||||
|
||||
iso_conn_lock(conn);
|
||||
sk = iso_sock_hold(conn);
|
||||
iso_conn_unlock(conn);
|
||||
iso_conn_put(conn);
|
||||
|
||||
if (!sk)
|
||||
return;
|
||||
struct iso_pinfo *pi = container_of(work, struct iso_pinfo,
|
||||
timeout_work.work);
|
||||
struct sock *sk = &pi->bt.sk;
|
||||
|
||||
BT_DBG("sock %p state %d", sk, sk->sk_state);
|
||||
|
||||
lock_sock(sk);
|
||||
sk->sk_err = ETIMEDOUT;
|
||||
sk->sk_state_change(sk);
|
||||
if (!sock_flag(sk, SOCK_ZAPPED)) {
|
||||
sk->sk_err = ETIMEDOUT;
|
||||
sk->sk_state_change(sk);
|
||||
}
|
||||
release_sock(sk);
|
||||
sock_put(sk);
|
||||
}
|
||||
|
||||
static void iso_sock_set_timer(struct sock *sk, long timeout)
|
||||
{
|
||||
lockdep_assert(lockdep_sock_is_held(sk));
|
||||
|
||||
cancel_delayed_work(&iso_pi(sk)->timeout_work);
|
||||
|
||||
if (!iso_pi(sk)->conn)
|
||||
return;
|
||||
|
||||
BT_DBG("sock %p state %d timeout %ld", sk, sk->sk_state, timeout);
|
||||
cancel_delayed_work(&iso_pi(sk)->conn->timeout_work);
|
||||
schedule_delayed_work(&iso_pi(sk)->conn->timeout_work, timeout);
|
||||
schedule_delayed_work(&iso_pi(sk)->timeout_work, timeout);
|
||||
}
|
||||
|
||||
static void iso_sock_clear_timer(struct sock *sk)
|
||||
{
|
||||
if (!iso_pi(sk)->conn)
|
||||
return;
|
||||
BT_DBG("sock %p state %d", sk, sk->sk_state);
|
||||
cancel_delayed_work(&iso_pi(sk)->timeout_work);
|
||||
}
|
||||
|
||||
static void iso_sock_disable_timer(struct sock *sk)
|
||||
{
|
||||
lockdep_assert(!lockdep_sock_is_held(sk));
|
||||
|
||||
BT_DBG("sock %p state %d", sk, sk->sk_state);
|
||||
cancel_delayed_work(&iso_pi(sk)->conn->timeout_work);
|
||||
disable_delayed_work_sync(&iso_pi(sk)->timeout_work);
|
||||
}
|
||||
|
||||
/* ---- ISO connections ---- */
|
||||
static struct iso_conn *iso_conn_add(struct hci_conn *hcon)
|
||||
__must_hold(&hcon->hdev->lock)
|
||||
{
|
||||
struct iso_conn *conn = hcon->iso_data;
|
||||
struct iso_conn *conn;
|
||||
|
||||
conn = iso_conn_hold_unless_zero(conn);
|
||||
spin_lock(&hcon->proto_lock);
|
||||
|
||||
conn = iso_conn_hold_unless_zero(hcon->iso_data);
|
||||
if (conn) {
|
||||
if (!conn->hcon) {
|
||||
iso_conn_lock(conn);
|
||||
conn->hcon = hcon;
|
||||
iso_conn_unlock(conn);
|
||||
}
|
||||
iso_conn_put(conn);
|
||||
spin_unlock(&hcon->proto_lock);
|
||||
return conn;
|
||||
}
|
||||
|
||||
conn = kzalloc_obj(*conn);
|
||||
if (!conn)
|
||||
conn = kzalloc_obj(*conn, GFP_ATOMIC);
|
||||
if (!conn) {
|
||||
spin_unlock(&hcon->proto_lock);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
kref_init(&conn->ref);
|
||||
spin_lock_init(&conn->lock);
|
||||
INIT_DELAYED_WORK(&conn->timeout_work, iso_sock_timeout);
|
||||
|
||||
hcon->iso_data = conn;
|
||||
conn->hcon = hcon;
|
||||
conn->tx_sn = 0;
|
||||
|
||||
spin_unlock(&hcon->proto_lock);
|
||||
|
||||
BT_DBG("hcon %p conn %p", hcon, conn);
|
||||
|
||||
return conn;
|
||||
|
|
@ -263,11 +296,14 @@ static void iso_chan_del(struct sock *sk, int err)
|
|||
}
|
||||
|
||||
static void iso_conn_del(struct hci_conn *hcon, int err)
|
||||
__must_hold(&hcon->hdev->lock)
|
||||
{
|
||||
struct iso_conn *conn = hcon->iso_data;
|
||||
struct iso_conn *conn;
|
||||
struct sock *sk;
|
||||
|
||||
conn = iso_conn_hold_unless_zero(conn);
|
||||
spin_lock(&hcon->proto_lock);
|
||||
conn = iso_conn_hold_unless_zero(hcon->iso_data);
|
||||
spin_unlock(&hcon->proto_lock);
|
||||
if (!conn)
|
||||
return;
|
||||
|
||||
|
|
@ -277,18 +313,28 @@ static void iso_conn_del(struct hci_conn *hcon, int err)
|
|||
iso_conn_lock(conn);
|
||||
sk = iso_sock_hold(conn);
|
||||
iso_conn_unlock(conn);
|
||||
iso_conn_put(conn);
|
||||
|
||||
if (!sk) {
|
||||
iso_conn_put(conn);
|
||||
return;
|
||||
}
|
||||
if (!sk)
|
||||
goto done;
|
||||
|
||||
iso_sock_disable_timer(sk);
|
||||
|
||||
lock_sock(sk);
|
||||
iso_sock_clear_timer(sk);
|
||||
iso_chan_del(sk, err);
|
||||
release_sock(sk);
|
||||
iso_sock_kill(sk);
|
||||
sock_put(sk);
|
||||
|
||||
done:
|
||||
/* No sk access to conn->hcon any more (lock_sock + hdev->lock) */
|
||||
spin_lock(&hcon->proto_lock);
|
||||
iso_conn_lock(conn);
|
||||
conn->hcon = NULL;
|
||||
hcon->iso_data = NULL;
|
||||
iso_conn_unlock(conn);
|
||||
spin_unlock(&hcon->proto_lock);
|
||||
|
||||
iso_conn_put(conn);
|
||||
}
|
||||
|
||||
static int __iso_chan_add(struct iso_conn *conn, struct sock *sk,
|
||||
|
|
@ -304,8 +350,14 @@ static int __iso_chan_add(struct iso_conn *conn, struct sock *sk,
|
|||
return -EBUSY;
|
||||
}
|
||||
|
||||
iso_pi(sk)->conn = conn;
|
||||
if (!conn->hcon) {
|
||||
BT_ERR("conn->hcon missing");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
iso_pi(sk)->conn = iso_conn_hold(conn);
|
||||
conn->sk = sk;
|
||||
clear_bit(ISO_CONN_DROPPED, conn->flags);
|
||||
|
||||
if (parent)
|
||||
bt_accept_enqueue(parent, sk, true);
|
||||
|
|
@ -402,6 +454,8 @@ static int iso_connect_bis(struct sock *sk)
|
|||
iso_pi(sk)->bc_sid = hcon->sid;
|
||||
}
|
||||
|
||||
lockdep_assert_held(&hcon->hdev->lock);
|
||||
|
||||
conn = iso_conn_add(hcon);
|
||||
if (!conn) {
|
||||
hci_conn_drop(hcon);
|
||||
|
|
@ -410,6 +464,7 @@ static int iso_connect_bis(struct sock *sk)
|
|||
}
|
||||
|
||||
err = iso_chan_add(conn, sk, NULL);
|
||||
iso_conn_put(conn);
|
||||
if (err)
|
||||
goto unlock;
|
||||
|
||||
|
|
@ -504,6 +559,8 @@ static int iso_connect_cis(struct sock *sk)
|
|||
}
|
||||
}
|
||||
|
||||
lockdep_assert_held(&hcon->hdev->lock);
|
||||
|
||||
conn = iso_conn_add(hcon);
|
||||
if (!conn) {
|
||||
hci_conn_drop(hcon);
|
||||
|
|
@ -512,6 +569,7 @@ static int iso_connect_cis(struct sock *sk)
|
|||
}
|
||||
|
||||
err = iso_chan_add(conn, sk, NULL);
|
||||
iso_conn_put(conn);
|
||||
if (err)
|
||||
goto unlock;
|
||||
|
||||
|
|
@ -790,9 +848,15 @@ static void iso_sock_cleanup_listen(struct sock *parent)
|
|||
*/
|
||||
static void iso_sock_kill(struct sock *sk)
|
||||
{
|
||||
iso_sock_disable_timer(sk);
|
||||
|
||||
lock_sock(sk);
|
||||
|
||||
if (!sock_flag(sk, SOCK_ZAPPED) || sk->sk_socket ||
|
||||
sock_flag(sk, SOCK_DEAD))
|
||||
test_bit(BT_SK_KILLED, &iso_pi(sk)->flags)) {
|
||||
release_sock(sk);
|
||||
return;
|
||||
}
|
||||
|
||||
BT_DBG("sk %p state %d", sk, sk->sk_state);
|
||||
|
||||
|
|
@ -806,6 +870,9 @@ static void iso_sock_kill(struct sock *sk)
|
|||
/* Kill poor orphan */
|
||||
bt_sock_unlink(&iso_sk_list, sk);
|
||||
sock_set_flag(sk, SOCK_DEAD);
|
||||
set_bit(BT_SK_KILLED, &iso_pi(sk)->flags);
|
||||
|
||||
release_sock(sk);
|
||||
sock_put(sk);
|
||||
}
|
||||
|
||||
|
|
@ -825,8 +892,8 @@ static void iso_sock_disconn(struct sock *sk)
|
|||
*/
|
||||
if (bis_sk) {
|
||||
hcon->state = BT_OPEN;
|
||||
hcon->iso_data = NULL;
|
||||
iso_pi(sk)->conn->hcon = NULL;
|
||||
set_bit(ISO_CONN_DROPPED, iso_pi(sk)->conn->flags);
|
||||
|
||||
iso_sock_clear_timer(sk);
|
||||
iso_chan_del(sk, bt_to_errno(hcon->abort_reason));
|
||||
sock_put(bis_sk);
|
||||
|
|
@ -835,10 +902,8 @@ static void iso_sock_disconn(struct sock *sk)
|
|||
}
|
||||
|
||||
sk->sk_state = BT_DISCONN;
|
||||
iso_conn_lock(iso_pi(sk)->conn);
|
||||
hci_conn_drop(iso_pi(sk)->conn->hcon);
|
||||
iso_pi(sk)->conn->hcon = NULL;
|
||||
iso_conn_unlock(iso_pi(sk)->conn);
|
||||
if (!test_and_set_bit(ISO_CONN_DROPPED, iso_pi(sk)->conn->flags))
|
||||
hci_conn_drop(iso_pi(sk)->conn->hcon);
|
||||
}
|
||||
|
||||
static void __iso_sock_close(struct sock *sk)
|
||||
|
|
@ -880,11 +945,11 @@ static void __iso_sock_close(struct sock *sk)
|
|||
/* Must be called on unlocked socket. */
|
||||
static void iso_sock_close(struct sock *sk)
|
||||
{
|
||||
iso_sock_disable_timer(sk);
|
||||
|
||||
lock_sock(sk);
|
||||
iso_sock_clear_timer(sk);
|
||||
__iso_sock_close(sk);
|
||||
release_sock(sk);
|
||||
iso_sock_kill(sk);
|
||||
}
|
||||
|
||||
static void iso_sock_init(struct sock *sk, struct sock *parent)
|
||||
|
|
@ -951,6 +1016,8 @@ static struct sock *iso_sock_alloc(struct net *net, struct socket *sock,
|
|||
iso_pi(sk)->qos = default_qos;
|
||||
iso_pi(sk)->sync_handle = -1;
|
||||
|
||||
INIT_DELAYED_WORK(&iso_pi(sk)->timeout_work, iso_sock_timeout);
|
||||
|
||||
bt_sock_link(&iso_sk_list, sk);
|
||||
return sk;
|
||||
}
|
||||
|
|
@ -1033,15 +1100,15 @@ static int iso_sock_rebind_bis(struct sock *sk, struct sockaddr_iso *sa,
|
|||
goto done;
|
||||
}
|
||||
|
||||
iso_pi(sk)->bc_num_bis = sa->iso_bc->bc_num_bis;
|
||||
|
||||
for (int i = 0; i < iso_pi(sk)->bc_num_bis; i++)
|
||||
for (int i = 0; i < sa->iso_bc->bc_num_bis; i++)
|
||||
if (sa->iso_bc->bc_bis[i] < 0x01 ||
|
||||
sa->iso_bc->bc_bis[i] > 0x1f) {
|
||||
err = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
iso_pi(sk)->bc_num_bis = sa->iso_bc->bc_num_bis;
|
||||
|
||||
memcpy(iso_pi(sk)->bc_bis, sa->iso_bc->bc_bis,
|
||||
iso_pi(sk)->bc_num_bis);
|
||||
|
||||
|
|
@ -1276,6 +1343,8 @@ static int iso_listen_bis(struct sock *sk)
|
|||
goto unlock;
|
||||
}
|
||||
|
||||
lockdep_assert_held(&hcon->hdev->lock);
|
||||
|
||||
conn = iso_conn_add(hcon);
|
||||
if (!conn) {
|
||||
hci_conn_drop(hcon);
|
||||
|
|
@ -1284,10 +1353,9 @@ static int iso_listen_bis(struct sock *sk)
|
|||
}
|
||||
|
||||
err = iso_chan_add(conn, sk, NULL);
|
||||
if (err) {
|
||||
hci_conn_drop(hcon);
|
||||
iso_conn_put(conn);
|
||||
if (err)
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
unlock:
|
||||
release_sock(sk);
|
||||
|
|
@ -1466,6 +1534,8 @@ static int iso_sock_getname(struct socket *sock, struct sockaddr *addr,
|
|||
|
||||
BT_DBG("sock %p, sk %p", sock, sk);
|
||||
|
||||
lock_sock(sk);
|
||||
|
||||
addr->sa_family = AF_BLUETOOTH;
|
||||
|
||||
if (peer) {
|
||||
|
|
@ -1487,6 +1557,8 @@ static int iso_sock_getname(struct socket *sock, struct sockaddr *addr,
|
|||
sa->iso_bdaddr_type = iso_pi(sk)->src_type;
|
||||
}
|
||||
|
||||
release_sock(sk);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
|
|
@ -1786,7 +1858,7 @@ static bool check_bcast_qos(struct bt_iso_qos *qos)
|
|||
return false;
|
||||
|
||||
if (!qos->bcast.timeout)
|
||||
qos->bcast.sync_timeout = BT_ISO_SYNC_TIMEOUT;
|
||||
qos->bcast.timeout = BT_ISO_SYNC_TIMEOUT;
|
||||
|
||||
if (qos->bcast.timeout < 0x000a || qos->bcast.timeout > 0x4000)
|
||||
return false;
|
||||
|
|
@ -2028,8 +2100,16 @@ static int iso_sock_release(struct socket *sock)
|
|||
release_sock(sk);
|
||||
}
|
||||
|
||||
/* Make sure sk is valid even if iso_conn_del() is concurrent */
|
||||
sock_hold(sk);
|
||||
|
||||
lock_sock(sk);
|
||||
sock_orphan(sk);
|
||||
release_sock(sk);
|
||||
|
||||
iso_sock_kill(sk);
|
||||
|
||||
sock_put(sk);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
|
@ -2037,14 +2117,17 @@ static void iso_sock_ready(struct sock *sk)
|
|||
{
|
||||
BT_DBG("sk %p", sk);
|
||||
|
||||
if (!sk)
|
||||
return;
|
||||
lockdep_assert(lockdep_sock_is_held(sk));
|
||||
|
||||
switch (sk->sk_state) {
|
||||
case BT_DISCONN:
|
||||
case BT_CLOSED:
|
||||
return;
|
||||
}
|
||||
|
||||
lock_sock(sk);
|
||||
iso_sock_clear_timer(sk);
|
||||
sk->sk_state = BT_CONNECTED;
|
||||
sk->sk_state_change(sk);
|
||||
release_sock(sk);
|
||||
}
|
||||
|
||||
static bool iso_match_big(struct sock *sk, void *data)
|
||||
|
|
@ -2074,7 +2157,7 @@ static bool iso_match_dst(struct sock *sk, void *data)
|
|||
static void iso_conn_ready(struct iso_conn *conn)
|
||||
{
|
||||
struct sock *parent = NULL;
|
||||
struct sock *sk = conn->sk;
|
||||
struct sock *sk;
|
||||
struct hci_ev_le_big_sync_established *ev = NULL;
|
||||
struct hci_ev_le_pa_sync_established *ev2 = NULL;
|
||||
struct hci_ev_le_per_adv_report *ev3 = NULL;
|
||||
|
|
@ -2083,7 +2166,22 @@ static void iso_conn_ready(struct iso_conn *conn)
|
|||
|
||||
BT_DBG("conn %p", conn);
|
||||
|
||||
iso_conn_lock(conn);
|
||||
sk = iso_sock_hold(conn);
|
||||
iso_conn_unlock(conn);
|
||||
|
||||
if (sk) {
|
||||
lock_sock(sk);
|
||||
|
||||
/* 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) {
|
||||
release_sock(sk);
|
||||
sock_put(sk);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Attempt to update source address in case of BIS Sender if
|
||||
* the advertisement is using a random address.
|
||||
*/
|
||||
|
|
@ -2096,14 +2194,15 @@ static void iso_conn_ready(struct iso_conn *conn)
|
|||
adv = hci_find_adv_instance(bis->hdev,
|
||||
bis->iso_qos.bcast.bis);
|
||||
if (adv && bacmp(&adv->random_addr, BDADDR_ANY)) {
|
||||
lock_sock(sk);
|
||||
iso_pi(sk)->src_type = BDADDR_LE_RANDOM;
|
||||
bacpy(&iso_pi(sk)->src, &adv->random_addr);
|
||||
release_sock(sk);
|
||||
}
|
||||
}
|
||||
|
||||
iso_sock_ready(conn->sk);
|
||||
iso_sock_ready(sk);
|
||||
|
||||
release_sock(sk);
|
||||
sock_put(sk);
|
||||
} else {
|
||||
hcon = conn->hcon;
|
||||
if (!hcon)
|
||||
|
|
@ -2350,7 +2449,7 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
|
|||
|
||||
lock_sock(sk);
|
||||
|
||||
hcon = iso_pi(sk)->conn->hcon;
|
||||
hcon = iso_pi(sk)->conn ? iso_pi(sk)->conn->hcon : NULL;
|
||||
iso_pi(sk)->qos.bcast.encryption = ev2->encryption;
|
||||
|
||||
if (ev2->num_bis < iso_pi(sk)->bc_num_bis)
|
||||
|
|
@ -2390,9 +2489,11 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
|
|||
if (!sk)
|
||||
goto done;
|
||||
|
||||
hcon = iso_pi(sk)->conn->hcon;
|
||||
lock_sock(sk);
|
||||
|
||||
hcon = iso_pi(sk)->conn ? iso_pi(sk)->conn->hcon : NULL;
|
||||
if (!hcon)
|
||||
goto done;
|
||||
goto release3;
|
||||
|
||||
if (ev3->data_status == LE_PA_DATA_TRUNCATED) {
|
||||
/* The controller was unable to retrieve PA data. */
|
||||
|
|
@ -2400,12 +2501,12 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
|
|||
HCI_MAX_PER_AD_TOT_LEN);
|
||||
hcon->le_per_adv_data_len = 0;
|
||||
hcon->le_per_adv_data_offset = 0;
|
||||
goto done;
|
||||
goto release3;
|
||||
}
|
||||
|
||||
if (hcon->le_per_adv_data_offset + ev3->length >
|
||||
HCI_MAX_PER_AD_TOT_LEN)
|
||||
goto done;
|
||||
goto release3;
|
||||
|
||||
memcpy(hcon->le_per_adv_data + hcon->le_per_adv_data_offset,
|
||||
ev3->data, ev3->length);
|
||||
|
|
@ -2424,18 +2525,19 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
|
|||
&base_len);
|
||||
|
||||
if (!base || base_len > BASE_MAX_LENGTH)
|
||||
goto done;
|
||||
goto release3;
|
||||
|
||||
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.
|
||||
*/
|
||||
hcon->le_per_adv_data_len = 0;
|
||||
}
|
||||
|
||||
release3:
|
||||
release_sock(sk);
|
||||
} else {
|
||||
sk = iso_get_sock(hdev, &hdev->bdaddr, BDADDR_ANY,
|
||||
BT_LISTEN, iso_match_dst, BDADDR_ANY);
|
||||
|
|
@ -2454,6 +2556,7 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
|
|||
}
|
||||
|
||||
static void iso_connect_cfm(struct hci_conn *hcon, __u8 status)
|
||||
__must_hold(&hcon->hdev->lock)
|
||||
{
|
||||
if (hcon->type != CIS_LINK && hcon->type != BIS_LINK &&
|
||||
hcon->type != PA_LINK) {
|
||||
|
|
@ -2465,8 +2568,10 @@ static void iso_connect_cfm(struct hci_conn *hcon, __u8 status)
|
|||
struct hci_link *link, *t;
|
||||
|
||||
list_for_each_entry_safe(link, t, &hcon->link_list,
|
||||
list)
|
||||
list) {
|
||||
lockdep_assert_held(&link->conn->hdev->lock);
|
||||
iso_conn_del(link->conn, bt_to_errno(status));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
|
@ -2488,14 +2593,17 @@ static void iso_connect_cfm(struct hci_conn *hcon, __u8 status)
|
|||
struct iso_conn *conn;
|
||||
|
||||
conn = iso_conn_add(hcon);
|
||||
if (conn)
|
||||
if (conn) {
|
||||
iso_conn_ready(conn);
|
||||
iso_conn_put(conn);
|
||||
}
|
||||
} else {
|
||||
iso_conn_del(hcon, bt_to_errno(status));
|
||||
}
|
||||
}
|
||||
|
||||
static void iso_disconn_cfm(struct hci_conn *hcon, __u8 reason)
|
||||
__must_hold(&hcon->hdev->lock)
|
||||
{
|
||||
if (hcon->type != CIS_LINK && hcon->type != BIS_LINK &&
|
||||
hcon->type != PA_LINK)
|
||||
|
|
@ -2522,7 +2630,10 @@ int iso_recv(struct hci_dev *hdev, u16 handle, struct sk_buff *skb, u16 flags)
|
|||
return -ENOENT;
|
||||
}
|
||||
|
||||
spin_lock(&hcon->proto_lock);
|
||||
conn = iso_conn_hold_unless_zero(hcon->iso_data);
|
||||
spin_unlock(&hcon->proto_lock);
|
||||
|
||||
hcon = NULL;
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
|
|
|
|||
|
|
@ -4820,6 +4820,10 @@ static int l2cap_le_connect_rsp(struct l2cap_conn *conn,
|
|||
if (!chan)
|
||||
return -EBADSLT;
|
||||
|
||||
chan = l2cap_chan_hold_unless_zero(chan);
|
||||
if (!chan)
|
||||
return -EBADSLT;
|
||||
|
||||
err = 0;
|
||||
|
||||
l2cap_chan_lock(chan);
|
||||
|
|
@ -4865,6 +4869,7 @@ static int l2cap_le_connect_rsp(struct l2cap_conn *conn,
|
|||
}
|
||||
|
||||
l2cap_chan_unlock(chan);
|
||||
l2cap_chan_put(chan);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2696,18 +2696,28 @@ static int mgmt_hci_cmd_sync(struct sock *sk, struct hci_dev *hdev,
|
|||
static bool pending_eir_or_class(struct hci_dev *hdev)
|
||||
{
|
||||
struct mgmt_pending_cmd *cmd;
|
||||
bool pending = false;
|
||||
|
||||
mutex_lock(&hdev->mgmt_pending_lock);
|
||||
|
||||
list_for_each_entry(cmd, &hdev->mgmt_pending, list) {
|
||||
switch (cmd->opcode) {
|
||||
case MGMT_OP_ADD_UUID:
|
||||
case MGMT_OP_REMOVE_UUID:
|
||||
case MGMT_OP_SET_DEV_CLASS:
|
||||
case MGMT_OP_SET_LOCAL_NAME:
|
||||
case MGMT_OP_SET_POWERED:
|
||||
return true;
|
||||
pending = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (pending)
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
mutex_unlock(&hdev->mgmt_pending_lock);
|
||||
|
||||
return pending;
|
||||
}
|
||||
|
||||
static const u8 bluetooth_base_uuid[] = {
|
||||
|
|
@ -3514,11 +3524,13 @@ static int set_io_capability(struct sock *sk, struct hci_dev *hdev, void *data,
|
|||
NULL, 0);
|
||||
}
|
||||
|
||||
static struct mgmt_pending_cmd *find_pairing(struct hci_conn *conn)
|
||||
static struct mgmt_pending_cmd *remove_pairing(struct hci_conn *conn)
|
||||
{
|
||||
struct hci_dev *hdev = conn->hdev;
|
||||
struct mgmt_pending_cmd *cmd;
|
||||
|
||||
mutex_lock(&hdev->mgmt_pending_lock);
|
||||
|
||||
list_for_each_entry(cmd, &hdev->mgmt_pending, list) {
|
||||
if (cmd->opcode != MGMT_OP_PAIR_DEVICE)
|
||||
continue;
|
||||
|
|
@ -3526,9 +3538,39 @@ static struct mgmt_pending_cmd *find_pairing(struct hci_conn *conn)
|
|||
if (cmd->user_data != conn)
|
||||
continue;
|
||||
|
||||
list_del(&cmd->list);
|
||||
mutex_unlock(&hdev->mgmt_pending_lock);
|
||||
return cmd;
|
||||
}
|
||||
|
||||
mutex_unlock(&hdev->mgmt_pending_lock);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct mgmt_pending_cmd *remove_pairing_by_addr(struct hci_dev *hdev,
|
||||
bdaddr_t *bdaddr)
|
||||
{
|
||||
struct mgmt_pending_cmd *cmd;
|
||||
struct hci_conn *conn;
|
||||
|
||||
mutex_lock(&hdev->mgmt_pending_lock);
|
||||
|
||||
list_for_each_entry(cmd, &hdev->mgmt_pending, list) {
|
||||
if (cmd->opcode != MGMT_OP_PAIR_DEVICE)
|
||||
continue;
|
||||
|
||||
conn = cmd->user_data;
|
||||
if (bacmp(bdaddr, &conn->dst) != 0)
|
||||
continue;
|
||||
|
||||
list_del(&cmd->list);
|
||||
mutex_unlock(&hdev->mgmt_pending_lock);
|
||||
return cmd;
|
||||
}
|
||||
|
||||
mutex_unlock(&hdev->mgmt_pending_lock);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
@ -3566,10 +3608,10 @@ void mgmt_smp_complete(struct hci_conn *conn, bool complete)
|
|||
u8 status = complete ? MGMT_STATUS_SUCCESS : MGMT_STATUS_FAILED;
|
||||
struct mgmt_pending_cmd *cmd;
|
||||
|
||||
cmd = find_pairing(conn);
|
||||
cmd = remove_pairing(conn);
|
||||
if (cmd) {
|
||||
cmd->cmd_complete(cmd, status);
|
||||
mgmt_pending_remove(cmd);
|
||||
mgmt_pending_free(cmd);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -3579,14 +3621,14 @@ static void pairing_complete_cb(struct hci_conn *conn, u8 status)
|
|||
|
||||
BT_DBG("status %u", status);
|
||||
|
||||
cmd = find_pairing(conn);
|
||||
cmd = remove_pairing(conn);
|
||||
if (!cmd) {
|
||||
BT_DBG("Unable to find a pending command");
|
||||
return;
|
||||
}
|
||||
|
||||
cmd->cmd_complete(cmd, mgmt_status(status));
|
||||
mgmt_pending_remove(cmd);
|
||||
mgmt_pending_free(cmd);
|
||||
}
|
||||
|
||||
static void le_pairing_complete_cb(struct hci_conn *conn, u8 status)
|
||||
|
|
@ -3598,14 +3640,14 @@ static void le_pairing_complete_cb(struct hci_conn *conn, u8 status)
|
|||
if (!status)
|
||||
return;
|
||||
|
||||
cmd = find_pairing(conn);
|
||||
cmd = remove_pairing(conn);
|
||||
if (!cmd) {
|
||||
BT_DBG("Unable to find a pending command");
|
||||
return;
|
||||
}
|
||||
|
||||
cmd->cmd_complete(cmd, mgmt_status(status));
|
||||
mgmt_pending_remove(cmd);
|
||||
mgmt_pending_free(cmd);
|
||||
}
|
||||
|
||||
static int pair_device(struct sock *sk, struct hci_dev *hdev, void *data,
|
||||
|
|
@ -3762,23 +3804,17 @@ static int cancel_pair_device(struct sock *sk, struct hci_dev *hdev, void *data,
|
|||
goto unlock;
|
||||
}
|
||||
|
||||
cmd = pending_find(MGMT_OP_PAIR_DEVICE, hdev);
|
||||
cmd = remove_pairing_by_addr(hdev, &addr->bdaddr);
|
||||
if (!cmd) {
|
||||
err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_CANCEL_PAIR_DEVICE,
|
||||
MGMT_STATUS_INVALID_PARAMS);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
conn = cmd->user_data;
|
||||
|
||||
if (bacmp(&addr->bdaddr, &conn->dst) != 0) {
|
||||
err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_CANCEL_PAIR_DEVICE,
|
||||
MGMT_STATUS_INVALID_PARAMS);
|
||||
goto unlock;
|
||||
}
|
||||
conn = hci_conn_get(cmd->user_data);
|
||||
|
||||
cmd->cmd_complete(cmd, MGMT_STATUS_CANCELLED);
|
||||
mgmt_pending_remove(cmd);
|
||||
mgmt_pending_free(cmd);
|
||||
|
||||
err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_CANCEL_PAIR_DEVICE, 0,
|
||||
addr, sizeof(*addr));
|
||||
|
|
@ -3796,6 +3832,8 @@ static int cancel_pair_device(struct sock *sk, struct hci_dev *hdev, void *data,
|
|||
if (conn->conn_reason == CONN_REASON_PAIR_DEVICE)
|
||||
hci_abort_conn(conn, HCI_ERROR_REMOTE_USER_TERM);
|
||||
|
||||
hci_conn_put(conn);
|
||||
|
||||
unlock:
|
||||
hci_dev_unlock(hdev);
|
||||
return err;
|
||||
|
|
@ -4043,6 +4081,12 @@ static int set_local_name(struct sock *sk, struct hci_dev *hdev, void *data,
|
|||
goto failed;
|
||||
}
|
||||
|
||||
if (hdev_is_powered(hdev) && pending_eir_or_class(hdev)) {
|
||||
err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_LOCAL_NAME,
|
||||
MGMT_STATUS_BUSY);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
memcpy(hdev->short_name, cp->short_name, sizeof(hdev->short_name));
|
||||
|
||||
if (!hdev_is_powered(hdev)) {
|
||||
|
|
@ -10137,14 +10181,14 @@ void mgmt_auth_failed(struct hci_conn *conn, u8 hci_status)
|
|||
ev.addr.type = link_to_bdaddr(conn->type, conn->dst_type);
|
||||
ev.status = status;
|
||||
|
||||
cmd = find_pairing(conn);
|
||||
cmd = remove_pairing(conn);
|
||||
|
||||
mgmt_event(MGMT_EV_AUTH_FAILED, conn->hdev, &ev, sizeof(ev),
|
||||
cmd ? cmd->sk : NULL);
|
||||
|
||||
if (cmd) {
|
||||
cmd->cmd_complete(cmd, status);
|
||||
mgmt_pending_remove(cmd);
|
||||
mgmt_pending_free(cmd);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1795,6 +1795,11 @@ static struct rfcomm_session *rfcomm_recv_frame(struct rfcomm_session *s,
|
|||
return s;
|
||||
}
|
||||
|
||||
if (skb->len < sizeof(*hdr) + 1) {
|
||||
kfree_skb(skb);
|
||||
return s;
|
||||
}
|
||||
|
||||
dlci = __get_dlci(hdr->addr);
|
||||
type = __get_type(hdr->ctrl);
|
||||
|
||||
|
|
|
|||
|
|
@ -188,6 +188,9 @@ static void sco_sock_clear_timer(struct sock *sk)
|
|||
}
|
||||
|
||||
/* ---- SCO connections ---- */
|
||||
/* Consumes a reference on @hcon, which the returned sco_conn owns until it is
|
||||
* freed. On failure (NULL return) the reference is left for the caller to drop.
|
||||
*/
|
||||
static struct sco_conn *sco_conn_add(struct hci_conn *hcon)
|
||||
{
|
||||
struct sco_conn *conn = hcon->sco_data;
|
||||
|
|
@ -198,6 +201,9 @@ static struct sco_conn *sco_conn_add(struct hci_conn *hcon)
|
|||
sco_conn_lock(conn);
|
||||
conn->hcon = hcon;
|
||||
sco_conn_unlock(conn);
|
||||
} else {
|
||||
/* conn already owns a reference on hcon */
|
||||
hci_conn_drop(hcon);
|
||||
}
|
||||
return conn;
|
||||
}
|
||||
|
|
@ -265,10 +271,8 @@ static void sco_conn_del(struct hci_conn *hcon, int err)
|
|||
sco_conn_unlock(conn);
|
||||
sco_conn_put(conn);
|
||||
|
||||
if (!sk) {
|
||||
sco_conn_put(conn);
|
||||
if (!sk)
|
||||
return;
|
||||
}
|
||||
|
||||
/* Kill socket */
|
||||
lock_sock(sk);
|
||||
|
|
@ -283,7 +287,7 @@ static void __sco_chan_add(struct sco_conn *conn, struct sock *sk,
|
|||
{
|
||||
BT_DBG("conn %p", conn);
|
||||
|
||||
sco_pi(sk)->conn = conn;
|
||||
sco_pi(sk)->conn = sco_conn_hold(conn);
|
||||
conn->sk = sk;
|
||||
|
||||
if (parent)
|
||||
|
|
@ -366,15 +370,15 @@ static int sco_connect(struct sock *sk)
|
|||
*/
|
||||
if (sk->sk_state != BT_OPEN && sk->sk_state != BT_BOUND) {
|
||||
release_sock(sk);
|
||||
hci_conn_drop(hcon);
|
||||
sco_conn_put(conn);
|
||||
err = -EBADFD;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
err = sco_chan_add(conn, sk, NULL);
|
||||
sco_conn_put(conn);
|
||||
if (err) {
|
||||
release_sock(sk);
|
||||
hci_conn_drop(hcon);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
|
|
@ -1452,8 +1456,6 @@ static void sco_conn_ready(struct sco_conn *conn)
|
|||
bacpy(&sco_pi(sk)->src, &conn->hcon->src);
|
||||
bacpy(&sco_pi(sk)->dst, &conn->hcon->dst);
|
||||
|
||||
sco_conn_hold(conn);
|
||||
hci_conn_hold(conn->hcon);
|
||||
__sco_chan_add(conn, sk, parent);
|
||||
|
||||
if (test_bit(BT_SK_DEFER_SETUP, &bt_sk(parent)->flags))
|
||||
|
|
@ -1509,10 +1511,12 @@ static void sco_connect_cfm(struct hci_conn *hcon, __u8 status)
|
|||
if (!status) {
|
||||
struct sco_conn *conn;
|
||||
|
||||
conn = sco_conn_add(hcon);
|
||||
conn = sco_conn_add(hci_conn_hold(hcon));
|
||||
if (conn) {
|
||||
sco_conn_ready(conn);
|
||||
sco_conn_put(conn);
|
||||
} else {
|
||||
hci_conn_drop(hcon);
|
||||
}
|
||||
} else
|
||||
sco_conn_del(hcon, bt_to_errno(status));
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user