diff options
author | Jianchao Wang <jianchao.w.wang@oracle.com> | 2018-05-04 04:01:57 -0400 |
---|---|---|
committer | Keith Busch <keith.busch@intel.com> | 2018-05-07 10:25:08 -0400 |
commit | 12d9f07022dcde261ad16e9a11f45096dc68b03c (patch) | |
tree | ed9a7b0d36e42fc0bb7fd2b3338ab9c6cc3b14a2 | |
parent | 75bc37fefc4471e718ba8e651aa74673d4e0a9eb (diff) |
nvme: fix use-after-free in nvme_free_ns_head
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>
-rw-r--r-- | drivers/nvme/host/core.c | 5 |
1 files changed, 5 insertions, 0 deletions
diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c index a3771c5729f5..2cbc378bc0d6 100644 --- a/drivers/nvme/host/core.c +++ b/drivers/nvme/host/core.c | |||
@@ -99,6 +99,7 @@ static struct class *nvme_subsys_class; | |||
99 | 99 | ||
100 | static void nvme_ns_remove(struct nvme_ns *ns); | 100 | static void nvme_ns_remove(struct nvme_ns *ns); |
101 | static int nvme_revalidate_disk(struct gendisk *disk); | 101 | static int nvme_revalidate_disk(struct gendisk *disk); |
102 | static void nvme_put_subsystem(struct nvme_subsystem *subsys); | ||
102 | 103 | ||
103 | int nvme_reset_ctrl(struct nvme_ctrl *ctrl) | 104 | int nvme_reset_ctrl(struct nvme_ctrl *ctrl) |
104 | { | 105 | { |
@@ -350,6 +351,7 @@ static void nvme_free_ns_head(struct kref *ref) | |||
350 | ida_simple_remove(&head->subsys->ns_ida, head->instance); | 351 | ida_simple_remove(&head->subsys->ns_ida, head->instance); |
351 | list_del_init(&head->entry); | 352 | list_del_init(&head->entry); |
352 | cleanup_srcu_struct(&head->srcu); | 353 | cleanup_srcu_struct(&head->srcu); |
354 | nvme_put_subsystem(head->subsys); | ||
353 | kfree(head); | 355 | kfree(head); |
354 | } | 356 | } |
355 | 357 | ||
@@ -2861,6 +2863,9 @@ static struct nvme_ns_head *nvme_alloc_ns_head(struct nvme_ctrl *ctrl, | |||
2861 | goto out_cleanup_srcu; | 2863 | goto out_cleanup_srcu; |
2862 | 2864 | ||
2863 | list_add_tail(&head->entry, &ctrl->subsys->nsheads); | 2865 | list_add_tail(&head->entry, &ctrl->subsys->nsheads); |
2866 | |||
2867 | kref_get(&ctrl->subsys->ref); | ||
2868 | |||
2864 | return head; | 2869 | return head; |
2865 | out_cleanup_srcu: | 2870 | out_cleanup_srcu: |
2866 | cleanup_srcu_struct(&head->srcu); | 2871 | cleanup_srcu_struct(&head->srcu); |