mirror of
https://github.com/torvalds/linux.git
synced 2026-07-27 17:47:41 +02:00
bluetooth pull request for net:
- hci_conn: Fix null ptr deref in hci_abort_conn() - af_bluetooth: fix UAF in bt_accept_dequeue() - L2CAP: validate option length before reading conf opt value - L2CAP: cancel pending_rx_work before taking conn->lock - L2CAP: Fix use-after-free in l2cap_sock_new_connection_cb() - L2CAP: fix tx ident leak for commands without a response - SCO: Fix a race condition in sco_sock_timeout() - ISO: avoid NULL deref of conn in iso_conn_big_sync() - ISO: fix malformed ISO_END/CONT handling - ISO: exclude RFU bits from ISO_SDU_Length - MGMT: Fix adv monitor add failure cleanup - MGMT: Fix UAF of hci_conn_params in add_device_complete - bnep: pin L2CAP connection during netdev registration - 6lowpan: avoid untracked enable work - 6lowpan: hold L2CAP conn across debugfs control - 6lowpan: Fix using chan->conn as indication to no remote netdev - btintel_pcie: Refactor FLR to use device_reprobe() - btnxpuart: Fix out-of-bounds firmware read in nxp_recv_fw_req_v3() - hci_uart: clear HCI_UART_SENDING when write_work is canceled - bpa10x: avoid OOB read of revision string in bpa10x_setup() -----BEGIN PGP SIGNATURE----- iQJNBAABCgA3FiEE7E6oRXp8w05ovYr/9JCA4xAyCykFAmpLwJsZHGx1aXoudm9u LmRlbnR6QGludGVsLmNvbQAKCRD0kIDjEDILKS5ZD/0fCFfYhjMTUOk6eiFM/+gd GKgQ5Tyg3C0oo7N8eOKGgM471DOsxKoDuBCmp5ezK9AyyTHnGnxDJz7ciYYGNR8f zYFF8u4HDit5C5cx0BDb8VMRTlK9A/Q3c6CVBmOueQNcGjekkkGEM2zNAwC/z43w ifVhNhkJKG0RnAV6ukq2mE0SJfG1+Vad5MXyp+IllfZig4f1MkggI9BRAK080xe4 M/iVBAWciPsQ+83WFt+p0DARYiIME57CLN3FQbtSYqRadMOlk8MzqTX7iChGs+rN TC+2nRJ7oefjtk7zYcOVJjkySEwvJWuHlK8Hmdehjc6s22hfg0VpZqCSPYZn5Z4V +VEpDBcT114jvuGZQF2e4wHn7YrYEVhYxPVWWU5+bs+V3a/aTHFHUhzVbvFb2m0z lEkLC/FtPWJz5gDjkuCal1Jle4BNcHOSdqKV/yB++ee1lM1N6veKSH9nYajD+qah BMWaXshjLAjHQOFfwwBy5cmm53hVpzMyDZEpMyxzDZ9xiQ4hGIg3b/b1M105yctf 1+h06J+GoCsYjGlNbT0RdP55TmRaDmZc7yEoD9eWUjMPxXiQoPNfEiBxpuQ6E8tz 1nXIabkehRpthq1sJC90K3+gk3zB3VeOVm38YzdHAFT4qMzLWRdKhls3rl0Nxzlq qlg33FW/SGiz5/PaD1IiiQ== =FcoD -----END PGP SIGNATURE----- Merge tag 'for-net-2026-07-06' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth Luiz Augusto von Dentz says: ==================== bluetooth pull request for net: - hci_conn: Fix null ptr deref in hci_abort_conn() - af_bluetooth: fix UAF in bt_accept_dequeue() - L2CAP: validate option length before reading conf opt value - L2CAP: cancel pending_rx_work before taking conn->lock - L2CAP: Fix use-after-free in l2cap_sock_new_connection_cb() - L2CAP: fix tx ident leak for commands without a response - SCO: Fix a race condition in sco_sock_timeout() - ISO: avoid NULL deref of conn in iso_conn_big_sync() - ISO: fix malformed ISO_END/CONT handling - ISO: exclude RFU bits from ISO_SDU_Length - MGMT: Fix adv monitor add failure cleanup - MGMT: Fix UAF of hci_conn_params in add_device_complete - bnep: pin L2CAP connection during netdev registration - 6lowpan: avoid untracked enable work - 6lowpan: hold L2CAP conn across debugfs control - 6lowpan: Fix using chan->conn as indication to no remote netdev - btintel_pcie: Refactor FLR to use device_reprobe() - btnxpuart: Fix out-of-bounds firmware read in nxp_recv_fw_req_v3() - hci_uart: clear HCI_UART_SENDING when write_work is canceled - bpa10x: avoid OOB read of revision string in bpa10x_setup() * tag 'for-net-2026-07-06' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth: Bluetooth: L2CAP: fix tx ident leak for commands without a response Bluetooth: bpa10x: avoid OOB read of revision string in bpa10x_setup() Bluetooth: ISO: exclude RFU bits from ISO_SDU_Length Bluetooth: ISO: fix malformed ISO_END/CONT handling Bluetooth: btintel_pcie: Refactor FLR to use device_reprobe() Bluetooth: L2CAP: Fix use-after-free in l2cap_sock_new_connection_cb() Bluetooth: fix UAF in bt_accept_dequeue() Bluetooth: bnep: pin L2CAP connection during netdev registration Bluetooth: sco: Fix a race condition in sco_sock_timeout() Bluetooth: MGMT: Fix adv monitor add failure cleanup Bluetooth: 6lowpan: hold L2CAP conn across debugfs control Bluetooth: 6lowpan: avoid untracked enable work Bluetooth: hci_conn: Fix null ptr deref in hci_abort_conn() Bluetooth: btnxpuart: Fix out-of-bounds firmware read in nxp_recv_fw_req_v3() Bluetooth: L2CAP: validate option length before reading conf opt value Bluetooth: L2CAP: cancel pending_rx_work before taking conn->lock Bluetooth: ISO: avoid NULL deref of conn in iso_conn_big_sync() Bluetooth: MGMT: Fix UAF of hci_conn_params in add_device_complete Bluetooth: 6lowpan: Fix using chan->conn as indication to no remote netdev Bluetooth: hci_uart: clear HCI_UART_SENDING when write_work is canceled ==================== Link: https://patch.msgid.link/20260706145229.728127-1-luiz.dentz@gmail.com Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
commit
5258b4fdae
|
|
@ -255,9 +255,13 @@ static int bpa10x_setup(struct hci_dev *hdev)
|
|||
if (IS_ERR(skb))
|
||||
return PTR_ERR(skb);
|
||||
|
||||
bt_dev_info(hdev, "%s", (char *)(skb->data + 1));
|
||||
/* Bounded print: the device controls skb->len. */
|
||||
if (skb->len > 1) {
|
||||
int len = skb->len - 1;
|
||||
|
||||
hci_set_fw_info(hdev, "%s", skb->data + 1);
|
||||
bt_dev_info(hdev, "%.*s", len, (char *)(skb->data + 1));
|
||||
hci_set_fw_info(hdev, "%.*s", len, skb->data + 1);
|
||||
}
|
||||
|
||||
kfree_skb(skb);
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -2127,6 +2127,9 @@ static int btintel_pcie_send_frame(struct hci_dev *hdev,
|
|||
if (test_bit(BTINTEL_PCIE_CORE_HALTED, &data->flags))
|
||||
return -ENODEV;
|
||||
|
||||
if (test_bit(BTINTEL_PCIE_RECOVERY_IN_PROGRESS, &data->flags))
|
||||
return -ENODEV;
|
||||
|
||||
/* Due to the fw limitation, the type header of the packet should be
|
||||
* 4 bytes unlike 1 byte for UART. In UART, the firmware can read
|
||||
* the first byte to get the packet type and redirect the rest of data
|
||||
|
|
@ -2485,7 +2488,6 @@ static void btintel_pcie_inc_recovery_count(struct pci_dev *pdev,
|
|||
}
|
||||
}
|
||||
|
||||
static int btintel_pcie_setup_hdev(struct btintel_pcie_data *data);
|
||||
static void btintel_pcie_reset(struct hci_dev *hdev);
|
||||
|
||||
static int btintel_pcie_acpi_reset_method(struct btintel_pcie_data *data)
|
||||
|
|
@ -2596,12 +2598,45 @@ static void btintel_pcie_perform_pldr(struct btintel_pcie_data *data)
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Issue a Function Level Reset and hand teardown/re-init off to the PCI
|
||||
* core via device_reprobe(), mirroring the PLDR path's contract.
|
||||
*
|
||||
* Caller must hold pci_lock_rescan_remove() and must have already
|
||||
* disabled interrupts and drained both rx_work and coredump_work.
|
||||
*/
|
||||
static int btintel_pcie_perform_flr(struct btintel_pcie_data *data)
|
||||
{
|
||||
struct pci_dev *pdev = data->pdev;
|
||||
int err;
|
||||
|
||||
/* pci_try_reset_function() avoids the device_lock ABBA against
|
||||
* btintel_pcie_remove(): .remove() runs with device_lock held and
|
||||
* then waits for this work via disable_work_sync(); the blocking
|
||||
* pci_reset_function() would deadlock by trying to re-acquire
|
||||
* device_lock here.
|
||||
*/
|
||||
err = pci_try_reset_function(pdev);
|
||||
if (err) {
|
||||
BT_ERR("Failed resetting the pcie device (%d)", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* device_reprobe() always detaches the driver first (running
|
||||
* .remove(), which frees 'data'); any re-probe failure leaves the
|
||||
* device unbound but 'data' is already gone, so just log it.
|
||||
*/
|
||||
if (device_reprobe(&pdev->dev))
|
||||
BT_ERR("BT reprobe failed for BDF:%s", pci_name(pdev));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void btintel_pcie_reset_work(struct work_struct *wk)
|
||||
{
|
||||
struct btintel_pcie_data *data =
|
||||
container_of(wk, struct btintel_pcie_data, reset_work);
|
||||
struct pci_dev *pdev = data->pdev;
|
||||
int err;
|
||||
|
||||
pci_lock_rescan_remove();
|
||||
|
||||
|
|
@ -2621,60 +2656,27 @@ static void btintel_pcie_reset_work(struct work_struct *wk)
|
|||
disable_work_sync(&data->coredump_work);
|
||||
|
||||
bt_dev_dbg(data->hdev, "Release bluetooth interface");
|
||||
|
||||
/* Both reset paths follow the same contract: on success they
|
||||
* destroy 'data' via device_reprobe() (a fresh probe re-INIT_WORKs
|
||||
* the coredump_work with disable count 0), so enable_work() must
|
||||
* NOT be called on the success path. Only the FLR path can fail
|
||||
* with 'data' still alive, in which case we balance the
|
||||
* disable_work_sync() above so a later successful reset is not
|
||||
* permanently blocked.
|
||||
*
|
||||
* pci_lock_rescan_remove() (held above) serializes against PCI
|
||||
* device addition/removal (hotplug), so no device can be added to
|
||||
* or removed from the bus list while this code runs.
|
||||
*/
|
||||
if (data->reset_type == BTINTEL_PCIE_IOSF_PRR_PLDR) {
|
||||
/* This function holds pci_lock_rescan_remove(), which acquires
|
||||
* pci_rescan_remove_lock. This mutex serializes against PCI device
|
||||
* addition/removal (hotplug), so no device can be added to or
|
||||
* removed from the bus list while this code runs.
|
||||
*
|
||||
* device_reprobe() inside btintel_pcie_perform_pldr() destroys
|
||||
* 'data' via .remove(); a fresh probe re-INIT_WORKs the
|
||||
* coredump_work with disable count 0, so we must not call
|
||||
* enable_work() on this path.
|
||||
*/
|
||||
btintel_pcie_perform_pldr(data);
|
||||
goto out;
|
||||
}
|
||||
btintel_pcie_release_hdev(data);
|
||||
|
||||
/* Use pci_try_reset_function() rather than pci_reset_function() to
|
||||
* avoid an ABBA deadlock against btintel_pcie_remove(): the PCI core
|
||||
* calls .remove() with device_lock held, and remove() then waits for
|
||||
* this work via cancel_work_sync(); pci_reset_function() would in
|
||||
* turn try to acquire the same device_lock, deadlocking both paths.
|
||||
*/
|
||||
err = pci_try_reset_function(pdev);
|
||||
if (err) {
|
||||
BT_ERR("Failed resetting the pcie device (%d)", err);
|
||||
goto out_enable;
|
||||
}
|
||||
if (btintel_pcie_perform_flr(data))
|
||||
enable_work(&data->coredump_work);
|
||||
|
||||
btintel_pcie_enable_interrupts(data);
|
||||
btintel_pcie_config_msix(data);
|
||||
|
||||
err = btintel_pcie_enable_bt(data);
|
||||
if (err) {
|
||||
BT_ERR("Failed to enable bluetooth hardware after reset (%d)",
|
||||
err);
|
||||
goto out_enable;
|
||||
}
|
||||
|
||||
btintel_pcie_reset_ia(data);
|
||||
btintel_pcie_start_rx(data);
|
||||
data->flags = 0;
|
||||
|
||||
err = btintel_pcie_setup_hdev(data);
|
||||
if (err) {
|
||||
BT_ERR("Failed registering hdev (%d)", err);
|
||||
goto out_enable;
|
||||
}
|
||||
|
||||
out_enable:
|
||||
/* Balance disable_work_sync() above on every exit. Leaving the
|
||||
* counter incremented on a failed reset would permanently disable
|
||||
* coredump_work even after a later successful reset.
|
||||
*/
|
||||
enable_work(&data->coredump_work);
|
||||
out:
|
||||
pci_dev_put(pdev);
|
||||
pci_unlock_rescan_remove();
|
||||
|
|
|
|||
|
|
@ -1267,6 +1267,12 @@ static int nxp_recv_fw_req_v3(struct hci_dev *hdev, struct sk_buff *skb)
|
|||
}
|
||||
|
||||
nxpdev->fw_dnld_v3_offset = offset - nxpdev->fw_v3_offset_correction;
|
||||
if (nxpdev->fw_dnld_v3_offset >= nxpdev->fw->size ||
|
||||
len > nxpdev->fw->size - nxpdev->fw_dnld_v3_offset) {
|
||||
bt_dev_err(hdev, "FW download out of bounds, ignoring request");
|
||||
len = 0;
|
||||
goto free_skb;
|
||||
}
|
||||
serdev_device_write_buf(nxpdev->serdev, nxpdev->fw->data +
|
||||
nxpdev->fw_dnld_v3_offset, len);
|
||||
|
||||
|
|
|
|||
|
|
@ -239,6 +239,8 @@ static int hci_uart_flush(struct hci_dev *hdev)
|
|||
|
||||
BT_DBG("hdev %p tty %p", hdev, tty);
|
||||
|
||||
disable_work_sync(&hu->write_work);
|
||||
|
||||
if (hu->tx_skb) {
|
||||
kfree_skb(hu->tx_skb); hu->tx_skb = NULL;
|
||||
}
|
||||
|
|
@ -254,6 +256,14 @@ static int hci_uart_flush(struct hci_dev *hdev)
|
|||
|
||||
percpu_up_read(&hu->proto_lock);
|
||||
|
||||
/* Resume TX. Also reschedule in case work was queued concurrently;
|
||||
* this may schedule write_work although there's nothing to do.
|
||||
*/
|
||||
enable_work(&hu->write_work);
|
||||
clear_bit(HCI_UART_SENDING, &hu->tx_state);
|
||||
if (test_bit(HCI_UART_TX_WAKEUP, &hu->tx_state))
|
||||
hci_uart_tx_wakeup(hu);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -271,12 +281,8 @@ static int hci_uart_open(struct hci_dev *hdev)
|
|||
/* Close device */
|
||||
static int hci_uart_close(struct hci_dev *hdev)
|
||||
{
|
||||
struct hci_uart *hu = hci_get_drvdata(hdev);
|
||||
|
||||
BT_DBG("hdev %p", hdev);
|
||||
|
||||
cancel_work_sync(&hu->write_work);
|
||||
|
||||
hci_uart_flush(hdev);
|
||||
hdev->flush = NULL;
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -3413,8 +3413,9 @@ static inline struct hci_iso_hdr *hci_iso_hdr(const struct sk_buff *skb)
|
|||
#define hci_iso_flags_pack(pb, ts) ((pb & 0x03) | ((ts & 0x01) << 2))
|
||||
|
||||
/* ISO data length and flags pack/unpack */
|
||||
#define hci_iso_data_len_pack(h, f) ((__u16) ((h) | ((f) << 14)))
|
||||
#define hci_iso_data_len(h) ((h) & 0x3fff)
|
||||
#define hci_iso_data_len_pack(h, f) ((__u16) (((h) & 0x0fff) | \
|
||||
(((f) & 0x3) << 14)))
|
||||
#define hci_iso_data_len(h) ((h) & 0x0fff)
|
||||
#define hci_iso_data_flags(h) ((h) >> 14)
|
||||
|
||||
/* codec transport types */
|
||||
|
|
|
|||
|
|
@ -985,6 +985,7 @@ enum {
|
|||
HCI_CONN_AUTH_FAILURE,
|
||||
HCI_CONN_PER_ADV,
|
||||
HCI_CONN_BIG_CREATED,
|
||||
HCI_CONN_CREATE,
|
||||
HCI_CONN_CREATE_CIS,
|
||||
HCI_CONN_CREATE_BIG_SYNC,
|
||||
HCI_CONN_BIG_SYNC,
|
||||
|
|
|
|||
|
|
@ -617,7 +617,8 @@ struct l2cap_chan {
|
|||
struct l2cap_ops {
|
||||
char *name;
|
||||
|
||||
struct l2cap_chan *(*new_connection) (struct l2cap_chan *chan);
|
||||
int (*new_connection)(struct l2cap_chan *chan,
|
||||
struct l2cap_chan *new_chan);
|
||||
int (*recv) (struct l2cap_chan * chan,
|
||||
struct sk_buff *skb);
|
||||
void (*teardown) (struct l2cap_chan *chan, int err);
|
||||
|
|
@ -882,9 +883,10 @@ static inline __u16 __next_seq(struct l2cap_chan *chan, __u16 seq)
|
|||
return (seq + 1) % (chan->tx_win_max + 1);
|
||||
}
|
||||
|
||||
static inline struct l2cap_chan *l2cap_chan_no_new_connection(struct l2cap_chan *chan)
|
||||
static inline int l2cap_chan_no_new_connection(struct l2cap_chan *chan,
|
||||
struct l2cap_chan *new_chan)
|
||||
{
|
||||
return NULL;
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static inline int l2cap_chan_no_recv(struct l2cap_chan *chan, struct sk_buff *skb)
|
||||
|
|
@ -961,7 +963,7 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len,
|
|||
void l2cap_chan_busy(struct l2cap_chan *chan, int busy);
|
||||
void l2cap_chan_rx_avail(struct l2cap_chan *chan, ssize_t rx_avail);
|
||||
int l2cap_chan_check_security(struct l2cap_chan *chan, bool initiator);
|
||||
void l2cap_chan_set_defaults(struct l2cap_chan *chan);
|
||||
void l2cap_chan_set_defaults(struct l2cap_chan *chan, struct l2cap_chan *pchan);
|
||||
int l2cap_ertm_init(struct l2cap_chan *chan);
|
||||
void l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan);
|
||||
void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan);
|
||||
|
|
|
|||
|
|
@ -632,7 +632,7 @@ static struct l2cap_chan *chan_create(void)
|
|||
if (!chan)
|
||||
return NULL;
|
||||
|
||||
l2cap_chan_set_defaults(chan);
|
||||
l2cap_chan_set_defaults(chan, NULL);
|
||||
|
||||
chan->chan_type = L2CAP_CHAN_CONN_ORIENTED;
|
||||
chan->mode = L2CAP_MODE_LE_FLOWCTL;
|
||||
|
|
@ -745,21 +745,6 @@ static inline void chan_ready_cb(struct l2cap_chan *chan)
|
|||
ifup(dev->netdev);
|
||||
}
|
||||
|
||||
static inline struct l2cap_chan *chan_new_conn_cb(struct l2cap_chan *pchan)
|
||||
{
|
||||
struct l2cap_chan *chan;
|
||||
|
||||
chan = chan_create();
|
||||
if (!chan)
|
||||
return NULL;
|
||||
|
||||
chan->ops = pchan->ops;
|
||||
|
||||
BT_DBG("chan %p pchan %p", chan, pchan);
|
||||
|
||||
return chan;
|
||||
}
|
||||
|
||||
static void unregister_dev(struct lowpan_btle_dev *dev)
|
||||
{
|
||||
struct hci_dev *hdev = READ_ONCE(dev->hdev);
|
||||
|
|
@ -797,20 +782,10 @@ static void chan_close_cb(struct l2cap_chan *chan)
|
|||
struct lowpan_btle_dev *dev = NULL;
|
||||
struct lowpan_peer *peer;
|
||||
int err = -ENOENT;
|
||||
bool last = false, remove = true;
|
||||
bool last = false;
|
||||
|
||||
BT_DBG("chan %p conn %p", chan, chan->conn);
|
||||
|
||||
if (chan->conn && chan->conn->hcon) {
|
||||
if (!is_bt_6lowpan(chan->conn->hcon))
|
||||
return;
|
||||
|
||||
/* If conn is set, then the netdev is also there and we should
|
||||
* not remove it.
|
||||
*/
|
||||
remove = false;
|
||||
}
|
||||
|
||||
spin_lock(&devices_lock);
|
||||
|
||||
list_for_each_entry_rcu(entry, &bt_6lowpan_devices, list) {
|
||||
|
|
@ -837,10 +812,8 @@ static void chan_close_cb(struct l2cap_chan *chan)
|
|||
|
||||
ifdown(dev->netdev);
|
||||
|
||||
if (remove) {
|
||||
INIT_WORK(&entry->delete_netdev, delete_netdev);
|
||||
schedule_work(&entry->delete_netdev);
|
||||
}
|
||||
INIT_WORK(&entry->delete_netdev, delete_netdev);
|
||||
schedule_work(&entry->delete_netdev);
|
||||
} else {
|
||||
spin_unlock(&devices_lock);
|
||||
}
|
||||
|
|
@ -901,7 +874,6 @@ static long chan_get_sndtimeo_cb(struct l2cap_chan *chan)
|
|||
|
||||
static const struct l2cap_ops bt_6lowpan_chan_ops = {
|
||||
.name = "L2CAP 6LoWPAN channel",
|
||||
.new_connection = chan_new_conn_cb,
|
||||
.recv = chan_recv_cb,
|
||||
.close = chan_close_cb,
|
||||
.state_change = chan_state_change_cb,
|
||||
|
|
@ -1029,16 +1001,19 @@ static int get_l2cap_conn(char *buf, bdaddr_t *addr, u8 *addr_type,
|
|||
|
||||
hci_dev_lock(hdev);
|
||||
hcon = hci_conn_hash_lookup_le(hdev, addr, le_addr_type);
|
||||
hci_dev_unlock(hdev);
|
||||
hci_dev_put(hdev);
|
||||
|
||||
if (!hcon)
|
||||
if (!hcon) {
|
||||
hci_dev_unlock(hdev);
|
||||
hci_dev_put(hdev);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
*conn = (struct l2cap_conn *)hcon->l2cap_data;
|
||||
*conn = l2cap_conn_hold_unless_zero(hcon->l2cap_data);
|
||||
|
||||
BT_DBG("conn %p dst %pMR type %u", *conn, &hcon->dst, hcon->dst_type);
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
hci_dev_put(hdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -1093,23 +1068,15 @@ static void disconnect_all_peers(void)
|
|||
} while (nchans);
|
||||
}
|
||||
|
||||
struct set_enable {
|
||||
struct work_struct work;
|
||||
bool flag;
|
||||
};
|
||||
|
||||
static void do_enable_set(struct work_struct *work)
|
||||
static void do_enable_set(bool flag)
|
||||
{
|
||||
struct set_enable *set_enable = container_of(work,
|
||||
struct set_enable, work);
|
||||
|
||||
if (!set_enable->flag || enable_6lowpan != set_enable->flag)
|
||||
if (!flag || enable_6lowpan != flag)
|
||||
/* Disconnect existing connections if 6lowpan is
|
||||
* disabled
|
||||
*/
|
||||
disconnect_all_peers();
|
||||
|
||||
enable_6lowpan = set_enable->flag;
|
||||
enable_6lowpan = flag;
|
||||
|
||||
mutex_lock(&set_lock);
|
||||
if (listen_chan) {
|
||||
|
|
@ -1121,22 +1088,11 @@ static void do_enable_set(struct work_struct *work)
|
|||
|
||||
listen_chan = bt_6lowpan_listen();
|
||||
mutex_unlock(&set_lock);
|
||||
|
||||
kfree(set_enable);
|
||||
}
|
||||
|
||||
static int lowpan_enable_set(void *data, u64 val)
|
||||
{
|
||||
struct set_enable *set_enable;
|
||||
|
||||
set_enable = kzalloc_obj(*set_enable);
|
||||
if (!set_enable)
|
||||
return -ENOMEM;
|
||||
|
||||
set_enable->flag = !!val;
|
||||
INIT_WORK(&set_enable->work, do_enable_set);
|
||||
|
||||
schedule_work(&set_enable->work);
|
||||
do_enable_set(!!val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1185,18 +1141,22 @@ static ssize_t lowpan_control_write(struct file *fp,
|
|||
if (conn) {
|
||||
struct lowpan_peer *peer;
|
||||
|
||||
if (!is_bt_6lowpan(conn->hcon))
|
||||
if (!is_bt_6lowpan(conn->hcon)) {
|
||||
l2cap_conn_put(conn);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
peer = lookup_peer(conn);
|
||||
if (peer) {
|
||||
BT_DBG("6LoWPAN connection already exists");
|
||||
l2cap_conn_put(conn);
|
||||
return -EALREADY;
|
||||
}
|
||||
|
||||
BT_DBG("conn %p dst %pMR type %d user %u", conn,
|
||||
&conn->hcon->dst, conn->hcon->dst_type,
|
||||
addr_type);
|
||||
l2cap_conn_put(conn);
|
||||
}
|
||||
|
||||
ret = bt_6lowpan_connect(&addr, addr_type);
|
||||
|
|
@ -1212,6 +1172,8 @@ static ssize_t lowpan_control_write(struct file *fp,
|
|||
return ret;
|
||||
|
||||
ret = bt_6lowpan_disconnect(conn, addr_type);
|
||||
if (conn)
|
||||
l2cap_conn_put(conn);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
|
|
|
|||
|
|
@ -305,7 +305,7 @@ struct sock *bt_accept_dequeue(struct sock *parent, struct socket *newsock)
|
|||
|
||||
restart:
|
||||
for (sk = bt_accept_get(parent, NULL); sk; sk = next) {
|
||||
/* Prevent early freeing of sk due to unlink and sock_kill */
|
||||
/* The reference from bt_accept_get() keeps sk alive. */
|
||||
lock_sock(sk);
|
||||
|
||||
/* Check sk has not already been unlinked via
|
||||
|
|
@ -321,13 +321,11 @@ struct sock *bt_accept_dequeue(struct sock *parent, struct socket *newsock)
|
|||
|
||||
next = bt_accept_get(parent, sk);
|
||||
|
||||
/* sk is safely in the parent list so reduce reference count */
|
||||
sock_put(sk);
|
||||
|
||||
/* FIXME: Is this check still needed */
|
||||
if (sk->sk_state == BT_CLOSED) {
|
||||
bt_accept_unlink(sk);
|
||||
release_sock(sk);
|
||||
sock_put(sk);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -337,16 +335,6 @@ struct sock *bt_accept_dequeue(struct sock *parent, struct socket *newsock)
|
|||
if (newsock)
|
||||
sock_graft(sk, newsock);
|
||||
|
||||
/* Hand the caller a reference taken while sk is
|
||||
* still locked. bt_accept_unlink() just dropped
|
||||
* the accept-queue reference; without this hold a
|
||||
* concurrent teardown (e.g. l2cap_conn_del() ->
|
||||
* l2cap_sock_kill()) could free sk between
|
||||
* release_sock() and the caller using it. Every
|
||||
* caller drops this with sock_put() when done.
|
||||
*/
|
||||
sock_hold(sk);
|
||||
|
||||
release_sock(sk);
|
||||
if (next)
|
||||
sock_put(next);
|
||||
|
|
@ -354,6 +342,7 @@ struct sock *bt_accept_dequeue(struct sock *parent, struct socket *newsock)
|
|||
}
|
||||
|
||||
release_sock(sk);
|
||||
sock_put(sk);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
|
|
|
|||
|
|
@ -559,14 +559,18 @@ static int bnep_session(void *arg)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static struct device *bnep_get_device(struct bnep_session *session)
|
||||
static struct l2cap_conn *bnep_get_conn(struct bnep_session *session)
|
||||
{
|
||||
struct l2cap_conn *conn = l2cap_pi(session->sock->sk)->chan->conn;
|
||||
struct l2cap_chan *chan = l2cap_pi(session->sock->sk)->chan;
|
||||
struct l2cap_conn *conn;
|
||||
|
||||
if (!conn || !conn->hcon)
|
||||
return NULL;
|
||||
l2cap_chan_lock(chan);
|
||||
conn = chan->conn;
|
||||
if (conn)
|
||||
l2cap_conn_get(conn);
|
||||
l2cap_chan_unlock(chan);
|
||||
|
||||
return &conn->hcon->dev;
|
||||
return conn;
|
||||
}
|
||||
|
||||
static const struct device_type bnep_type = {
|
||||
|
|
@ -578,6 +582,7 @@ int bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock)
|
|||
u32 valid_flags = BIT(BNEP_SETUP_RESPONSE);
|
||||
struct net_device *dev;
|
||||
struct bnep_session *s, *ss;
|
||||
struct l2cap_conn *conn = NULL;
|
||||
u8 dst[ETH_ALEN], src[ETH_ALEN];
|
||||
int err;
|
||||
|
||||
|
|
@ -637,10 +642,18 @@ int bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock)
|
|||
bnep_set_default_proto_filter(s);
|
||||
#endif
|
||||
|
||||
SET_NETDEV_DEV(dev, bnep_get_device(s));
|
||||
conn = bnep_get_conn(s);
|
||||
if (!conn) {
|
||||
err = -ENOTCONN;
|
||||
goto failed;
|
||||
}
|
||||
|
||||
SET_NETDEV_DEV(dev, &conn->hcon->dev);
|
||||
SET_NETDEV_DEVTYPE(dev, &bnep_type);
|
||||
|
||||
err = register_netdev(dev);
|
||||
l2cap_conn_put(conn);
|
||||
conn = NULL;
|
||||
if (err)
|
||||
goto failed;
|
||||
|
||||
|
|
@ -662,6 +675,8 @@ int bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock)
|
|||
return 0;
|
||||
|
||||
failed:
|
||||
if (conn)
|
||||
l2cap_conn_put(conn);
|
||||
up_write(&bnep_session_sem);
|
||||
free_netdev(dev);
|
||||
return err;
|
||||
|
|
|
|||
|
|
@ -3178,26 +3178,11 @@ int hci_abort_conn(struct hci_conn *conn, u8 reason)
|
|||
|
||||
conn->abort_reason = reason;
|
||||
|
||||
/* If the connection is pending check the command opcode since that
|
||||
* might be blocking on hci_cmd_sync_work while waiting its respective
|
||||
* event so we need to hci_cmd_sync_cancel to cancel it.
|
||||
*
|
||||
* hci_connect_le serializes the connection attempts so only one
|
||||
* connection can be in BT_CONNECT at time.
|
||||
/* Cancel the connect attempt. A return of 0 means the create command
|
||||
* was still queued and got dequeued, so there is nothing to disconnect.
|
||||
*/
|
||||
if (conn->state == BT_CONNECT && READ_ONCE(hdev->req_status) == HCI_REQ_PEND) {
|
||||
switch (hci_skb_event(hdev->sent_cmd)) {
|
||||
case HCI_EV_CONN_COMPLETE:
|
||||
case HCI_EV_LE_CONN_COMPLETE:
|
||||
case HCI_EV_LE_ENHANCED_CONN_COMPLETE:
|
||||
case HCI_EVT_LE_CIS_ESTABLISHED:
|
||||
hci_cmd_sync_cancel(hdev, ECANCELED);
|
||||
break;
|
||||
}
|
||||
/* Cancel connect attempt if still queued/pending */
|
||||
} else if (!hci_cancel_connect_sync(hdev, conn)) {
|
||||
if (!hci_cancel_connect_sync(hdev, conn))
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Run immediately if on cmd_sync_work since this may be called
|
||||
* as a result to MGMT_OP_DISCONNECT/MGMT_OP_UNPAIR which does
|
||||
|
|
|
|||
|
|
@ -6633,6 +6633,11 @@ 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;
|
||||
|
||||
|
|
@ -6645,6 +6650,7 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data)
|
|||
hdev->le_scan_type == LE_SCAN_ACTIVE &&
|
||||
!hci_dev_test_flag(hdev, HCI_LE_SIMULTANEOUS_ROLES)) {
|
||||
hci_conn_del(conn);
|
||||
hci_conn_put(conn);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
|
|
@ -6690,6 +6696,12 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data)
|
|||
&own_addr_type);
|
||||
if (err)
|
||||
goto done;
|
||||
|
||||
/* Mark create connection in flight so hci_cancel_connect_sync() can
|
||||
* cancel it while blocking on the connection complete event.
|
||||
*/
|
||||
set_bit(HCI_CONN_CREATE, &conn->flags);
|
||||
|
||||
/* Send command LE Extended Create Connection if supported */
|
||||
if (use_ext_conn(hdev)) {
|
||||
err = hci_le_ext_create_conn_sync(hdev, conn, own_addr_type);
|
||||
|
|
@ -6725,11 +6737,14 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data)
|
|||
conn->conn_timeout, NULL);
|
||||
|
||||
done:
|
||||
clear_bit(HCI_CONN_CREATE, &conn->flags);
|
||||
|
||||
if (err == -ETIMEDOUT)
|
||||
hci_le_connect_cancel_sync(hdev, conn, 0x00);
|
||||
|
||||
/* Re-enable advertising after the connection attempt is finished. */
|
||||
hci_resume_advertising_sync(hdev);
|
||||
hci_conn_put(conn);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
|
@ -7004,10 +7019,25 @@ static int hci_acl_create_conn_sync(struct hci_dev *hdev, void *data)
|
|||
else
|
||||
cp.role_switch = 0x00;
|
||||
|
||||
return __hci_cmd_sync_status_sk(hdev, HCI_OP_CREATE_CONN,
|
||||
sizeof(cp), &cp,
|
||||
HCI_EV_CONN_COMPLETE,
|
||||
conn->conn_timeout, NULL);
|
||||
/* 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.
|
||||
*/
|
||||
set_bit(HCI_CONN_CREATE, &conn->flags);
|
||||
|
||||
err = __hci_cmd_sync_status_sk(hdev, HCI_OP_CREATE_CONN,
|
||||
sizeof(cp), &cp,
|
||||
HCI_EV_CONN_COMPLETE,
|
||||
conn->conn_timeout, NULL);
|
||||
|
||||
clear_bit(HCI_CONN_CREATE, &conn->flags);
|
||||
hci_conn_put(conn);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int hci_connect_acl_sync(struct hci_dev *hdev, struct hci_conn *conn)
|
||||
|
|
@ -7059,22 +7089,97 @@ int hci_connect_le_sync(struct hci_dev *hdev, struct hci_conn *conn)
|
|||
return (err == -EEXIST) ? 0 : err;
|
||||
}
|
||||
|
||||
int hci_cancel_connect_sync(struct hci_dev *hdev, struct hci_conn *conn)
|
||||
static int hci_acl_cancel_create_conn_sync(struct hci_dev *hdev,
|
||||
struct hci_conn *conn)
|
||||
{
|
||||
if (conn->state != BT_OPEN)
|
||||
return -EINVAL;
|
||||
struct hci_cmd_sync_work_entry *entry;
|
||||
int err = -EBUSY;
|
||||
|
||||
switch (conn->type) {
|
||||
case ACL_LINK:
|
||||
return !hci_cmd_sync_dequeue_once(hdev,
|
||||
hci_acl_create_conn_sync,
|
||||
conn, NULL);
|
||||
case LE_LINK:
|
||||
return !hci_cmd_sync_dequeue_once(hdev, hci_le_create_conn_sync,
|
||||
conn, create_le_conn_complete);
|
||||
/* cmd_sync_work_lock makes the HCI_CONN_CREATE test and the cancel
|
||||
* atomic against the worker, which takes this lock to dequeue every
|
||||
* entry: while it is held no other command can become pending, so
|
||||
* hci_cmd_sync_cancel() cannot cancel an unrelated command.
|
||||
*/
|
||||
mutex_lock(&hdev->cmd_sync_work_lock);
|
||||
|
||||
/* In flight: this connection owns the pending request, cancel it. */
|
||||
if (test_bit(HCI_CONN_CREATE, &conn->flags)) {
|
||||
hci_cmd_sync_cancel(hdev, ECANCELED);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
return -ENOENT;
|
||||
/* Still queued: a successful dequeue means it never started, so there
|
||||
* is nothing to disconnect.
|
||||
*/
|
||||
entry = _hci_cmd_sync_lookup_entry(hdev, hci_acl_create_conn_sync, conn,
|
||||
NULL);
|
||||
if (entry) {
|
||||
_hci_cmd_sync_cancel_entry(hdev, entry, -ECANCELED);
|
||||
err = 0;
|
||||
}
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&hdev->cmd_sync_work_lock);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int hci_le_cancel_create_conn_sync(struct hci_dev *hdev,
|
||||
struct hci_conn *conn)
|
||||
{
|
||||
struct hci_cmd_sync_work_entry *entry;
|
||||
int err = -EBUSY;
|
||||
|
||||
/* cmd_sync_work_lock keeps the HCI_CONN_CREATE test and the cancel
|
||||
* atomic against the cmd_sync worker.
|
||||
*/
|
||||
mutex_lock(&hdev->cmd_sync_work_lock);
|
||||
|
||||
if (test_bit(HCI_CONN_CREATE, &conn->flags)) {
|
||||
hci_cmd_sync_cancel(hdev, ECANCELED);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
entry = _hci_cmd_sync_lookup_entry(hdev, hci_le_create_conn_sync, conn,
|
||||
create_le_conn_complete);
|
||||
if (entry) {
|
||||
_hci_cmd_sync_cancel_entry(hdev, entry, -ECANCELED);
|
||||
err = 0;
|
||||
}
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&hdev->cmd_sync_work_lock);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int hci_cis_cancel_create_conn_sync(struct hci_dev *hdev,
|
||||
struct hci_conn *conn)
|
||||
{
|
||||
/* LE Create CIS is shared by the whole CIG and cannot be dequeued
|
||||
* per-connection, so only an in-flight command can be cancelled.
|
||||
* cmd_sync_work_lock keeps the test and the cancel atomic against the
|
||||
* cmd_sync worker.
|
||||
*/
|
||||
mutex_lock(&hdev->cmd_sync_work_lock);
|
||||
|
||||
if (test_bit(HCI_CONN_CREATE_CIS, &conn->flags))
|
||||
hci_cmd_sync_cancel(hdev, ECANCELED);
|
||||
|
||||
mutex_unlock(&hdev->cmd_sync_work_lock);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
int hci_cancel_connect_sync(struct hci_dev *hdev, struct hci_conn *conn)
|
||||
{
|
||||
switch (conn->type) {
|
||||
case ACL_LINK:
|
||||
return hci_acl_cancel_create_conn_sync(hdev, conn);
|
||||
case LE_LINK:
|
||||
return hci_le_cancel_create_conn_sync(hdev, conn);
|
||||
case CIS_LINK:
|
||||
return hci_cis_cancel_create_conn_sync(hdev, conn);
|
||||
default:
|
||||
return -ENOENT;
|
||||
}
|
||||
}
|
||||
|
||||
int hci_le_conn_update_sync(struct hci_dev *hdev, struct hci_conn *conn,
|
||||
|
|
|
|||
|
|
@ -1590,6 +1590,7 @@ static void iso_conn_big_sync(struct sock *sk)
|
|||
{
|
||||
int err;
|
||||
struct hci_dev *hdev;
|
||||
struct iso_conn *conn;
|
||||
bdaddr_t src, dst;
|
||||
u8 src_type;
|
||||
|
||||
|
|
@ -1612,8 +1613,17 @@ static void iso_conn_big_sync(struct sock *sk)
|
|||
hci_dev_lock(hdev);
|
||||
lock_sock(sk);
|
||||
|
||||
/* The socket lock was dropped for hci_get_route(), so the connection
|
||||
* may have been torn down meanwhile: iso_chan_del() clears conn and
|
||||
* the broadcast teardown path can clear conn->hcon on its own. Check
|
||||
* both before dereferencing conn->hcon.
|
||||
*/
|
||||
conn = iso_pi(sk)->conn;
|
||||
if (!conn || !conn->hcon)
|
||||
goto unlock;
|
||||
|
||||
if (!test_and_set_bit(BT_SK_BIG_SYNC, &iso_pi(sk)->flags)) {
|
||||
err = hci_conn_big_create_sync(hdev, iso_pi(sk)->conn->hcon,
|
||||
err = hci_conn_big_create_sync(hdev, conn->hcon,
|
||||
&iso_pi(sk)->qos,
|
||||
iso_pi(sk)->sync_handle,
|
||||
iso_pi(sk)->bc_num_bis,
|
||||
|
|
@ -1622,6 +1632,7 @@ static void iso_conn_big_sync(struct sock *sk)
|
|||
bt_dev_err(hdev, "hci_big_create_sync: %d", err);
|
||||
}
|
||||
|
||||
unlock:
|
||||
release_sock(sk);
|
||||
hci_dev_unlock(hdev);
|
||||
hci_dev_put(hdev);
|
||||
|
|
@ -2529,7 +2540,7 @@ int iso_recv(struct hci_dev *hdev, u16 handle, struct sk_buff *skb, u16 flags)
|
|||
switch (pb) {
|
||||
case ISO_START:
|
||||
case ISO_SINGLE:
|
||||
if (conn->rx_len) {
|
||||
if (conn->rx_skb || conn->rx_len) {
|
||||
BT_ERR("Unexpected start frame (len %d)", skb->len);
|
||||
kfree_skb(conn->rx_skb);
|
||||
conn->rx_skb = NULL;
|
||||
|
|
@ -2610,12 +2621,14 @@ int iso_recv(struct hci_dev *hdev, u16 handle, struct sk_buff *skb, u16 flags)
|
|||
break;
|
||||
|
||||
case ISO_CONT:
|
||||
BT_DBG("Cont: frag len %d (expecting %d)", skb->len,
|
||||
case ISO_END:
|
||||
BT_DBG("%s: frag len %d (expecting %d)",
|
||||
(pb == ISO_END) ? "End" : "Cont", skb->len,
|
||||
conn->rx_len);
|
||||
|
||||
if (!conn->rx_len) {
|
||||
BT_ERR("Unexpected continuation frame (len %d)",
|
||||
skb->len);
|
||||
if (!conn->rx_skb) {
|
||||
BT_ERR("Unexpected ISO %s frame (len %d)",
|
||||
(pb == ISO_END) ? "End" : "Cont", skb->len);
|
||||
goto drop;
|
||||
}
|
||||
|
||||
|
|
@ -2631,17 +2644,9 @@ int iso_recv(struct hci_dev *hdev, u16 handle, struct sk_buff *skb, u16 flags)
|
|||
skb_copy_from_linear_data(skb, skb_put(conn->rx_skb, skb->len),
|
||||
skb->len);
|
||||
conn->rx_len -= skb->len;
|
||||
break;
|
||||
|
||||
case ISO_END:
|
||||
if (!conn->rx_len) {
|
||||
BT_ERR("Unexpected end frame (len %d)", skb->len);
|
||||
goto drop;
|
||||
}
|
||||
|
||||
skb_copy_from_linear_data(skb, skb_put(conn->rx_skb, skb->len),
|
||||
skb->len);
|
||||
conn->rx_len -= skb->len;
|
||||
if (pb == ISO_CONT)
|
||||
break;
|
||||
|
||||
if (!conn->rx_len) {
|
||||
struct sk_buff *rx_skb = conn->rx_skb;
|
||||
|
|
@ -2652,6 +2657,13 @@ int iso_recv(struct hci_dev *hdev, u16 handle, struct sk_buff *skb, u16 flags)
|
|||
*/
|
||||
conn->rx_skb = NULL;
|
||||
iso_recv_frame(conn, rx_skb);
|
||||
} else {
|
||||
BT_ERR("ISO fragment incomplete (len %d, expected %d)",
|
||||
skb->len, conn->rx_len);
|
||||
kfree_skb(conn->rx_skb);
|
||||
conn->rx_skb = NULL;
|
||||
conn->rx_len = 0;
|
||||
goto drop;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -522,7 +522,10 @@ void l2cap_chan_put(struct l2cap_chan *c)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(l2cap_chan_put);
|
||||
|
||||
void l2cap_chan_set_defaults(struct l2cap_chan *chan)
|
||||
/* Initialise @chan with default values, inheriting from the parent channel
|
||||
* @pchan when it is given.
|
||||
*/
|
||||
void l2cap_chan_set_defaults(struct l2cap_chan *chan, struct l2cap_chan *pchan)
|
||||
{
|
||||
chan->fcs = L2CAP_FCS_CRC16;
|
||||
chan->max_tx = L2CAP_DEFAULT_MAX_TX;
|
||||
|
|
@ -536,6 +539,31 @@ void l2cap_chan_set_defaults(struct l2cap_chan *chan)
|
|||
chan->retrans_timeout = L2CAP_DEFAULT_RETRANS_TO;
|
||||
chan->monitor_timeout = L2CAP_DEFAULT_MONITOR_TO;
|
||||
|
||||
if (pchan) {
|
||||
BT_DBG("chan %p pchan %p", chan, pchan);
|
||||
|
||||
chan->chan_type = pchan->chan_type;
|
||||
chan->imtu = pchan->imtu;
|
||||
chan->omtu = pchan->omtu;
|
||||
chan->mode = pchan->mode;
|
||||
chan->fcs = pchan->fcs;
|
||||
chan->max_tx = pchan->max_tx;
|
||||
chan->tx_win = pchan->tx_win;
|
||||
chan->tx_win_max = pchan->tx_win_max;
|
||||
chan->sec_level = pchan->sec_level;
|
||||
chan->conf_state = pchan->conf_state;
|
||||
chan->flags = pchan->flags;
|
||||
chan->tx_credits = pchan->tx_credits;
|
||||
chan->rx_credits = pchan->rx_credits;
|
||||
|
||||
if (chan->chan_type == L2CAP_CHAN_FIXED) {
|
||||
chan->scid = pchan->scid;
|
||||
chan->dcid = pchan->scid;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
chan->conf_state = 0;
|
||||
set_bit(CONF_NOT_COMPLETE, &chan->conf_state);
|
||||
|
||||
|
|
@ -1775,19 +1803,13 @@ static void l2cap_conn_del(struct hci_conn *hcon, int err)
|
|||
disable_delayed_work_sync(&conn->info_timer);
|
||||
disable_delayed_work_sync(&conn->id_addr_timer);
|
||||
|
||||
cancel_work_sync(&conn->pending_rx_work);
|
||||
|
||||
mutex_lock(&conn->lock);
|
||||
|
||||
kfree_skb(conn->rx_skb);
|
||||
|
||||
skb_queue_purge(&conn->pending_rx);
|
||||
|
||||
/* We can not call flush_work(&conn->pending_rx_work) here since we
|
||||
* might block if we are running on a worker from the same workqueue
|
||||
* pending_rx_work is waiting on.
|
||||
*/
|
||||
if (work_pending(&conn->pending_rx_work))
|
||||
cancel_work_sync(&conn->pending_rx_work);
|
||||
|
||||
ida_destroy(&conn->tx_ida);
|
||||
|
||||
l2cap_unregister_all_users(conn);
|
||||
|
|
@ -3051,13 +3073,24 @@ static struct sk_buff *l2cap_build_cmd(struct l2cap_conn *conn, u8 code,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static inline int l2cap_get_conf_opt(void **ptr, int *type, int *olen,
|
||||
unsigned long *val)
|
||||
static inline int l2cap_get_conf_opt(void **ptr, void *end, int *type,
|
||||
int *olen, unsigned long *val)
|
||||
{
|
||||
struct l2cap_conf_opt *opt = *ptr;
|
||||
int len;
|
||||
|
||||
/* opt->len is attacker-controlled. Validate that the full option
|
||||
* (header + value) actually fits in the buffer before touching
|
||||
* opt->val, otherwise the switch below reads past the end of the
|
||||
* caller's buffer.
|
||||
*/
|
||||
if (end - *ptr < L2CAP_CONF_OPT_SIZE)
|
||||
return -EINVAL;
|
||||
|
||||
len = L2CAP_CONF_OPT_SIZE + opt->len;
|
||||
if (end - *ptr < len)
|
||||
return -EINVAL;
|
||||
|
||||
*ptr += len;
|
||||
|
||||
*type = opt->type;
|
||||
|
|
@ -3429,6 +3462,7 @@ static int l2cap_parse_conf_req(struct l2cap_chan *chan, void *data, size_t data
|
|||
void *ptr = rsp->data;
|
||||
void *endptr = data + data_size;
|
||||
void *req = chan->conf_req;
|
||||
void *req_end = req + chan->conf_len;
|
||||
int len = chan->conf_len;
|
||||
int type, hint, olen;
|
||||
unsigned long val;
|
||||
|
|
@ -3442,9 +3476,11 @@ static int l2cap_parse_conf_req(struct l2cap_chan *chan, void *data, size_t data
|
|||
BT_DBG("chan %p", chan);
|
||||
|
||||
while (len >= L2CAP_CONF_OPT_SIZE) {
|
||||
len -= l2cap_get_conf_opt(&req, &type, &olen, &val);
|
||||
if (len < 0)
|
||||
int ret = l2cap_get_conf_opt(&req, req_end, &type, &olen, &val);
|
||||
|
||||
if (ret < 0)
|
||||
break;
|
||||
len -= ret;
|
||||
|
||||
hint = type & L2CAP_CONF_HINT;
|
||||
type &= L2CAP_CONF_MASK;
|
||||
|
|
@ -3672,6 +3708,7 @@ static int l2cap_parse_conf_rsp(struct l2cap_chan *chan, void *rsp, int len,
|
|||
struct l2cap_conf_req *req = data;
|
||||
void *ptr = req->data;
|
||||
void *endptr = data + size;
|
||||
void *rsp_end = rsp + len;
|
||||
int type, olen;
|
||||
unsigned long val;
|
||||
struct l2cap_conf_rfc rfc = { .mode = L2CAP_MODE_BASIC };
|
||||
|
|
@ -3680,9 +3717,11 @@ static int l2cap_parse_conf_rsp(struct l2cap_chan *chan, void *rsp, int len,
|
|||
BT_DBG("chan %p, rsp %p, len %d, req %p", chan, rsp, len, data);
|
||||
|
||||
while (len >= L2CAP_CONF_OPT_SIZE) {
|
||||
len -= l2cap_get_conf_opt(&rsp, &type, &olen, &val);
|
||||
if (len < 0)
|
||||
int ret = l2cap_get_conf_opt(&rsp, rsp_end, &type, &olen, &val);
|
||||
|
||||
if (ret < 0)
|
||||
break;
|
||||
len -= ret;
|
||||
|
||||
switch (type) {
|
||||
case L2CAP_CONF_MTU:
|
||||
|
|
@ -3933,6 +3972,7 @@ static void l2cap_conf_rfc_get(struct l2cap_chan *chan, void *rsp, int len)
|
|||
{
|
||||
int type, olen;
|
||||
unsigned long val;
|
||||
void *rsp_end = rsp + len;
|
||||
/* Use sane default values in case a misbehaving remote device
|
||||
* did not send an RFC or extended window size option.
|
||||
*/
|
||||
|
|
@ -3951,9 +3991,11 @@ static void l2cap_conf_rfc_get(struct l2cap_chan *chan, void *rsp, int len)
|
|||
return;
|
||||
|
||||
while (len >= L2CAP_CONF_OPT_SIZE) {
|
||||
len -= l2cap_get_conf_opt(&rsp, &type, &olen, &val);
|
||||
if (len < 0)
|
||||
int ret = l2cap_get_conf_opt(&rsp, rsp_end, &type, &olen, &val);
|
||||
|
||||
if (ret < 0)
|
||||
break;
|
||||
len -= ret;
|
||||
|
||||
switch (type) {
|
||||
case L2CAP_CONF_RFC:
|
||||
|
|
@ -4010,6 +4052,38 @@ static inline int l2cap_command_rej(struct l2cap_conn *conn,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* Allocate and initialise a channel for an incoming connection.
|
||||
*
|
||||
* The channel inherits its configuration from @pchan and is linked into @conn
|
||||
* before ->new_connection() runs, so the conn list reference keeps it alive if
|
||||
* the callback exposes it (e.g. via the socket accept queue) before this
|
||||
* returns. The l2cap_chan_create() reference is taken over by the subsystem on
|
||||
* success and dropped here on failure.
|
||||
*/
|
||||
static struct l2cap_chan *l2cap_new_connection(struct l2cap_conn *conn,
|
||||
struct l2cap_chan *pchan)
|
||||
{
|
||||
struct l2cap_chan *chan;
|
||||
|
||||
chan = l2cap_chan_create();
|
||||
if (!chan)
|
||||
return NULL;
|
||||
|
||||
l2cap_chan_set_defaults(chan, pchan);
|
||||
chan->ops = pchan->ops;
|
||||
|
||||
__l2cap_chan_add(conn, chan);
|
||||
|
||||
if (pchan->ops->new_connection &&
|
||||
pchan->ops->new_connection(pchan, chan) < 0) {
|
||||
l2cap_chan_del(chan, 0);
|
||||
l2cap_chan_put(chan);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return chan;
|
||||
}
|
||||
|
||||
static void l2cap_connect(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd,
|
||||
u8 *data, u8 rsp_code)
|
||||
{
|
||||
|
|
@ -4056,7 +4130,7 @@ static void l2cap_connect(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd,
|
|||
goto response;
|
||||
}
|
||||
|
||||
chan = pchan->ops->new_connection(pchan);
|
||||
chan = l2cap_new_connection(conn, pchan);
|
||||
if (!chan)
|
||||
goto response;
|
||||
|
||||
|
|
@ -4074,8 +4148,6 @@ static void l2cap_connect(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd,
|
|||
chan->psm = psm;
|
||||
chan->dcid = scid;
|
||||
|
||||
__l2cap_chan_add(conn, chan);
|
||||
|
||||
dcid = chan->scid;
|
||||
|
||||
__set_chan_timer(chan, chan->ops->get_sndtimeo(chan));
|
||||
|
|
@ -4807,6 +4879,7 @@ static void l2cap_put_ident(struct l2cap_conn *conn, u8 code, u8 id)
|
|||
case L2CAP_ECHO_RSP:
|
||||
case L2CAP_INFO_RSP:
|
||||
case L2CAP_CONN_PARAM_UPDATE_RSP:
|
||||
case L2CAP_LE_CONN_RSP:
|
||||
case L2CAP_ECRED_CONN_RSP:
|
||||
case L2CAP_ECRED_RECONF_RSP:
|
||||
/* First do a lookup since the remote may send bogus ids that
|
||||
|
|
@ -4958,7 +5031,7 @@ static int l2cap_le_connect_req(struct l2cap_conn *conn,
|
|||
goto response_unlock;
|
||||
}
|
||||
|
||||
chan = pchan->ops->new_connection(pchan);
|
||||
chan = l2cap_new_connection(conn, pchan);
|
||||
if (!chan) {
|
||||
result = L2CAP_CR_LE_NO_MEM;
|
||||
goto response_unlock;
|
||||
|
|
@ -4973,8 +5046,6 @@ static int l2cap_le_connect_req(struct l2cap_conn *conn,
|
|||
chan->omtu = mtu;
|
||||
chan->remote_mps = mps;
|
||||
|
||||
__l2cap_chan_add(conn, chan);
|
||||
|
||||
l2cap_le_flowctl_init(chan, __le16_to_cpu(req->credits));
|
||||
|
||||
dcid = chan->scid;
|
||||
|
|
@ -5182,7 +5253,7 @@ static inline int l2cap_ecred_conn_req(struct l2cap_conn *conn,
|
|||
continue;
|
||||
}
|
||||
|
||||
chan = pchan->ops->new_connection(pchan);
|
||||
chan = l2cap_new_connection(conn, pchan);
|
||||
if (!chan) {
|
||||
result = L2CAP_CR_LE_NO_MEM;
|
||||
continue;
|
||||
|
|
@ -5197,8 +5268,6 @@ static inline int l2cap_ecred_conn_req(struct l2cap_conn *conn,
|
|||
chan->omtu = mtu;
|
||||
chan->remote_mps = mps;
|
||||
|
||||
__l2cap_chan_add(conn, chan);
|
||||
|
||||
l2cap_ecred_init(chan, __le16_to_cpu(req->credits));
|
||||
|
||||
/* Init response */
|
||||
|
|
@ -6704,6 +6773,7 @@ static void l2cap_chan_le_send_credits(struct l2cap_chan *chan)
|
|||
struct l2cap_conn *conn = chan->conn;
|
||||
struct l2cap_le_credits pkt;
|
||||
u16 return_credits = l2cap_le_rx_credits(chan);
|
||||
int ident;
|
||||
|
||||
if (chan->mode != L2CAP_MODE_LE_FLOWCTL &&
|
||||
chan->mode != L2CAP_MODE_EXT_FLOWCTL)
|
||||
|
|
@ -6721,9 +6791,18 @@ static void l2cap_chan_le_send_credits(struct l2cap_chan *chan)
|
|||
pkt.cid = cpu_to_le16(chan->scid);
|
||||
pkt.credits = cpu_to_le16(return_credits);
|
||||
|
||||
chan->ident = l2cap_get_ident(conn);
|
||||
ident = l2cap_get_ident(conn);
|
||||
|
||||
l2cap_send_cmd(conn, chan->ident, L2CAP_LE_CREDITS, sizeof(pkt), &pkt);
|
||||
l2cap_send_cmd(conn, ident, L2CAP_LE_CREDITS, sizeof(pkt), &pkt);
|
||||
|
||||
/* L2CAP_LE_CREDITS has no response so the ident is never released by
|
||||
* l2cap_put_ident() - release it right away, otherwise the tx_ida
|
||||
* range is exhausted after 254 packets and from then on credits are
|
||||
* sent with the invalid ident 0, which some remote stacks ignore,
|
||||
* stalling the channel.
|
||||
*/
|
||||
if (ident > 0)
|
||||
ida_free(&conn->tx_ida, ident);
|
||||
}
|
||||
|
||||
void l2cap_chan_rx_avail(struct l2cap_chan *chan, ssize_t rx_avail)
|
||||
|
|
@ -7478,14 +7557,12 @@ static void l2cap_connect_cfm(struct hci_conn *hcon, u8 status)
|
|||
goto next;
|
||||
|
||||
l2cap_chan_lock(pchan);
|
||||
chan = pchan->ops->new_connection(pchan);
|
||||
chan = l2cap_new_connection(conn, pchan);
|
||||
if (chan) {
|
||||
bacpy(&chan->src, &hcon->src);
|
||||
bacpy(&chan->dst, &hcon->dst);
|
||||
chan->src_type = bdaddr_src_type(hcon);
|
||||
chan->dst_type = dst_type;
|
||||
|
||||
__l2cap_chan_add(conn, chan);
|
||||
}
|
||||
|
||||
l2cap_chan_unlock(pchan);
|
||||
|
|
@ -7702,6 +7779,7 @@ struct l2cap_conn *l2cap_conn_hold_unless_zero(struct l2cap_conn *c)
|
|||
|
||||
return c;
|
||||
}
|
||||
EXPORT_SYMBOL(l2cap_conn_hold_unless_zero);
|
||||
|
||||
int l2cap_recv_acldata(struct hci_dev *hdev, u16 handle,
|
||||
struct sk_buff *skb, u16 flags)
|
||||
|
|
|
|||
|
|
@ -43,7 +43,8 @@ static struct bt_sock_list l2cap_sk_list = {
|
|||
static const struct proto_ops l2cap_sock_ops;
|
||||
static void l2cap_sock_init(struct sock *sk, struct sock *parent);
|
||||
static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock,
|
||||
int proto, gfp_t prio, int kern);
|
||||
int proto, gfp_t prio, int kern,
|
||||
struct l2cap_chan *chan);
|
||||
static void l2cap_sock_cleanup_listen(struct sock *parent);
|
||||
|
||||
bool l2cap_is_socket(struct socket *sock)
|
||||
|
|
@ -1284,6 +1285,23 @@ static int l2cap_sock_recvmsg(struct socket *sock, struct msghdr *msg,
|
|||
return err;
|
||||
}
|
||||
|
||||
/* Release the sock's ref on chan and clear the pointer so that the ref is
|
||||
* dropped exactly once even if both l2cap_sock_kill() and
|
||||
* l2cap_sock_destruct() run. Setting chan->data to NULL first stops any other
|
||||
* task from dereferencing the now-dead sock pointer.
|
||||
*/
|
||||
static void l2cap_sock_put_chan(struct sock *sk)
|
||||
{
|
||||
struct l2cap_chan *chan = l2cap_pi(sk)->chan;
|
||||
|
||||
if (!chan)
|
||||
return;
|
||||
|
||||
chan->data = NULL;
|
||||
l2cap_pi(sk)->chan = NULL;
|
||||
l2cap_chan_put(chan);
|
||||
}
|
||||
|
||||
/* Kill socket (only if zapped and orphan)
|
||||
* Must be called on unlocked socket, with l2cap channel lock.
|
||||
*/
|
||||
|
|
@ -1294,13 +1312,9 @@ static void l2cap_sock_kill(struct sock *sk)
|
|||
|
||||
BT_DBG("sk %p state %s", sk, state_to_string(sk->sk_state));
|
||||
|
||||
/* Sock is dead, so set chan data to NULL, avoid other task use invalid
|
||||
* sock pointer.
|
||||
*/
|
||||
l2cap_pi(sk)->chan->data = NULL;
|
||||
/* Kill poor orphan */
|
||||
l2cap_sock_put_chan(sk);
|
||||
|
||||
l2cap_chan_put(l2cap_pi(sk)->chan);
|
||||
/* Kill poor orphan */
|
||||
sock_set_flag(sk, SOCK_DEAD);
|
||||
sock_put(sk);
|
||||
}
|
||||
|
|
@ -1492,8 +1506,8 @@ static void l2cap_sock_cleanup_listen(struct sock *parent)
|
|||
|
||||
/* Close not yet accepted channels.
|
||||
*
|
||||
* bt_accept_dequeue() now returns sk with an extra reference held
|
||||
* (taken while sk was still locked) so a concurrent l2cap_conn_del()
|
||||
* bt_accept_dequeue() returns sk with its temporary queue-walk
|
||||
* reference held, so a concurrent l2cap_conn_del()
|
||||
* -> l2cap_sock_kill() cannot free sk under us.
|
||||
*
|
||||
* cleanup_listen() runs under the parent sk lock, so unlike
|
||||
|
|
@ -1543,12 +1557,13 @@ static void l2cap_sock_cleanup_listen(struct sock *parent)
|
|||
}
|
||||
}
|
||||
|
||||
static struct l2cap_chan *l2cap_sock_new_connection_cb(struct l2cap_chan *chan)
|
||||
static int l2cap_sock_new_connection_cb(struct l2cap_chan *chan,
|
||||
struct l2cap_chan *new_chan)
|
||||
{
|
||||
struct sock *sk, *parent = chan->data;
|
||||
|
||||
if (!parent)
|
||||
return NULL;
|
||||
return -EINVAL;
|
||||
|
||||
lock_sock(parent);
|
||||
|
||||
|
|
@ -1556,25 +1571,28 @@ static struct l2cap_chan *l2cap_sock_new_connection_cb(struct l2cap_chan *chan)
|
|||
if (sk_acceptq_is_full(parent)) {
|
||||
BT_DBG("backlog full %d", parent->sk_ack_backlog);
|
||||
release_sock(parent);
|
||||
return NULL;
|
||||
return -ENOBUFS;
|
||||
}
|
||||
|
||||
sk = l2cap_sock_alloc(sock_net(parent), NULL, BTPROTO_L2CAP,
|
||||
GFP_ATOMIC, 0);
|
||||
GFP_ATOMIC, 0, new_chan);
|
||||
if (!sk) {
|
||||
release_sock(parent);
|
||||
return NULL;
|
||||
}
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
bt_sock_reclassify_lock(sk, BTPROTO_L2CAP);
|
||||
|
||||
l2cap_sock_init(sk, parent);
|
||||
|
||||
/* The conn list reference taken by l2cap_new_connection() keeps new_chan
|
||||
* alive once release_sock() lets another task free this socket.
|
||||
*/
|
||||
bt_accept_enqueue(parent, sk, false);
|
||||
|
||||
release_sock(parent);
|
||||
|
||||
return l2cap_pi(sk)->chan;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l2cap_sock_recv_cb(struct l2cap_chan *chan, struct sk_buff *skb)
|
||||
|
|
@ -1871,10 +1889,7 @@ static void l2cap_sock_destruct(struct sock *sk)
|
|||
|
||||
BT_DBG("sk %p", sk);
|
||||
|
||||
if (l2cap_pi(sk)->chan) {
|
||||
l2cap_pi(sk)->chan->data = NULL;
|
||||
l2cap_chan_put(l2cap_pi(sk)->chan);
|
||||
}
|
||||
l2cap_sock_put_chan(sk);
|
||||
|
||||
list_for_each_entry_safe(rx_busy, next, &l2cap_pi(sk)->rx_busy, list) {
|
||||
kfree_skb(rx_busy->skb);
|
||||
|
|
@ -1907,30 +1922,12 @@ static void l2cap_sock_init(struct sock *sk, struct sock *parent)
|
|||
BT_DBG("sk %p", sk);
|
||||
|
||||
if (parent) {
|
||||
struct l2cap_chan *pchan = l2cap_pi(parent)->chan;
|
||||
|
||||
sk->sk_type = parent->sk_type;
|
||||
bt_sk(sk)->flags = bt_sk(parent)->flags;
|
||||
|
||||
chan->chan_type = pchan->chan_type;
|
||||
chan->imtu = pchan->imtu;
|
||||
chan->omtu = pchan->omtu;
|
||||
chan->conf_state = pchan->conf_state;
|
||||
chan->mode = pchan->mode;
|
||||
chan->fcs = pchan->fcs;
|
||||
chan->max_tx = pchan->max_tx;
|
||||
chan->tx_win = pchan->tx_win;
|
||||
chan->tx_win_max = pchan->tx_win_max;
|
||||
chan->sec_level = pchan->sec_level;
|
||||
chan->flags = pchan->flags;
|
||||
chan->tx_credits = pchan->tx_credits;
|
||||
chan->rx_credits = pchan->rx_credits;
|
||||
|
||||
if (chan->chan_type == L2CAP_CHAN_FIXED) {
|
||||
chan->scid = pchan->scid;
|
||||
chan->dcid = pchan->scid;
|
||||
}
|
||||
|
||||
/* Channel configuration is inherited from the parent by
|
||||
* l2cap_new_connection().
|
||||
*/
|
||||
security_sk_clone(parent, sk);
|
||||
} else {
|
||||
switch (sk->sk_type) {
|
||||
|
|
@ -1956,7 +1953,7 @@ static void l2cap_sock_init(struct sock *sk, struct sock *parent)
|
|||
chan->mode = L2CAP_MODE_BASIC;
|
||||
}
|
||||
|
||||
l2cap_chan_set_defaults(chan);
|
||||
l2cap_chan_set_defaults(chan, NULL);
|
||||
}
|
||||
|
||||
/* Default config options */
|
||||
|
|
@ -1975,10 +1972,10 @@ static struct proto l2cap_proto = {
|
|||
};
|
||||
|
||||
static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock,
|
||||
int proto, gfp_t prio, int kern)
|
||||
int proto, gfp_t prio, int kern,
|
||||
struct l2cap_chan *chan)
|
||||
{
|
||||
struct sock *sk;
|
||||
struct l2cap_chan *chan;
|
||||
|
||||
sk = bt_sock_alloc(net, sock, &l2cap_proto, proto, prio, kern);
|
||||
if (!sk)
|
||||
|
|
@ -1989,16 +1986,7 @@ static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock,
|
|||
|
||||
INIT_LIST_HEAD(&l2cap_pi(sk)->rx_busy);
|
||||
|
||||
chan = l2cap_chan_create();
|
||||
if (!chan) {
|
||||
sk_free(sk);
|
||||
if (sock)
|
||||
sock->sk = NULL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
l2cap_chan_hold(chan);
|
||||
|
||||
/* The sock takes ownership of the caller's reference on chan. */
|
||||
l2cap_pi(sk)->chan = chan;
|
||||
|
||||
return sk;
|
||||
|
|
@ -2008,6 +1996,7 @@ static int l2cap_sock_create(struct net *net, struct socket *sock, int protocol,
|
|||
int kern)
|
||||
{
|
||||
struct sock *sk;
|
||||
struct l2cap_chan *chan;
|
||||
|
||||
BT_DBG("sock %p", sock);
|
||||
|
||||
|
|
@ -2022,10 +2011,16 @@ static int l2cap_sock_create(struct net *net, struct socket *sock, int protocol,
|
|||
|
||||
sock->ops = &l2cap_sock_ops;
|
||||
|
||||
sk = l2cap_sock_alloc(net, sock, protocol, GFP_ATOMIC, kern);
|
||||
if (!sk)
|
||||
chan = l2cap_chan_create();
|
||||
if (!chan)
|
||||
return -ENOMEM;
|
||||
|
||||
sk = l2cap_sock_alloc(net, sock, protocol, GFP_ATOMIC, kern, chan);
|
||||
if (!sk) {
|
||||
l2cap_chan_put(chan);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
l2cap_sock_init(sk, NULL);
|
||||
bt_sock_link(&l2cap_sk_list, sk);
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -5375,6 +5375,8 @@ static void mgmt_add_adv_patterns_monitor_complete(struct hci_dev *hdev,
|
|||
if (monitor->state == ADV_MONITOR_STATE_NOT_REGISTERED)
|
||||
monitor->state = ADV_MONITOR_STATE_REGISTERED;
|
||||
hci_update_passive_scan(hdev);
|
||||
} else {
|
||||
hci_free_adv_monitor(hdev, monitor);
|
||||
}
|
||||
|
||||
mgmt_cmd_complete(cmd->sk, cmd->hdev->id, cmd->opcode,
|
||||
|
|
@ -7658,6 +7660,8 @@ static void add_device_complete(struct hci_dev *hdev, void *data, int err)
|
|||
if (!err) {
|
||||
struct hci_conn_params *params;
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
params = hci_conn_params_lookup(hdev, &cp->addr.bdaddr,
|
||||
le_addr_type(cp->addr.type));
|
||||
|
||||
|
|
@ -7666,6 +7670,7 @@ static void add_device_complete(struct hci_dev *hdev, void *data, int err)
|
|||
device_flags_changed(NULL, hdev, &cp->addr.bdaddr,
|
||||
cp->addr.type, hdev->conn_flags,
|
||||
params ? params->flags : 0);
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
mgmt_cmd_complete(cmd->sk, hdev->id, MGMT_OP_ADD_DEVICE,
|
||||
|
|
|
|||
|
|
@ -291,7 +291,7 @@ static int msft_le_monitor_advertisement_cb(struct hci_dev *hdev, u16 opcode,
|
|||
monitor->state = ADV_MONITOR_STATE_OFFLOADED;
|
||||
|
||||
unlock:
|
||||
if (status)
|
||||
if (status && msft->resuming)
|
||||
hci_free_adv_monitor(hdev, monitor);
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
|
|
|
|||
|
|
@ -570,10 +570,23 @@ static void __sco_sock_close(struct sock *sk)
|
|||
/* Must be called on unlocked socket. */
|
||||
static void sco_sock_close(struct sock *sk)
|
||||
{
|
||||
struct sco_conn *conn;
|
||||
|
||||
lock_sock(sk);
|
||||
conn = sco_pi(sk)->conn;
|
||||
if (conn)
|
||||
sco_conn_hold(conn);
|
||||
release_sock(sk);
|
||||
|
||||
if (conn)
|
||||
disable_delayed_work_sync(&conn->timeout_work);
|
||||
|
||||
lock_sock(sk);
|
||||
sco_sock_clear_timer(sk);
|
||||
__sco_sock_close(sk);
|
||||
release_sock(sk);
|
||||
|
||||
if (conn)
|
||||
sco_conn_put(conn);
|
||||
}
|
||||
|
||||
static void sco_sock_init(struct sock *sk, struct sock *parent)
|
||||
|
|
|
|||
|
|
@ -3201,34 +3201,19 @@ static const struct l2cap_ops smp_chan_ops = {
|
|||
.get_sndtimeo = l2cap_chan_no_get_sndtimeo,
|
||||
};
|
||||
|
||||
static inline struct l2cap_chan *smp_new_conn_cb(struct l2cap_chan *pchan)
|
||||
static inline int smp_new_conn_cb(struct l2cap_chan *chan,
|
||||
struct l2cap_chan *new_chan)
|
||||
{
|
||||
struct l2cap_chan *chan;
|
||||
|
||||
BT_DBG("pchan %p", pchan);
|
||||
|
||||
chan = l2cap_chan_create();
|
||||
if (!chan)
|
||||
return NULL;
|
||||
|
||||
chan->chan_type = pchan->chan_type;
|
||||
chan->ops = &smp_chan_ops;
|
||||
chan->scid = pchan->scid;
|
||||
chan->dcid = chan->scid;
|
||||
chan->imtu = pchan->imtu;
|
||||
chan->omtu = pchan->omtu;
|
||||
chan->mode = pchan->mode;
|
||||
new_chan->ops = &smp_chan_ops;
|
||||
|
||||
/* Other L2CAP channels may request SMP routines in order to
|
||||
* change the security level. This means that the SMP channel
|
||||
* lock must be considered in its own category to avoid lockdep
|
||||
* warnings.
|
||||
*/
|
||||
atomic_set(&chan->nesting, L2CAP_NESTING_SMP);
|
||||
atomic_set(&new_chan->nesting, L2CAP_NESTING_SMP);
|
||||
|
||||
BT_DBG("created chan %p", chan);
|
||||
|
||||
return chan;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct l2cap_ops smp_root_chan_ops = {
|
||||
|
|
@ -3288,7 +3273,7 @@ static struct l2cap_chan *smp_add_cid(struct hci_dev *hdev, u16 cid)
|
|||
|
||||
l2cap_add_scid(chan, cid);
|
||||
|
||||
l2cap_chan_set_defaults(chan);
|
||||
l2cap_chan_set_defaults(chan, NULL);
|
||||
|
||||
if (cid == L2CAP_CID_SMP) {
|
||||
u8 bdaddr_type;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user