skd: Avoid that module unloading triggers a use-after-free
authorBart Van Assche <bart.vanassche@wdc.com>
Thu, 17 Aug 2017 20:12:45 +0000 (13:12 -0700)
committerJens Axboe <axboe@kernel.dk>
Fri, 18 Aug 2017 14:45:29 +0000 (08:45 -0600)
Since put_disk() triggers a disk_release() call and since that
last function calls blk_put_queue() if disk->queue != NULL, clear
the disk->queue pointer before calling put_disk(). This avoids
that unloading the skd kernel module triggers the following
use-after-free:

WARNING: CPU: 8 PID: 297 at lib/refcount.c:128 refcount_sub_and_test+0x70/0x80
refcount_t: underflow; use-after-free.
CPU: 8 PID: 297 Comm: kworker/8:1 Not tainted 4.11.10-300.fc26.x86_64 #1
Workqueue: events work_for_cpu_fn
Call Trace:
 dump_stack+0x63/0x84
 __warn+0xcb/0xf0
 warn_slowpath_fmt+0x5a/0x80
 refcount_sub_and_test+0x70/0x80
 refcount_dec_and_test+0x11/0x20
 kobject_put+0x1f/0x50
 blk_put_queue+0x15/0x20
 disk_release+0xae/0xf0
 device_release+0x32/0x90
 kobject_release+0x67/0x170
 kobject_put+0x2b/0x50
 put_disk+0x17/0x20
 skd_destruct+0x5c/0x890 [skd]
 skd_pci_probe+0x124d/0x13a0 [skd]
 local_pci_probe+0x42/0xa0
 work_for_cpu_fn+0x14/0x20
 process_one_work+0x19e/0x470
 worker_thread+0x1dc/0x4a0
 kthread+0x125/0x140
 ret_from_fork+0x25/0x30

Signed-off-by: Bart Van Assche <bart.vanassche@wdc.com>
Cc: Christoph Hellwig <hch@lst.de>
Cc: Hannes Reinecke <hare@suse.de>
Cc: Johannes Thumshirn <jthumshirn@suse.de>
Cc: <stable@vger.kernel.org>
Signed-off-by: Jens Axboe <axboe@kernel.dk>
drivers/block/skd_main.c

index d0368682bd437de8b742cf610cbcd75fb07cd02f..edab9c04e8adabc2942b295503d4c950cab82e47 100644 (file)
@@ -4539,15 +4539,16 @@ static void skd_free_disk(struct skd_device *skdev)
 {
        struct gendisk *disk = skdev->disk;
 
-       if (disk != NULL) {
-               struct request_queue *q = disk->queue;
-
-               if (disk->flags & GENHD_FL_UP)
-                       del_gendisk(disk);
-               if (q)
-                       blk_cleanup_queue(q);
-               put_disk(disk);
+       if (disk && (disk->flags & GENHD_FL_UP))
+               del_gendisk(disk);
+
+       if (skdev->queue) {
+               blk_cleanup_queue(skdev->queue);
+               skdev->queue = NULL;
+               disk->queue = NULL;
        }
+
+       put_disk(disk);
        skdev->disk = NULL;
 }