Skip to content

Commit 2fa4a32

Browse files
JasonYanHwmartinkpetersen
authored andcommitted
scsi: libsas: dynamically allocate and free ata host
Commit 2623c7a ("libata: add refcounting to ata_host") v4.17+ introduced refcounting to ata_host and will increase or decrease the refcount when adding or deleting transport ATA port. Now the ata host for libsas is embedded in domain_device, and the ->kref member is not initialized. Afer we add ata transport class, ata_host_get() will be called when adding transport ATA port and a warning will be triggered as below: refcount_t: increment on 0; use-after-free. WARNING: CPU: 2 PID: 103 at lib/refcount.c:153 refcount_inc+0x40/0x48 ...... Call trace: refcount_inc+0x40/0x48 ata_host_get+0x10/0x18 ata_tport_add+0x40/0x120 ata_sas_tport_add+0xc/0x14 sas_ata_init+0x7c/0xc8 sas_discover_domain+0x380/0x53c process_one_work+0x12c/0x288 worker_thread+0x58/0x3f0 kthread+0xfc/0x128 ret_from_fork+0x10/0x18 And also when removing transport ATA port ata_host_put() will be called and another similar warning will be triggered. If the refcount decreased to zero, the ata host will be freed. But this ata host is only part of domain_device, it cannot be freed directly. So we have to change this embedded static ata host to a dynamically allocated ata host and initialize the ->kref member. To use ata_host_get() and ata_host_put() in libsas, we need to move the declaration of these functions to the public libata.h and export them. Fixes: b6240a4 ("scsi: libsas: add transport class for ATA devices") Signed-off-by: Jason Yan <[email protected]> CC: John Garry <[email protected]> CC: Taras Kondratiuk <[email protected]> CC: Tejun Heo <[email protected]> Acked-by: Tejun Heo <[email protected]> Signed-off-by: Martin K. Petersen <[email protected]>
1 parent 693ba15 commit 2fa4a32

File tree

6 files changed

+34
-17
lines changed

6 files changed

+34
-17
lines changed

drivers/ata/libata-core.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6421,6 +6421,7 @@ void ata_host_init(struct ata_host *host, struct device *dev,
64216421
host->n_tags = ATA_MAX_QUEUE;
64226422
host->dev = dev;
64236423
host->ops = ops;
6424+
kref_init(&host->kref);
64246425
}
64256426

64266427
void __ata_port_probe(struct ata_port *ap)
@@ -7388,3 +7389,5 @@ EXPORT_SYMBOL_GPL(ata_cable_80wire);
73887389
EXPORT_SYMBOL_GPL(ata_cable_unknown);
73897390
EXPORT_SYMBOL_GPL(ata_cable_ignore);
73907391
EXPORT_SYMBOL_GPL(ata_cable_sata);
7392+
EXPORT_SYMBOL_GPL(ata_host_get);
7393+
EXPORT_SYMBOL_GPL(ata_host_put);

drivers/ata/libata.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,8 +100,6 @@ extern int ata_port_probe(struct ata_port *ap);
100100
extern void __ata_port_probe(struct ata_port *ap);
101101
extern unsigned int ata_read_log_page(struct ata_device *dev, u8 log,
102102
u8 page, void *buf, unsigned int sectors);
103-
extern void ata_host_get(struct ata_host *host);
104-
extern void ata_host_put(struct ata_host *host);
105103

106104
#define to_ata_port(d) container_of(d, struct ata_port, tdev)
107105

drivers/scsi/libsas/sas_ata.c

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -552,34 +552,46 @@ int sas_ata_init(struct domain_device *found_dev)
552552
{
553553
struct sas_ha_struct *ha = found_dev->port->ha;
554554
struct Scsi_Host *shost = ha->core.shost;
555+
struct ata_host *ata_host;
555556
struct ata_port *ap;
556557
int rc;
557558

558-
ata_host_init(&found_dev->sata_dev.ata_host, ha->dev, &sas_sata_ops);
559-
ap = ata_sas_port_alloc(&found_dev->sata_dev.ata_host,
560-
&sata_port_info,
561-
shost);
559+
ata_host = kzalloc(sizeof(*ata_host), GFP_KERNEL);
560+
if (!ata_host) {
561+
SAS_DPRINTK("ata host alloc failed.\n");
562+
return -ENOMEM;
563+
}
564+
565+
ata_host_init(ata_host, ha->dev, &sas_sata_ops);
566+
567+
ap = ata_sas_port_alloc(ata_host, &sata_port_info, shost);
562568
if (!ap) {
563569
SAS_DPRINTK("ata_sas_port_alloc failed.\n");
564-
return -ENODEV;
570+
rc = -ENODEV;
571+
goto free_host;
565572
}
566573

567574
ap->private_data = found_dev;
568575
ap->cbl = ATA_CBL_SATA;
569576
ap->scsi_host = shost;
570577
rc = ata_sas_port_init(ap);
571-
if (rc) {
572-
ata_sas_port_destroy(ap);
573-
return rc;
574-
}
575-
rc = ata_sas_tport_add(found_dev->sata_dev.ata_host.dev, ap);
576-
if (rc) {
577-
ata_sas_port_destroy(ap);
578-
return rc;
579-
}
578+
if (rc)
579+
goto destroy_port;
580+
581+
rc = ata_sas_tport_add(ata_host->dev, ap);
582+
if (rc)
583+
goto destroy_port;
584+
585+
found_dev->sata_dev.ata_host = ata_host;
580586
found_dev->sata_dev.ap = ap;
581587

582588
return 0;
589+
590+
destroy_port:
591+
ata_sas_port_destroy(ap);
592+
free_host:
593+
ata_host_put(ata_host);
594+
return rc;
583595
}
584596

585597
void sas_ata_task_abort(struct sas_task *task)

drivers/scsi/libsas/sas_discover.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,8 @@ void sas_free_device(struct kref *kref)
316316
if (dev_is_sata(dev) && dev->sata_dev.ap) {
317317
ata_sas_tport_delete(dev->sata_dev.ap);
318318
ata_sas_port_destroy(dev->sata_dev.ap);
319+
ata_host_put(dev->sata_dev.ata_host);
320+
dev->sata_dev.ata_host = NULL;
319321
dev->sata_dev.ap = NULL;
320322
}
321323

include/linux/libata.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1110,6 +1110,8 @@ extern struct ata_host *ata_host_alloc(struct device *dev, int max_ports);
11101110
extern struct ata_host *ata_host_alloc_pinfo(struct device *dev,
11111111
const struct ata_port_info * const * ppi, int n_ports);
11121112
extern int ata_slave_link_init(struct ata_port *ap);
1113+
extern void ata_host_get(struct ata_host *host);
1114+
extern void ata_host_put(struct ata_host *host);
11131115
extern int ata_host_start(struct ata_host *host);
11141116
extern int ata_host_register(struct ata_host *host,
11151117
struct scsi_host_template *sht);

include/scsi/libsas.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ struct sata_device {
161161
u8 port_no; /* port number, if this is a PM (Port) */
162162

163163
struct ata_port *ap;
164-
struct ata_host ata_host;
164+
struct ata_host *ata_host;
165165
struct smp_resp rps_resp ____cacheline_aligned; /* report_phy_sata_resp */
166166
u8 fis[ATA_RESP_FIS_SIZE];
167167
};

0 commit comments

Comments
 (0)