| From foo@baz Sun Jun 17 12:07:34 CEST 2018 |
| From: Jianchao Wang <jianchao.w.wang@oracle.com> |
| Date: Fri, 4 May 2018 16:01:57 +0800 |
| Subject: nvme: fix use-after-free in nvme_free_ns_head |
| |
| From: Jianchao Wang <jianchao.w.wang@oracle.com> |
| |
| [ Upstream commit 12d9f07022dcde261ad16e9a11f45096dc68b03c ] |
| |
| Currently only nvme_ctrl will take a reference counter of |
| nvme_subsystem, nvme_ns_head also needs it. Otherwise |
| nvme_free_ns_head will access the nvme_subsystem.ns_ida |
| which has been freed by __nvme_release_subsystem after all the |
| reference of nvme_subsystem have been released by nvme_free_ctrl. |
| This could cause memory corruption. |
| |
| BUG: KASAN: use-after-free in radix_tree_next_chunk+0x9f/0x4b0 |
| Read of size 8 at addr ffff88036494d2e8 by task fio/1815 |
| |
| CPU: 1 PID: 1815 Comm: fio Kdump: loaded Tainted: G W 4.17.0-rc1+ #18 |
| Hardware name: LENOVO 10MLS0E339/3106, BIOS M1AKT22A 06/27/2017 |
| Call Trace: |
| dump_stack+0x91/0xeb |
| print_address_description+0x6b/0x290 |
| kasan_report+0x261/0x360 |
| radix_tree_next_chunk+0x9f/0x4b0 |
| ida_remove+0x8b/0x180 |
| ida_simple_remove+0x26/0x40 |
| nvme_free_ns_head+0x58/0xc0 |
| __blkdev_put+0x30a/0x3a0 |
| blkdev_close+0x44/0x50 |
| __fput+0x184/0x380 |
| task_work_run+0xaf/0xe0 |
| do_exit+0x501/0x1440 |
| do_group_exit+0x89/0x140 |
| __x64_sys_exit_group+0x28/0x30 |
| do_syscall_64+0x72/0x230 |
| |
| Signed-off-by: Jianchao Wang <jianchao.w.wang@oracle.com> |
| Reviewed-by: Christoph Hellwig <hch@lst.de> |
| Signed-off-by: Keith Busch <keith.busch@intel.com> |
| Signed-off-by: Sasha Levin <alexander.levin@microsoft.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| drivers/nvme/host/core.c | 5 +++++ |
| 1 file changed, 5 insertions(+) |
| |
| --- a/drivers/nvme/host/core.c |
| +++ b/drivers/nvme/host/core.c |
| @@ -99,6 +99,7 @@ static struct class *nvme_subsys_class; |
| |
| static void nvme_ns_remove(struct nvme_ns *ns); |
| static int nvme_revalidate_disk(struct gendisk *disk); |
| +static void nvme_put_subsystem(struct nvme_subsystem *subsys); |
| |
| static __le32 nvme_get_log_dw10(u8 lid, size_t size) |
| { |
| @@ -353,6 +354,7 @@ static void nvme_free_ns_head(struct kre |
| ida_simple_remove(&head->subsys->ns_ida, head->instance); |
| list_del_init(&head->entry); |
| cleanup_srcu_struct(&head->srcu); |
| + nvme_put_subsystem(head->subsys); |
| kfree(head); |
| } |
| |
| @@ -2843,6 +2845,9 @@ static struct nvme_ns_head *nvme_alloc_n |
| goto out_cleanup_srcu; |
| |
| list_add_tail(&head->entry, &ctrl->subsys->nsheads); |
| + |
| + kref_get(&ctrl->subsys->ref); |
| + |
| return head; |
| out_cleanup_srcu: |
| cleanup_srcu_struct(&head->srcu); |