All of lore.kernel.org
 help / color / mirror / Atom feed
From: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
To: linux-kernel@vger.kernel.org
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>,
	stable@vger.kernel.org,
	syzbot <syzbot+a5df189917e79d5e59c9@syzkaller.appspotmail.com>,
	Linus Torvalds <torvalds@linux-foundation.org>,
	Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>,
	Luiz Augusto von Dentz <luiz.von.dentz@intel.com>,
	Sasha Levin <sashal@kernel.org>
Subject: [PATCH 4.9 10/30] Bluetooth: defer cleanup of resources in hci_unregister_dev()
Date: Fri, 13 Aug 2021 17:06:38 +0200	[thread overview]
Message-ID: <20210813150522.774261130@linuxfoundation.org> (raw)
In-Reply-To: <20210813150522.445553924@linuxfoundation.org>

From: Tetsuo Handa <penguin-kernel@i-love.sakura.ne.jp>

[ Upstream commit e04480920d1eec9c061841399aa6f35b6f987d8b ]

syzbot is hitting might_sleep() warning at hci_sock_dev_event() due to
calling lock_sock() with rw spinlock held [1].

It seems that history of this locking problem is a trial and error.

Commit b40df5743ee8 ("[PATCH] bluetooth: fix socket locking in
hci_sock_dev_event()") in 2.6.21-rc4 changed bh_lock_sock() to
lock_sock() as an attempt to fix lockdep warning.

Then, commit 4ce61d1c7a8e ("[BLUETOOTH]: Fix locking in
hci_sock_dev_event().") in 2.6.22-rc2 changed lock_sock() to
local_bh_disable() + bh_lock_sock_nested() as an attempt to fix the
sleep in atomic context warning.

Then, commit 4b5dd696f81b ("Bluetooth: Remove local_bh_disable() from
hci_sock.c") in 3.3-rc1 removed local_bh_disable().

Then, commit e305509e678b ("Bluetooth: use correct lock to prevent UAF
of hdev object") in 5.13-rc5 again changed bh_lock_sock_nested() to
lock_sock() as an attempt to fix CVE-2021-3573.

This difficulty comes from current implementation that
hci_sock_dev_event(HCI_DEV_UNREG) is responsible for dropping all
references from sockets because hci_unregister_dev() immediately
reclaims resources as soon as returning from
hci_sock_dev_event(HCI_DEV_UNREG).

But the history suggests that hci_sock_dev_event(HCI_DEV_UNREG) was not
doing what it should do.

Therefore, instead of trying to detach sockets from device, let's accept
not detaching sockets from device at hci_sock_dev_event(HCI_DEV_UNREG),
by moving actual cleanup of resources from hci_unregister_dev() to
hci_cleanup_dev() which is called by bt_host_release() when all
references to this unregistered device (which is a kobject) are gone.

Since hci_sock_dev_event(HCI_DEV_UNREG) no longer resets
hci_pi(sk)->hdev, we need to check whether this device was unregistered
and return an error based on HCI_UNREGISTER flag.  There might be subtle
behavioral difference in "monitor the hdev" functionality; please report
if you found something went wrong due to this patch.

Link: https://syzkaller.appspot.com/bug?extid=a5df189917e79d5e59c9 [1]
Reported-by: syzbot <syzbot+a5df189917e79d5e59c9@syzkaller.appspotmail.com>
Suggested-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Fixes: e305509e678b ("Bluetooth: use correct lock to prevent UAF of hdev object")
Acked-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
 include/net/bluetooth/hci_core.h |  1 +
 net/bluetooth/hci_core.c         | 16 +++++------
 net/bluetooth/hci_sock.c         | 49 +++++++++++++++++++++-----------
 net/bluetooth/hci_sysfs.c        |  3 ++
 4 files changed, 45 insertions(+), 24 deletions(-)

diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index 33db6c6d3fba..819796fb9d46 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -1028,6 +1028,7 @@ struct hci_dev *hci_alloc_dev(void);
 void hci_free_dev(struct hci_dev *hdev);
 int hci_register_dev(struct hci_dev *hdev);
 void hci_unregister_dev(struct hci_dev *hdev);
+void hci_cleanup_dev(struct hci_dev *hdev);
 int hci_suspend_dev(struct hci_dev *hdev);
 int hci_resume_dev(struct hci_dev *hdev);
 int hci_reset_dev(struct hci_dev *hdev);
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index 839c534bdcdb..8517da7f282e 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -3146,14 +3146,10 @@ EXPORT_SYMBOL(hci_register_dev);
 /* Unregister HCI device */
 void hci_unregister_dev(struct hci_dev *hdev)
 {
-	int id;
-
 	BT_DBG("%p name %s bus %d", hdev, hdev->name, hdev->bus);
 
 	hci_dev_set_flag(hdev, HCI_UNREGISTER);
 
-	id = hdev->id;
-
 	write_lock(&hci_dev_list_lock);
 	list_del(&hdev->list);
 	write_unlock(&hci_dev_list_lock);
@@ -3182,7 +3178,14 @@ void hci_unregister_dev(struct hci_dev *hdev)
 	}
 
 	device_del(&hdev->dev);
+	/* Actual cleanup is deferred until hci_cleanup_dev(). */
+	hci_dev_put(hdev);
+}
+EXPORT_SYMBOL(hci_unregister_dev);
 
+/* Cleanup HCI device */
+void hci_cleanup_dev(struct hci_dev *hdev)
+{
 	debugfs_remove_recursive(hdev->debugfs);
 	kfree_const(hdev->hw_info);
 	kfree_const(hdev->fw_info);
@@ -3204,11 +3207,8 @@ void hci_unregister_dev(struct hci_dev *hdev)
 	hci_discovery_filter_clear(hdev);
 	hci_dev_unlock(hdev);
 
-	hci_dev_put(hdev);
-
-	ida_simple_remove(&hci_index_ida, id);
+	ida_simple_remove(&hci_index_ida, hdev->id);
 }
-EXPORT_SYMBOL(hci_unregister_dev);
 
 /* Suspend HCI device */
 int hci_suspend_dev(struct hci_dev *hdev)
diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c
index 35f5585188de..d30163eb1643 100644
--- a/net/bluetooth/hci_sock.c
+++ b/net/bluetooth/hci_sock.c
@@ -59,6 +59,17 @@ struct hci_pinfo {
 	char              comm[TASK_COMM_LEN];
 };
 
+static struct hci_dev *hci_hdev_from_sock(struct sock *sk)
+{
+	struct hci_dev *hdev = hci_pi(sk)->hdev;
+
+	if (!hdev)
+		return ERR_PTR(-EBADFD);
+	if (hci_dev_test_flag(hdev, HCI_UNREGISTER))
+		return ERR_PTR(-EPIPE);
+	return hdev;
+}
+
 void hci_sock_set_flag(struct sock *sk, int nr)
 {
 	set_bit(nr, &hci_pi(sk)->flags);
@@ -747,19 +758,13 @@ void hci_sock_dev_event(struct hci_dev *hdev, int event)
 	if (event == HCI_DEV_UNREG) {
 		struct sock *sk;
 
-		/* Detach sockets from device */
+		/* Wake up sockets using this dead device */
 		read_lock(&hci_sk_list.lock);
 		sk_for_each(sk, &hci_sk_list.head) {
-			lock_sock(sk);
 			if (hci_pi(sk)->hdev == hdev) {
-				hci_pi(sk)->hdev = NULL;
 				sk->sk_err = EPIPE;
-				sk->sk_state = BT_OPEN;
 				sk->sk_state_change(sk);
-
-				hci_dev_put(hdev);
 			}
-			release_sock(sk);
 		}
 		read_unlock(&hci_sk_list.lock);
 	}
@@ -918,10 +923,10 @@ static int hci_sock_blacklist_del(struct hci_dev *hdev, void __user *arg)
 static int hci_sock_bound_ioctl(struct sock *sk, unsigned int cmd,
 				unsigned long arg)
 {
-	struct hci_dev *hdev = hci_pi(sk)->hdev;
+	struct hci_dev *hdev = hci_hdev_from_sock(sk);
 
-	if (!hdev)
-		return -EBADFD;
+	if (IS_ERR(hdev))
+		return PTR_ERR(hdev);
 
 	if (hci_dev_test_flag(hdev, HCI_USER_CHANNEL))
 		return -EBUSY;
@@ -1075,6 +1080,18 @@ static int hci_sock_bind(struct socket *sock, struct sockaddr *addr,
 
 	lock_sock(sk);
 
+	/* Allow detaching from dead device and attaching to alive device, if
+	 * the caller wants to re-bind (instead of close) this socket in
+	 * response to hci_sock_dev_event(HCI_DEV_UNREG) notification.
+	 */
+	hdev = hci_pi(sk)->hdev;
+	if (hdev && hci_dev_test_flag(hdev, HCI_UNREGISTER)) {
+		hci_pi(sk)->hdev = NULL;
+		sk->sk_state = BT_OPEN;
+		hci_dev_put(hdev);
+	}
+	hdev = NULL;
+
 	if (sk->sk_state == BT_BOUND) {
 		err = -EALREADY;
 		goto done;
@@ -1351,9 +1368,9 @@ static int hci_sock_getname(struct socket *sock, struct sockaddr *addr,
 
 	lock_sock(sk);
 
-	hdev = hci_pi(sk)->hdev;
-	if (!hdev) {
-		err = -EBADFD;
+	hdev = hci_hdev_from_sock(sk);
+	if (IS_ERR(hdev)) {
+		err = PTR_ERR(hdev);
 		goto done;
 	}
 
@@ -1713,9 +1730,9 @@ static int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg,
 		goto done;
 	}
 
-	hdev = hci_pi(sk)->hdev;
-	if (!hdev) {
-		err = -EBADFD;
+	hdev = hci_hdev_from_sock(sk);
+	if (IS_ERR(hdev)) {
+		err = PTR_ERR(hdev);
 		goto done;
 	}
 
diff --git a/net/bluetooth/hci_sysfs.c b/net/bluetooth/hci_sysfs.c
index ca7a35ebaefb..cb7d06bb0243 100644
--- a/net/bluetooth/hci_sysfs.c
+++ b/net/bluetooth/hci_sysfs.c
@@ -82,6 +82,9 @@ void hci_conn_del_sysfs(struct hci_conn *conn)
 static void bt_host_release(struct device *dev)
 {
 	struct hci_dev *hdev = to_hci_dev(dev);
+
+	if (hci_dev_test_flag(hdev, HCI_UNREGISTER))
+		hci_cleanup_dev(hdev);
 	kfree(hdev);
 	module_put(THIS_MODULE);
 }
-- 
2.30.2




  parent reply	other threads:[~2021-08-13 15:09 UTC|newest]

Thread overview: 34+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-08-13 15:06 [PATCH 4.9 00/30] 4.9.280-rc1 review Greg Kroah-Hartman
2021-08-13 15:06 ` [PATCH 4.9 01/30] ALSA: seq: Fix racy deletion of subscriber Greg Kroah-Hartman
2021-08-13 15:06 ` [PATCH 4.9 02/30] scsi: sr: Return correct event when media event code is 3 Greg Kroah-Hartman
2021-08-13 15:06 ` [PATCH 4.9 03/30] media: videobuf2-core: dequeue if start_streaming fails Greg Kroah-Hartman
2021-08-13 15:06 ` [PATCH 4.9 04/30] net: natsemi: Fix missing pci_disable_device() in probe and remove Greg Kroah-Hartman
2021-08-13 15:06 ` [PATCH 4.9 05/30] mips: Fix non-POSIX regexp Greg Kroah-Hartman
2021-08-13 15:06 ` [PATCH 4.9 06/30] bnx2x: fix an error code in bnx2x_nic_load() Greg Kroah-Hartman
2021-08-13 15:06 ` [PATCH 4.9 07/30] net: pegasus: fix uninit-value in get_interrupt_interval Greg Kroah-Hartman
2021-08-13 15:06 ` [PATCH 4.9 08/30] net: fec: fix use-after-free in fec_drv_remove Greg Kroah-Hartman
2021-08-13 15:06 ` [PATCH 4.9 09/30] net: vxge: fix use-after-free in vxge_device_unregister Greg Kroah-Hartman
2021-08-13 15:06 ` Greg Kroah-Hartman [this message]
2021-08-13 15:06 ` [PATCH 4.9 11/30] USB: usbtmc: Fix RCU stall warning Greg Kroah-Hartman
2021-08-13 15:06 ` [PATCH 4.9 12/30] USB: serial: option: add Telit FD980 composition 0x1056 Greg Kroah-Hartman
2021-08-13 15:06 ` [PATCH 4.9 13/30] USB: serial: ch341: fix character loss at high transfer rates Greg Kroah-Hartman
2021-08-13 15:06 ` [PATCH 4.9 14/30] USB: serial: ftdi_sio: add device ID for Auto-M3 OP-COM v2 Greg Kroah-Hartman
2021-08-13 15:06 ` [PATCH 4.9 15/30] usb: otg-fsm: Fix hrtimer list corruption Greg Kroah-Hartman
2021-08-13 15:06 ` [PATCH 4.9 16/30] scripts/tracing: fix the bug that cant parse raw_trace_func Greg Kroah-Hartman
2021-08-13 15:06 ` [PATCH 4.9 17/30] media: rtl28xxu: fix zero-length control request Greg Kroah-Hartman
2021-08-13 15:06 ` [PATCH 4.9 18/30] pipe: increase minimum default pipe size to 2 pages Greg Kroah-Hartman
2021-08-13 15:06 ` [PATCH 4.9 19/30] serial: 8250: Mask out floating 16/32-bit bus bits Greg Kroah-Hartman
2021-08-13 15:06 ` [PATCH 4.9 20/30] MIPS: Malta: Do not byte-swap accesses to the CBUS UART Greg Kroah-Hartman
2021-08-13 15:06 ` [PATCH 4.9 21/30] pcmcia: i82092: fix a null pointer dereference bug Greg Kroah-Hartman
2021-08-13 15:06 ` [PATCH 4.9 22/30] perf/x86/amd: Dont touch the AMD64_EVENTSEL_HOSTONLY bit inside the guest Greg Kroah-Hartman
2021-08-13 15:06 ` [PATCH 4.9 23/30] reiserfs: add check for root_inode in reiserfs_fill_super Greg Kroah-Hartman
2021-08-13 15:06 ` [PATCH 4.9 24/30] reiserfs: check directory items on read from disk Greg Kroah-Hartman
2021-08-13 15:06 ` [PATCH 4.9 25/30] alpha: Send stop IPI to send to online CPUs Greg Kroah-Hartman
2021-08-13 15:06 ` [PATCH 4.9 26/30] net/qla3xxx: fix schedule while atomic in ql_wait_for_drvr_lock and ql_adapter_reset Greg Kroah-Hartman
2021-08-13 15:06 ` [PATCH 4.9 27/30] USB:ehci:fix Kunpeng920 ehci hardware problem Greg Kroah-Hartman
2021-08-13 15:06 ` [PATCH 4.9 28/30] ppp: Fix generating ppp unit id when ifname is not specified Greg Kroah-Hartman
2021-08-13 15:06 ` [PATCH 4.9 29/30] net: xilinx_emaclite: Do not print real IOMEM pointer Greg Kroah-Hartman
2021-08-13 15:06 ` [PATCH 4.9 30/30] ovl: prevent private clone if bind mount is not allowed Greg Kroah-Hartman
2021-08-13 23:26 ` [PATCH 4.9 00/30] 4.9.280-rc1 review Shuah Khan
2021-08-14 14:12 ` Naresh Kamboju
2021-08-14 19:50 ` Guenter Roeck

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20210813150522.774261130@linuxfoundation.org \
    --to=gregkh@linuxfoundation.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=luiz.von.dentz@intel.com \
    --cc=penguin-kernel@I-love.SAKURA.ne.jp \
    --cc=sashal@kernel.org \
    --cc=stable@vger.kernel.org \
    --cc=syzbot+a5df189917e79d5e59c9@syzkaller.appspotmail.com \
    --cc=torvalds@linux-foundation.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.