| From: Bart Van Assche <bart.vanassche@wdc.com> |
| Date: Thu, 17 Aug 2017 13:12:45 -0700 |
| Subject: skd: Avoid that module unloading triggers a use-after-free |
| |
| commit 7277cc67b3916eed47558c64f9c9c0de00a35cda upstream. |
| |
| 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> |
| Signed-off-by: Jens Axboe <axboe@kernel.dk> |
| Signed-off-by: Ben Hutchings <ben@decadent.org.uk> |
| --- |
| drivers/block/skd_main.c | 17 +++++++++-------- |
| 1 file changed, 9 insertions(+), 8 deletions(-) |
| |
| --- a/drivers/block/skd_main.c |
| +++ b/drivers/block/skd_main.c |
| @@ -4685,15 +4685,16 @@ static void skd_free_disk(struct skd_dev |
| { |
| struct gendisk *disk = skdev->disk; |
| |
| - if (disk != NULL) { |
| - struct request_queue *q = disk->queue; |
| + if (disk && (disk->flags & GENHD_FL_UP)) |
| + del_gendisk(disk); |
| |
| - if (disk->flags & GENHD_FL_UP) |
| - del_gendisk(disk); |
| - if (q) |
| - blk_cleanup_queue(q); |
| - put_disk(disk); |
| + if (skdev->queue) { |
| + blk_cleanup_queue(skdev->queue); |
| + skdev->queue = NULL; |
| + disk->queue = NULL; |
| } |
| + |
| + put_disk(disk); |
| skdev->disk = NULL; |
| } |
| |