[PATCH] block: fix potential double free during probe failure

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



disk_release drops the reference on the request_queue if disk->queue
is set.  But many drivers set the pointer early, which means they
have to clear it before calling put_disk during a probe failure,
which is often forgotten.  Even worse for drivers that refcount their
per-disk structure like nvme the path for a real teardown vs probe
failure can be shared.  Fix all this by setting a gendisk flag only
when add_disk has acquired a request_queue reference, and only drop
the reference when the flag was set.  Also make sure add_disk always
grabs a reference, even if the queue is marked dying, as we expect
the reference.

This patch fixes a potential use after free during nvme probe failure.
The fact that nvme can call add_disk on a dying queue is another bug
in the nvme code that we'll fix separatel.

Reported-by: Tong Zhang <ztong0001@xxxxxxxxx>
Signed-off-by: Christoph Hellwig <hch@xxxxxx>
---
 block/genhd.c         | 6 ++++--
 include/linux/genhd.h | 1 +
 2 files changed, 5 insertions(+), 2 deletions(-)

diff --git a/block/genhd.c b/block/genhd.c
index 9d060e79eb31d8..ef2784c69d59ee 100644
--- a/block/genhd.c
+++ b/block/genhd.c
@@ -832,7 +832,9 @@ static void __device_add_disk(struct device *parent, struct gendisk *disk,
 	 * Take an extra ref on queue which will be put on disk_release()
 	 * so that it sticks around as long as @disk is there.
 	 */
-	WARN_ON_ONCE(!blk_get_queue(disk->queue));
+	WARN_ON_ONCE(blk_queue_dying(disk->queue));
+	__blk_get_queue(disk->queue);
+	disk->flags |= GENHD_FL_QUEUE_REF;
 
 	disk_add_events(disk);
 	blk_integrity_add(disk);
@@ -1564,7 +1566,7 @@ static void disk_release(struct device *dev)
 	kfree(disk->random);
 	disk_replace_part_tbl(disk, NULL);
 	hd_free_part(&disk->part0);
-	if (disk->queue)
+	if (disk->flags & GENHD_FL_QUEUE_REF)
 		blk_put_queue(disk->queue);
 	kfree(disk);
 }
diff --git a/include/linux/genhd.h b/include/linux/genhd.h
index 1c97cf84f011a7..822a619924e3b5 100644
--- a/include/linux/genhd.h
+++ b/include/linux/genhd.h
@@ -133,6 +133,7 @@ struct hd_struct {
 #define GENHD_FL_BLOCK_EVENTS_ON_EXCL_WRITE	0x0100
 #define GENHD_FL_NO_PART_SCAN			0x0200
 #define GENHD_FL_HIDDEN				0x0400
+#define GENHD_FL_QUEUE_REF			0x0800
 
 enum {
 	DISK_EVENT_MEDIA_CHANGE			= 1 << 0, /* media changed */
-- 
2.28.0




[Index of Archives]     [Linux RAID]     [Linux SCSI]     [Linux ATA RAID]     [IDE]     [Linux Wireless]     [Linux Kernel]     [ATH6KL]     [Linux Bluetooth]     [Linux Netdev]     [Kernel Newbies]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Device Mapper]

  Powered by Linux