| From a6d05617eee84826821f29705e541f9b366ddc6d Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Wed, 27 May 2020 12:01:53 +0800 |
| Subject: bcache: fix refcount underflow in bcache_device_free() |
| |
| From: Coly Li <colyli@suse.de> |
| |
| [ Upstream commit 86da9f736740eba602389908574dfbb0f517baa5 ] |
| |
| The problematic code piece in bcache_device_free() is, |
| |
| 785 static void bcache_device_free(struct bcache_device *d) |
| 786 { |
| 787 struct gendisk *disk = d->disk; |
| [snipped] |
| 799 if (disk) { |
| 800 if (disk->flags & GENHD_FL_UP) |
| 801 del_gendisk(disk); |
| 802 |
| 803 if (disk->queue) |
| 804 blk_cleanup_queue(disk->queue); |
| 805 |
| 806 ida_simple_remove(&bcache_device_idx, |
| 807 first_minor_to_idx(disk->first_minor)); |
| 808 put_disk(disk); |
| 809 } |
| [snipped] |
| 816 } |
| |
| At line 808, put_disk(disk) may encounter kobject refcount of 'disk' |
| being underflow. |
| |
| Here is how to reproduce the issue, |
| - Attche the backing device to a cache device and do random write to |
| make the cache being dirty. |
| - Stop the bcache device while the cache device has dirty data of the |
| backing device. |
| - Only register the backing device back, NOT register cache device. |
| - The bcache device node /dev/bcache0 won't show up, because backing |
| device waits for the cache device shows up for the missing dirty |
| data. |
| - Now echo 1 into /sys/fs/bcache/pendings_cleanup, to stop the pending |
| backing device. |
| - After the pending backing device stopped, use 'dmesg' to check kernel |
| message, a use-after-free warning from KASA reported the refcount of |
| kobject linked to the 'disk' is underflow. |
| |
| The dropping refcount at line 808 in the above code piece is added by |
| add_disk(d->disk) in bch_cached_dev_run(). But in the above condition |
| the cache device is not registered, bch_cached_dev_run() has no chance |
| to be called and the refcount is not added. The put_disk() for a non- |
| added refcount of gendisk kobject triggers a underflow warning. |
| |
| This patch checks whether GENHD_FL_UP is set in disk->flags, if it is |
| not set then the bcache device was not added, don't call put_disk() |
| and the the underflow issue can be avoided. |
| |
| Signed-off-by: Coly Li <colyli@suse.de> |
| Signed-off-by: Jens Axboe <axboe@kernel.dk> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| drivers/md/bcache/super.c | 7 +++++-- |
| 1 file changed, 5 insertions(+), 2 deletions(-) |
| |
| diff --git a/drivers/md/bcache/super.c b/drivers/md/bcache/super.c |
| index d98354fa28e3..4d8bf731b118 100644 |
| --- a/drivers/md/bcache/super.c |
| +++ b/drivers/md/bcache/super.c |
| @@ -797,7 +797,9 @@ static void bcache_device_free(struct bcache_device *d) |
| bcache_device_detach(d); |
| |
| if (disk) { |
| - if (disk->flags & GENHD_FL_UP) |
| + bool disk_added = (disk->flags & GENHD_FL_UP) != 0; |
| + |
| + if (disk_added) |
| del_gendisk(disk); |
| |
| if (disk->queue) |
| @@ -805,7 +807,8 @@ static void bcache_device_free(struct bcache_device *d) |
| |
| ida_simple_remove(&bcache_device_idx, |
| first_minor_to_idx(disk->first_minor)); |
| - put_disk(disk); |
| + if (disk_added) |
| + put_disk(disk); |
| } |
| |
| bioset_exit(&d->bio_split); |
| -- |
| 2.25.1 |
| |