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:
Jakub Kicinski 2026-07-28 17:30:21 -07:00
commit 0408e7f6f6
12 changed files with 516 additions and 262 deletions

View File

@ -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;

View File

@ -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;
}

View File

@ -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);

View File

@ -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;

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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);

View File

@ -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;
}

View File

@ -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);
}
}

View File

@ -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);

View File

@ -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));