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:
Paolo Abeni 2026-07-08 11:10:21 +02:00
commit 5258b4fdae
19 changed files with 469 additions and 303 deletions

View File

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

View File

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

View File

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

View File

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

View File

@ -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 */

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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