Re: [PATCH] libata: Remove extra scsi_host_put() in ata_scsi_add_hosts()

From: Jens Axboe
Date: Thu Mar 12 2020 - 11:07:18 EST


On 2/28/20 4:33 AM, John Garry wrote:
> If the call to scsi_add_host_with_dma() in ata_scsi_add_hosts() fails,
> then we may get use-after-free KASAN warns:
>
> ==================================================================
> BUG: KASAN: use-after-free in kobject_put+0x24/0x180
> Read of size 1 at addr ffff0026b8c80364 by task swapper/0/1
> CPU: 1 PID: 1 Comm: swapper/0 Tainted: G W 5.6.0-rc3-00004-g5a71b206ea82-dirty #1765
> Hardware name: Huawei TaiShan 200 (Model 2280)/BC82AMDD, BIOS 2280-V2 CS V3.B160.01 02/24/2020
> Call trace:
> dump_backtrace+0x0/0x298
> show_stack+0x14/0x20
> dump_stack+0x118/0x190
> print_address_description.isra.9+0x6c/0x3b8
> __kasan_report+0x134/0x23c
> kasan_report+0xc/0x18
> __asan_load1+0x5c/0x68
> kobject_put+0x24/0x180
> put_device+0x10/0x20
> scsi_host_put+0x10/0x18
> ata_devres_release+0x74/0xb0
> release_nodes+0x2d0/0x470
> devres_release_all+0x50/0x78
> really_probe+0x2d4/0x560
> driver_probe_device+0x7c/0x148
> device_driver_attach+0x94/0xa0
> __driver_attach+0xa8/0x110
> bus_for_each_dev+0xe8/0x158
> driver_attach+0x30/0x40
> bus_add_driver+0x220/0x2e0
> driver_register+0xbc/0x1d0
> __pci_register_driver+0xbc/0xd0
> ahci_pci_driver_init+0x20/0x28
> do_one_initcall+0xf0/0x608
> kernel_init_freeable+0x31c/0x384
> kernel_init+0x10/0x118
> ret_from_fork+0x10/0x18
>
> Allocated by task 5:
> save_stack+0x28/0xc8
> __kasan_kmalloc.isra.8+0xbc/0xd8
> kasan_kmalloc+0xc/0x18
> __kmalloc+0x1a8/0x280
> scsi_host_alloc+0x44/0x678
> ata_scsi_add_hosts+0x74/0x268
> ata_host_register+0x228/0x488
> ahci_host_activate+0x1c4/0x2a8
> ahci_init_one+0xd18/0x1298
> local_pci_probe+0x74/0xf0
> work_for_cpu_fn+0x2c/0x48
> process_one_work+0x488/0xc08
> worker_thread+0x330/0x5d0
> kthread+0x1c8/0x1d0
> ret_from_fork+0x10/0x18
>
> Freed by task 5:
> save_stack+0x28/0xc8
> __kasan_slab_free+0x118/0x180
> kasan_slab_free+0x10/0x18
> slab_free_freelist_hook+0xa4/0x1a0
> kfree+0xd4/0x3a0
> scsi_host_dev_release+0x100/0x148
> device_release+0x7c/0xe0
> kobject_put+0xb0/0x180
> put_device+0x10/0x20
> scsi_host_put+0x10/0x18
> ata_scsi_add_hosts+0x210/0x268
> ata_host_register+0x228/0x488
> ahci_host_activate+0x1c4/0x2a8
> ahci_init_one+0xd18/0x1298
> local_pci_probe+0x74/0xf0
> work_for_cpu_fn+0x2c/0x48
> process_one_work+0x488/0xc08
> worker_thread+0x330/0x5d0
> kthread+0x1c8/0x1d0
> ret_from_fork+0x10/0x18
>
> There is also refcount issue, as well:
> WARNING: CPU: 1 PID: 1 at lib/refcount.c:28 refcount_warn_saturate+0xf8/0x170
>
> The issue is that we make an erroneous extra call to scsi_host_put()
> for that host:
>
> So in ahci_init_one()->ata_host_alloc_pinfo()->ata_host_alloc(), we setup
> a device release method - ata_devres_release() - which intends to release
> the SCSI hosts:
>
> static void ata_devres_release(struct device *gendev, void *res)
> {
> ...
> for (i = 0; i < host->n_ports; i++) {
> struct ata_port *ap = host->ports[i];
>
> if (!ap)
> continue;
>
> if (ap->scsi_host)
> scsi_host_put(ap->scsi_host);
>
> }
> ...
> }
>
> However in the ata_scsi_add_hosts() error path, we also call
> scsi_host_put() for the SCSI hosts.
>
> Fix by removing the the scsi_host_put() calls in ata_scsi_add_hosts() and
> leave this to ata_devres_release().

Applied for 5.7, thanks.

--
Jens Axboe