diff options
| author | Mark Brown <broonie@kernel.org> | 2026-07-03 15:44:33 +0100 |
|---|---|---|
| committer | Mark Brown <broonie@kernel.org> | 2026-07-03 15:44:33 +0100 |
| commit | 53079182b2ff074d87a56da47fd781849a6b2652 (patch) | |
| tree | 8bea77480130b18bed9875c31d6dda8bd76b3a6d /net/bluetooth | |
| parent | e0ef27d451779585b22e79c1f0e6b96cb464f806 (diff) | |
| parent | 7ea67149af719895ddf003cb8e9d2b287ef0a223 (diff) | |
| download | linux-next-53079182b2ff074d87a56da47fd781849a6b2652.tar.gz linux-next-53079182b2ff074d87a56da47fd781849a6b2652.zip | |
Merge branch 'master' of https://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth-next.git
Diffstat (limited to 'net/bluetooth')
| -rw-r--r-- | net/bluetooth/6lowpan.c | 84 | ||||
| -rw-r--r-- | net/bluetooth/Makefile | 2 | ||||
| -rw-r--r-- | net/bluetooth/af_bluetooth.c | 24 | ||||
| -rw-r--r-- | net/bluetooth/bnep/Makefile | 2 | ||||
| -rw-r--r-- | net/bluetooth/bnep/core.c | 27 | ||||
| -rw-r--r-- | net/bluetooth/hci_conn.c | 21 | ||||
| -rw-r--r-- | net/bluetooth/hci_core.c | 3 | ||||
| -rw-r--r-- | net/bluetooth/hci_debugfs.c | 12 | ||||
| -rw-r--r-- | net/bluetooth/hci_event.c | 5 | ||||
| -rw-r--r-- | net/bluetooth/hci_sync.c | 175 | ||||
| -rw-r--r-- | net/bluetooth/hidp/Makefile | 2 | ||||
| -rw-r--r-- | net/bluetooth/iso.c | 44 | ||||
| -rw-r--r-- | net/bluetooth/l2cap_core.c | 140 | ||||
| -rw-r--r-- | net/bluetooth/l2cap_sock.c | 108 | ||||
| -rw-r--r-- | net/bluetooth/mgmt.c | 5 | ||||
| -rw-r--r-- | net/bluetooth/msft.c | 2 | ||||
| -rw-r--r-- | net/bluetooth/rfcomm/Makefile | 2 | ||||
| -rw-r--r-- | net/bluetooth/rfcomm/sock.c | 1 | ||||
| -rw-r--r-- | net/bluetooth/sco.c | 15 | ||||
| -rw-r--r-- | net/bluetooth/smp.c | 27 |
20 files changed, 418 insertions, 283 deletions
diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c index cb1e329d66fd..d504a363a30f 100644 --- a/net/bluetooth/6lowpan.c +++ b/net/bluetooth/6lowpan.c @@ -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 @@ done: } 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; diff --git a/net/bluetooth/Makefile b/net/bluetooth/Makefile index 41049b280887..ff466ea97436 100644 --- a/net/bluetooth/Makefile +++ b/net/bluetooth/Makefile @@ -25,3 +25,5 @@ bluetooth-$(CONFIG_BT_MSFTEXT) += msft.o bluetooth-$(CONFIG_BT_AOSPEXT) += aosp.o bluetooth-$(CONFIG_BT_DEBUGFS) += hci_debugfs.o bluetooth-$(CONFIG_BT_SELFTEST) += selftest.o + +CONTEXT_ANALYSIS := y diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c index bcbc11c9cb15..411d66f24393 100644 --- a/net/bluetooth/af_bluetooth.c +++ b/net/bluetooth/af_bluetooth.c @@ -209,6 +209,7 @@ bool bt_sock_linked(struct bt_sock_list *l, struct sock *s) EXPORT_SYMBOL(bt_sock_linked); void bt_accept_enqueue(struct sock *parent, struct sock *sk, bool bh) + __context_unsafe(/* conditional locking */) { const struct cred *old_cred; struct pid *old_pid; @@ -305,7 +306,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 +322,11 @@ restart: 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 +336,6 @@ restart: 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 +343,7 @@ restart: } release_sock(sk); + sock_put(sk); } return NULL; @@ -826,7 +816,8 @@ EXPORT_SYMBOL(bt_sock_wait_ready); #ifdef CONFIG_PROC_FS static void *bt_seq_start(struct seq_file *seq, loff_t *pos) - __acquires(seq->private->l->lock) + __acquires_shared(&((struct bt_sock_list *) + pde_data(file_inode(seq->file)))->lock) { struct bt_sock_list *l = pde_data(file_inode(seq->file)); @@ -842,7 +833,8 @@ static void *bt_seq_next(struct seq_file *seq, void *v, loff_t *pos) } static void bt_seq_stop(struct seq_file *seq, void *v) - __releases(seq->private->l->lock) + __releases_shared(&((struct bt_sock_list *) + pde_data(file_inode(seq->file)))->lock) { struct bt_sock_list *l = pde_data(file_inode(seq->file)); diff --git a/net/bluetooth/bnep/Makefile b/net/bluetooth/bnep/Makefile index 8af9d56bb012..f42015cc3245 100644 --- a/net/bluetooth/bnep/Makefile +++ b/net/bluetooth/bnep/Makefile @@ -6,3 +6,5 @@ obj-$(CONFIG_BT_BNEP) += bnep.o bnep-objs := core.o sock.o netdev.o + +CONTEXT_ANALYSIS := y diff --git a/net/bluetooth/bnep/core.c b/net/bluetooth/bnep/core.c index add9a8f7535d..f7d88c33e23e 100644 --- a/net/bluetooth/bnep/core.c +++ b/net/bluetooth/bnep/core.c @@ -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; diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index c335372e4062..1966cd153d97 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -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 diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 5ba9fe8261ec..d1e78ae7728e 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -62,6 +62,7 @@ static DEFINE_IDA(hci_index_ida); /* Get HCI device by index. * Device is held on return. */ static struct hci_dev *__hci_dev_get(int index, int *srcu_index) + __context_unsafe(/* conditional locking */) { struct hci_dev *hdev = NULL, *d; @@ -89,11 +90,13 @@ struct hci_dev *hci_dev_get(int index) } static struct hci_dev *hci_dev_get_srcu(int index, int *srcu_index) + __context_unsafe(/* conditional locking vs return */) { return __hci_dev_get(index, srcu_index); } static void hci_dev_put_srcu(struct hci_dev *hdev, int srcu_index) + __context_unsafe(/* conditional locking vs return */) { srcu_read_unlock(&hdev->srcu, srcu_index); hci_dev_put(hdev); diff --git a/net/bluetooth/hci_debugfs.c b/net/bluetooth/hci_debugfs.c index 0635e4641db4..aadffaaff20e 100644 --- a/net/bluetooth/hci_debugfs.c +++ b/net/bluetooth/hci_debugfs.c @@ -1161,16 +1161,12 @@ static ssize_t force_no_mitm_write(struct file *file, size_t count, loff_t *ppos) { struct hci_dev *hdev = file->private_data; - char buf[32]; - size_t buf_size = min(count, (sizeof(buf) - 1)); bool enable; + int err; - if (copy_from_user(buf, user_buf, buf_size)) - return -EFAULT; - - buf[buf_size] = '\0'; - if (kstrtobool(buf, &enable)) - return -EINVAL; + err = kstrtobool_from_user(user_buf, count, &enable); + if (err) + return err; if (enable == hci_dev_test_flag(hdev, HCI_FORCE_NO_MITM)) return -EALREADY; diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index b6d963ce26d0..5b867aa99332 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -269,7 +269,10 @@ static u8 hci_cc_reset(struct hci_dev *hdev, void *data, struct sk_buff *skb) { struct hci_ev_status *rp = data; - bt_dev_dbg(hdev, "status 0x%2.2x", rp->status); + if (rp->status) + bt_dev_err(hdev, "status 0x%2.2x", rp->status); + else + bt_dev_dbg(hdev, "status 0x%2.2x", rp->status); clear_bit(HCI_RESET, &hdev->flags); diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c index 3be8c3581c6c..b096fca86bb5 100644 --- a/net/bluetooth/hci_sync.c +++ b/net/bluetooth/hci_sync.c @@ -860,32 +860,6 @@ void hci_cmd_sync_cancel_entry(struct hci_dev *hdev, } EXPORT_SYMBOL(hci_cmd_sync_cancel_entry); -/* Dequeue one HCI command entry: - * - * - Lookup and cancel first entry that matches. - */ -bool hci_cmd_sync_dequeue_once(struct hci_dev *hdev, - hci_cmd_sync_work_func_t func, - void *data, hci_cmd_sync_work_destroy_t destroy) -{ - struct hci_cmd_sync_work_entry *entry; - - mutex_lock(&hdev->cmd_sync_work_lock); - - entry = _hci_cmd_sync_lookup_entry(hdev, func, data, destroy); - if (!entry) { - mutex_unlock(&hdev->cmd_sync_work_lock); - return false; - } - - _hci_cmd_sync_cancel_entry(hdev, entry, -ECANCELED); - - mutex_unlock(&hdev->cmd_sync_work_lock); - - return true; -} -EXPORT_SYMBOL(hci_cmd_sync_dequeue_once); - /* Dequeue HCI command entry: * * - Lookup and cancel any entry that matches by function callback or data or @@ -3672,17 +3646,19 @@ static const struct hci_init_stage hci_init0[] = { int hci_reset_sync(struct hci_dev *hdev) { - int err; - set_bit(HCI_RESET, &hdev->flags); - err = __hci_cmd_sync_status(hdev, HCI_OP_RESET, 0, NULL, - HCI_CMD_TIMEOUT); - if (err) - return err; + return __hci_cmd_sync_status(hdev, HCI_OP_RESET, 0, NULL, + HCI_CMD_TIMEOUT); +} - return 0; +/* Send a raw HCI reset for use by vendor drivers */ +int __hci_reset_sync(struct hci_dev *hdev) +{ + return __hci_cmd_sync_status(hdev, HCI_OP_RESET, 0, NULL, + HCI_INIT_TIMEOUT); } +EXPORT_SYMBOL(__hci_reset_sync); static int hci_init0_sync(struct hci_dev *hdev) { @@ -6633,6 +6609,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 +6626,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 +6672,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 +6713,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 +6995,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 +7065,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; + + /* 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; + } + + /* 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_cmd_sync_dequeue_once(hdev, - hci_acl_create_conn_sync, - conn, NULL); + return hci_acl_cancel_create_conn_sync(hdev, conn); case LE_LINK: - return !hci_cmd_sync_dequeue_once(hdev, hci_le_create_conn_sync, - conn, create_le_conn_complete); + return hci_le_cancel_create_conn_sync(hdev, conn); + case CIS_LINK: + return hci_cis_cancel_create_conn_sync(hdev, conn); + default: + return -ENOENT; } - - return -ENOENT; } int hci_le_conn_update_sync(struct hci_dev *hdev, struct hci_conn *conn, diff --git a/net/bluetooth/hidp/Makefile b/net/bluetooth/hidp/Makefile index f41b0aa02b23..53e139e41bdc 100644 --- a/net/bluetooth/hidp/Makefile +++ b/net/bluetooth/hidp/Makefile @@ -6,3 +6,5 @@ obj-$(CONFIG_BT_HIDP) += hidp.o hidp-objs := core.o sock.o + +CONTEXT_ANALYSIS := y diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c index 793a481d7066..2e95a153912c 100644 --- a/net/bluetooth/iso.c +++ b/net/bluetooth/iso.c @@ -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; } diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 62133eef9d2f..538ae9aa3479 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -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 @@ fail: 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, ident, L2CAP_LE_CREDITS, sizeof(pkt), &pkt); - l2cap_send_cmd(conn, chan->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) diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 4853f1b33449..735167f73f31 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -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 @@ done: 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); } @@ -1351,6 +1365,7 @@ static int __l2cap_wait_ack(struct sock *sk, struct l2cap_chan *chan) } static int l2cap_sock_shutdown(struct socket *sock, int how) + __context_unsafe(/* complex chan->conn locking */) { struct sock *sk = sock->sk; struct l2cap_chan *chan; @@ -1492,8 +1507,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 +1558,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 +1572,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 +1890,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 +1923,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 +1954,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 +1973,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 +1987,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 +1997,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 +2012,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; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index d23ca1dd0893..733a4b70e10c 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -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, diff --git a/net/bluetooth/msft.c b/net/bluetooth/msft.c index 2f008167cbaa..d7badce8746c 100644 --- a/net/bluetooth/msft.c +++ b/net/bluetooth/msft.c @@ -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); diff --git a/net/bluetooth/rfcomm/Makefile b/net/bluetooth/rfcomm/Makefile index 593e5c48c131..15f909f40f25 100644 --- a/net/bluetooth/rfcomm/Makefile +++ b/net/bluetooth/rfcomm/Makefile @@ -7,3 +7,5 @@ obj-$(CONFIG_BT_RFCOMM) += rfcomm.o rfcomm-y := core.o sock.o rfcomm-$(CONFIG_BT_RFCOMM_TTY) += tty.o + +CONTEXT_ANALYSIS := y diff --git a/net/bluetooth/rfcomm/sock.c b/net/bluetooth/rfcomm/sock.c index feb302a491fa..958081adb9b5 100644 --- a/net/bluetooth/rfcomm/sock.c +++ b/net/bluetooth/rfcomm/sock.c @@ -60,6 +60,7 @@ static void rfcomm_sk_data_ready(struct rfcomm_dlc *d, struct sk_buff *skb) } static void rfcomm_sk_state_change(struct rfcomm_dlc *d, int err) + __must_hold(&d->lock) { struct sock *sk = d->owner, *parent; diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c index fcc597be5bbd..c05f79b7aa31 100644 --- a/net/bluetooth/sco.c +++ b/net/bluetooth/sco.c @@ -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) diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 031d3022cb1e..c4470958b0d5 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -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); - - BT_DBG("created chan %p", chan); + atomic_set(&new_chan->nesting, L2CAP_NESTING_SMP); - return chan; + return 0; } static const struct l2cap_ops smp_root_chan_ops = { @@ -3288,7 +3273,7 @@ create_chan: l2cap_add_scid(chan, cid); - l2cap_chan_set_defaults(chan); + l2cap_chan_set_defaults(chan, NULL); if (cid == L2CAP_CID_SMP) { u8 bdaddr_type; |
