mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Sasha Levin <sashal@kernel.org>
To: linux-kernel@vger.kernel.org, stable@vger.kernel.org
Cc: Stefan Hajnoczi <stefanha@redhat.com>,
	Lance Digby <ldigby@redhat.com>,
	"Michael S . Tsirkin" <mst@redhat.com>,
	Stefano Garzarella <sgarzare@redhat.com>,
	Sasha Levin <sashal@kernel.org>,
	virtualization@lists.linux-foundation.org,
	linux-block@vger.kernel.org
Subject: [PATCH AUTOSEL 5.4 29/49] virtio-blk: handle block_device_operations callbacks after hot unplug
Date: Thu, 14 May 2020 14:52:50 -0400	[thread overview]
Message-ID: <20200514185311.20294-29-sashal@kernel.org> (raw)
In-Reply-To: <20200514185311.20294-1-sashal@kernel.org>

From: Stefan Hajnoczi <stefanha@redhat.com>

[ Upstream commit 90b5feb8c4bebc76c27fcaf3e1a0e5ca2d319e9e ]

A userspace process holding a file descriptor to a virtio_blk device can
still invoke block_device_operations after hot unplug.  This leads to a
use-after-free accessing vblk->vdev in virtblk_getgeo() when
ioctl(HDIO_GETGEO) is invoked:

  BUG: unable to handle kernel NULL pointer dereference at 0000000000000090
  IP: [<ffffffffc00e5450>] virtio_check_driver_offered_feature+0x10/0x90 [virtio]
  PGD 800000003a92f067 PUD 3a930067 PMD 0
  Oops: 0000 [#1] SMP
  CPU: 0 PID: 1310 Comm: hdio-getgeo Tainted: G           OE  ------------   3.10.0-1062.el7.x86_64 #1
  Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.13.0-0-gf21b5a4aeb02-prebuilt.qemu.org 04/01/2014
  task: ffff9be5fbfb8000 ti: ffff9be5fa890000 task.ti: ffff9be5fa890000
  RIP: 0010:[<ffffffffc00e5450>]  [<ffffffffc00e5450>] virtio_check_driver_offered_feature+0x10/0x90 [virtio]
  RSP: 0018:ffff9be5fa893dc8  EFLAGS: 00010246
  RAX: ffff9be5fc3f3400 RBX: ffff9be5fa893e30 RCX: 0000000000000000
  RDX: 0000000000000000 RSI: 0000000000000004 RDI: ffff9be5fbc10b40
  RBP: ffff9be5fa893dc8 R08: 0000000000000301 R09: 0000000000000301
  R10: 0000000000000000 R11: 0000000000000000 R12: ffff9be5fdc24680
  R13: ffff9be5fbc10b40 R14: ffff9be5fbc10480 R15: 0000000000000000
  FS:  00007f1bfb968740(0000) GS:ffff9be5ffc00000(0000) knlGS:0000000000000000
  CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
  CR2: 0000000000000090 CR3: 000000003a894000 CR4: 0000000000360ff0
  DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
  DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400
  Call Trace:
   [<ffffffffc016ac37>] virtblk_getgeo+0x47/0x110 [virtio_blk]
   [<ffffffff8d3f200d>] ? handle_mm_fault+0x39d/0x9b0
   [<ffffffff8d561265>] blkdev_ioctl+0x1f5/0xa20
   [<ffffffff8d488771>] block_ioctl+0x41/0x50
   [<ffffffff8d45d9e0>] do_vfs_ioctl+0x3a0/0x5a0
   [<ffffffff8d45dc81>] SyS_ioctl+0xa1/0xc0

A related problem is that virtblk_remove() leaks the vd_index_ida index
when something still holds a reference to vblk->disk during hot unplug.
This causes virtio-blk device names to be lost (vda, vdb, etc).

Fix these issues by protecting vblk->vdev with a mutex and reference
counting vblk so the vd_index_ida index can be removed in all cases.

Fixes: 48e4043d4529 ("virtio: add virtio disk geometry feature")
Reported-by: Lance Digby <ldigby@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Link: https://lore.kernel.org/r/20200430140442.171016-1-stefanha@redhat.com
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Reviewed-by: Stefano Garzarella <sgarzare@redhat.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
 drivers/block/virtio_blk.c | 86 ++++++++++++++++++++++++++++++++++----
 1 file changed, 78 insertions(+), 8 deletions(-)

diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c
index a55383b139df9..0cf2fe2902307 100644
--- a/drivers/block/virtio_blk.c
+++ b/drivers/block/virtio_blk.c
@@ -33,6 +33,15 @@ struct virtio_blk_vq {
 } ____cacheline_aligned_in_smp;
 
 struct virtio_blk {
+	/*
+	 * This mutex must be held by anything that may run after
+	 * virtblk_remove() sets vblk->vdev to NULL.
+	 *
+	 * blk-mq, virtqueue processing, and sysfs attribute code paths are
+	 * shut down before vblk->vdev is set to NULL and therefore do not need
+	 * to hold this mutex.
+	 */
+	struct mutex vdev_mutex;
 	struct virtio_device *vdev;
 
 	/* The disk structure for the kernel. */
@@ -44,6 +53,13 @@ struct virtio_blk {
 	/* Process context for config space updates */
 	struct work_struct config_work;
 
+	/*
+	 * Tracks references from block_device_operations open/release and
+	 * virtio_driver probe/remove so this object can be freed once no
+	 * longer in use.
+	 */
+	refcount_t refs;
+
 	/* What host tells us, plus 2 for header & tailer. */
 	unsigned int sg_elems;
 
@@ -388,10 +404,55 @@ static int virtblk_get_id(struct gendisk *disk, char *id_str)
 	return err;
 }
 
+static void virtblk_get(struct virtio_blk *vblk)
+{
+	refcount_inc(&vblk->refs);
+}
+
+static void virtblk_put(struct virtio_blk *vblk)
+{
+	if (refcount_dec_and_test(&vblk->refs)) {
+		ida_simple_remove(&vd_index_ida, vblk->index);
+		mutex_destroy(&vblk->vdev_mutex);
+		kfree(vblk);
+	}
+}
+
+static int virtblk_open(struct block_device *bd, fmode_t mode)
+{
+	struct virtio_blk *vblk = bd->bd_disk->private_data;
+	int ret = 0;
+
+	mutex_lock(&vblk->vdev_mutex);
+
+	if (vblk->vdev)
+		virtblk_get(vblk);
+	else
+		ret = -ENXIO;
+
+	mutex_unlock(&vblk->vdev_mutex);
+	return ret;
+}
+
+static void virtblk_release(struct gendisk *disk, fmode_t mode)
+{
+	struct virtio_blk *vblk = disk->private_data;
+
+	virtblk_put(vblk);
+}
+
 /* We provide getgeo only to please some old bootloader/partitioning tools */
 static int virtblk_getgeo(struct block_device *bd, struct hd_geometry *geo)
 {
 	struct virtio_blk *vblk = bd->bd_disk->private_data;
+	int ret = 0;
+
+	mutex_lock(&vblk->vdev_mutex);
+
+	if (!vblk->vdev) {
+		ret = -ENXIO;
+		goto out;
+	}
 
 	/* see if the host passed in geometry config */
 	if (virtio_has_feature(vblk->vdev, VIRTIO_BLK_F_GEOMETRY)) {
@@ -407,12 +468,16 @@ static int virtblk_getgeo(struct block_device *bd, struct hd_geometry *geo)
 		geo->sectors = 1 << 5;
 		geo->cylinders = get_capacity(bd->bd_disk) >> 11;
 	}
-	return 0;
+out:
+	mutex_unlock(&vblk->vdev_mutex);
+	return ret;
 }
 
 static const struct block_device_operations virtblk_fops = {
 	.ioctl  = virtblk_ioctl,
 	.owner  = THIS_MODULE,
+	.open = virtblk_open,
+	.release = virtblk_release,
 	.getgeo = virtblk_getgeo,
 };
 
@@ -767,6 +832,10 @@ static int virtblk_probe(struct virtio_device *vdev)
 		goto out_free_index;
 	}
 
+	/* This reference is dropped in virtblk_remove(). */
+	refcount_set(&vblk->refs, 1);
+	mutex_init(&vblk->vdev_mutex);
+
 	vblk->vdev = vdev;
 	vblk->sg_elems = sg_elems;
 
@@ -932,8 +1001,6 @@ static int virtblk_probe(struct virtio_device *vdev)
 static void virtblk_remove(struct virtio_device *vdev)
 {
 	struct virtio_blk *vblk = vdev->priv;
-	int index = vblk->index;
-	int refc;
 
 	/* Make sure no work handler is accessing the device. */
 	flush_work(&vblk->config_work);
@@ -943,18 +1010,21 @@ static void virtblk_remove(struct virtio_device *vdev)
 
 	blk_mq_free_tag_set(&vblk->tag_set);
 
+	mutex_lock(&vblk->vdev_mutex);
+
 	/* Stop all the virtqueues. */
 	vdev->config->reset(vdev);
 
-	refc = kref_read(&disk_to_dev(vblk->disk)->kobj.kref);
+	/* Virtqueues are stopped, nothing can use vblk->vdev anymore. */
+	vblk->vdev = NULL;
+
 	put_disk(vblk->disk);
 	vdev->config->del_vqs(vdev);
 	kfree(vblk->vqs);
-	kfree(vblk);
 
-	/* Only free device id if we don't have any users */
-	if (refc == 1)
-		ida_simple_remove(&vd_index_ida, index);
+	mutex_unlock(&vblk->vdev_mutex);
+
+	virtblk_put(vblk);
 }
 
 #ifdef CONFIG_PM_SLEEP
-- 
2.20.1


  parent reply	other threads:[~2020-05-14 18:54 UTC|newest]

Thread overview: 49+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-05-14 18:52 [PATCH AUTOSEL 5.4 01/49] kbuild: avoid concurrency issue in parallel building dtbs and dtbs_check Sasha Levin
2020-05-14 18:52 ` [PATCH AUTOSEL 5.4 02/49] net: drop_monitor: use IS_REACHABLE() to guard net_dm_hw_report() Sasha Levin
2020-05-14 18:52 ` [PATCH AUTOSEL 5.4 03/49] Makefile: disallow data races on gcc-10 as well Sasha Levin
2020-05-14 18:52 ` [PATCH AUTOSEL 5.4 04/49] gcc-common.h: Update for GCC 10 Sasha Levin
2020-05-14 18:52 ` [PATCH AUTOSEL 5.4 05/49] HID: multitouch: add eGalaxTouch P80H84 support Sasha Levin
2020-05-14 18:52 ` [PATCH AUTOSEL 5.4 06/49] HID: alps: Add AUI1657 device ID Sasha Levin
2020-05-14 18:52 ` [PATCH AUTOSEL 5.4 07/49] HID: alps: ALPS_1657 is too specific; use U1_UNICORN_LEGACY instead Sasha Levin
2020-05-14 18:52 ` [PATCH AUTOSEL 5.4 08/49] phy: tegra: Select USB_COMMON for usb_get_maximum_speed() Sasha Levin
2020-05-14 18:52 ` [PATCH AUTOSEL 5.4 09/49] scsi: qla2xxx: Fix hang when issuing nvme disconnect-all in NPIV Sasha Levin
2020-05-14 18:52 ` [PATCH AUTOSEL 5.4 10/49] scsi: qla2xxx: Delete all sessions before unregister local nvme port Sasha Levin
2020-05-14 18:52 ` [PATCH AUTOSEL 5.4 11/49] configfs: fix config_item refcnt leak in configfs_rmdir() Sasha Levin
2020-05-14 18:52 ` [PATCH AUTOSEL 5.4 12/49] vhost/vsock: fix packet delivery order to monitoring devices Sasha Levin
2020-05-14 18:52 ` [PATCH AUTOSEL 5.4 13/49] aquantia: Fix the media type of AQC100 ethernet controller in the driver Sasha Levin
2020-05-14 18:52 ` [PATCH AUTOSEL 5.4 14/49] net/sonic: Fix a resource leak in an error handling path in 'jazz_sonic_probe()' Sasha Levin
2020-05-14 18:52 ` [PATCH AUTOSEL 5.4 15/49] most: core: use function subsys_initcall() Sasha Levin
2020-05-14 18:52 ` [PATCH AUTOSEL 5.4 16/49] component: Silence bind error on -EPROBE_DEFER Sasha Levin
2020-05-14 18:52 ` [PATCH AUTOSEL 5.4 17/49] net/ena: Fix build warning in ena_xdp_set() Sasha Levin
2020-05-14 18:52 ` [PATCH AUTOSEL 5.4 18/49] scsi: ibmvscsi: Fix WARN_ON during event pool release Sasha Levin
2020-05-14 18:52 ` [PATCH AUTOSEL 5.4 19/49] HID: i2c-hid: reset Synaptics SYNA2393 on resume Sasha Levin
2020-05-14 18:52 ` [PATCH AUTOSEL 5.4 20/49] x86/mm/cpa: Flush direct map alias during cpa Sasha Levin
2020-05-14 18:52 ` [PATCH AUTOSEL 5.4 21/49] ibmvnic: Skip fatal error reset after passive init Sasha Levin
2020-05-14 18:52 ` [PATCH AUTOSEL 5.4 22/49] soc: qcom: ipa: IPA endpoints Sasha Levin
2020-05-14 18:52 ` [PATCH AUTOSEL 5.4 23/49] net: ipa: fix a bug in ipa_endpoint_stop() Sasha Levin
2020-05-14 18:52 ` [PATCH AUTOSEL 5.4 24/49] net: moxa: Fix a potential double 'free_irq()' Sasha Levin
2020-05-14 18:52 ` [PATCH AUTOSEL 5.4 25/49] ftrace/selftests: workaround cgroup RT scheduling issues Sasha Levin
2020-05-14 18:52 ` [PATCH AUTOSEL 5.4 26/49] ftrace/selftest: make unresolved cases cause failure if --fail-unresolved set Sasha Levin
2020-05-14 18:52 ` [PATCH AUTOSEL 5.4 27/49] x86/apic: Move TSC deadline timer debug printk Sasha Levin
2020-05-14 18:52 ` [PATCH AUTOSEL 5.4 28/49] gtp: set NLM_F_MULTI flag in gtp_genl_dump_pdp() Sasha Levin
2020-05-14 18:52 ` Sasha Levin [this message]
2020-05-14 18:52 ` [PATCH AUTOSEL 5.4 30/49] HID: quirks: Add HID_QUIRK_NO_INIT_REPORTS quirk for Dell K12A keyboard-dock Sasha Levin
2020-05-14 18:52 ` [PATCH AUTOSEL 5.4 31/49] sun6i: dsi: fix gcc-4.8 Sasha Levin
2020-05-14 18:52 ` [PATCH AUTOSEL 5.4 32/49] ceph: fix double unlock in handle_cap_export() Sasha Levin
2020-05-14 18:52 ` [PATCH AUTOSEL 5.4 33/49] stmmac: fix pointer check after utilization in stmmac_interrupt Sasha Levin
2020-05-14 18:52 ` [PATCH AUTOSEL 5.4 34/49] USB: core: Fix misleading driver bug report Sasha Levin
2020-05-14 18:52 ` [PATCH AUTOSEL 5.4 35/49] platform/x86: asus-nb-wmi: Do not load on Asus T100TA and T200TA Sasha Levin
2020-05-14 18:52 ` [PATCH AUTOSEL 5.4 36/49] iommu/amd: Call domain_flush_complete() in update_domain() Sasha Levin
2020-05-14 18:52 ` [PATCH AUTOSEL 5.4 37/49] drm/amd/display: Prevent dpcd reads with passive dongles Sasha Levin
2020-05-14 18:52 ` [PATCH AUTOSEL 5.4 38/49] KVM: selftests: Fix build for evmcs.h Sasha Levin
2020-05-14 18:53 ` [PATCH AUTOSEL 5.4 39/49] ARM: futex: Address build warning Sasha Levin
2020-05-14 18:53 ` [PATCH AUTOSEL 5.4 40/49] scripts/gdb: repair rb_first() and rb_last() Sasha Levin
2020-05-14 18:53 ` [PATCH AUTOSEL 5.4 41/49] Stop the ad-hoc games with -Wno-maybe-initialized Sasha Levin
2020-05-14 18:53 ` [PATCH AUTOSEL 5.4 42/49] gcc-10: disable 'zero-length-bounds' warning for now Sasha Levin
2020-05-14 18:53 ` [PATCH AUTOSEL 5.4 43/49] gcc-10: disable 'array-bounds' " Sasha Levin
2020-05-14 18:53 ` [PATCH AUTOSEL 5.4 44/49] gcc-10: disable 'stringop-overflow' " Sasha Levin
2020-05-14 18:53 ` [PATCH AUTOSEL 5.4 45/49] gcc-10: disable 'restrict' " Sasha Levin
2020-05-14 18:53 ` [PATCH AUTOSEL 5.4 46/49] gcc-10: mark more functions __init to avoid section mismatch warnings Sasha Levin
2020-05-14 18:53 ` [PATCH AUTOSEL 5.4 47/49] crypto: lrw - simplify error handling in create() Sasha Levin
2020-05-14 18:53 ` [PATCH AUTOSEL 5.4 48/49] crypto: xts - simplify error handling in ->create() Sasha Levin
2020-05-14 18:53 ` [PATCH AUTOSEL 5.4 49/49] gcc-10: avoid shadowing standard library 'free()' in crypto Sasha Levin

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=20200514185311.20294-29-sashal@kernel.org \
    --to=sashal@kernel.org \
    --cc=ldigby@redhat.com \
    --cc=linux-block@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=mst@redhat.com \
    --cc=sgarzare@redhat.com \
    --cc=stable@vger.kernel.org \
    --cc=stefanha@redhat.com \
    --cc=virtualization@lists.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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

Powered by JetHome