From: Greg Kroah-Hartman Date: Sun, 26 Aug 2018 06:46:01 +0000 (+0200) Subject: 4.18-stable patches X-Git-Tag: v3.18.120~5 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=491d3f52ef75aff6c96c299fdba3a13373aad7dd;p=thirdparty%2Fkernel%2Fstable-queue.git 4.18-stable patches added patches: scsi-libsas-dynamically-allocate-and-free-ata-host.patch --- diff --git a/queue-4.18/scsi-libsas-dynamically-allocate-and-free-ata-host.patch b/queue-4.18/scsi-libsas-dynamically-allocate-and-free-ata-host.patch new file mode 100644 index 00000000000..54f6219ca6c --- /dev/null +++ b/queue-4.18/scsi-libsas-dynamically-allocate-and-free-ata-host.patch @@ -0,0 +1,184 @@ +From 2fa4a32613c9182b00e46872755b0662374424a7 Mon Sep 17 00:00:00 2001 +From: Jason Yan +Date: Thu, 10 May 2018 11:05:16 +0800 +Subject: scsi: libsas: dynamically allocate and free ata host + +From: Jason Yan + +commit 2fa4a32613c9182b00e46872755b0662374424a7 upstream. + +Commit 2623c7a5f2 ("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: b6240a4df018 ("scsi: libsas: add transport class for ATA devices") +Signed-off-by: Jason Yan +CC: John Garry +CC: Taras Kondratiuk +CC: Tejun Heo +Acked-by: Tejun Heo +Signed-off-by: Martin K. Petersen +Signed-off-by: Greg Kroah-Hartman + +--- + drivers/ata/libata-core.c | 3 ++ + drivers/ata/libata.h | 2 - + drivers/scsi/libsas/sas_ata.c | 40 ++++++++++++++++++++++++------------- + drivers/scsi/libsas/sas_discover.c | 2 + + include/linux/libata.h | 2 + + include/scsi/libsas.h | 2 - + 6 files changed, 34 insertions(+), 17 deletions(-) + +--- a/drivers/ata/libata-core.c ++++ b/drivers/ata/libata-core.c +@@ -6424,6 +6424,7 @@ void ata_host_init(struct ata_host *host + host->n_tags = ATA_MAX_QUEUE; + host->dev = dev; + host->ops = ops; ++ kref_init(&host->kref); + } + + void __ata_port_probe(struct ata_port *ap) +@@ -7391,3 +7392,5 @@ EXPORT_SYMBOL_GPL(ata_cable_80wire); + EXPORT_SYMBOL_GPL(ata_cable_unknown); + EXPORT_SYMBOL_GPL(ata_cable_ignore); + EXPORT_SYMBOL_GPL(ata_cable_sata); ++EXPORT_SYMBOL_GPL(ata_host_get); ++EXPORT_SYMBOL_GPL(ata_host_put); +\ No newline at end of file +--- a/drivers/ata/libata.h ++++ b/drivers/ata/libata.h +@@ -100,8 +100,6 @@ extern int ata_port_probe(struct ata_por + extern void __ata_port_probe(struct ata_port *ap); + extern unsigned int ata_read_log_page(struct ata_device *dev, u8 log, + u8 page, void *buf, unsigned int sectors); +-extern void ata_host_get(struct ata_host *host); +-extern void ata_host_put(struct ata_host *host); + + #define to_ata_port(d) container_of(d, struct ata_port, tdev) + +--- a/drivers/scsi/libsas/sas_ata.c ++++ b/drivers/scsi/libsas/sas_ata.c +@@ -557,34 +557,46 @@ int sas_ata_init(struct domain_device *f + { + struct sas_ha_struct *ha = found_dev->port->ha; + struct Scsi_Host *shost = ha->core.shost; ++ struct ata_host *ata_host; + struct ata_port *ap; + int rc; + +- ata_host_init(&found_dev->sata_dev.ata_host, ha->dev, &sas_sata_ops); +- ap = ata_sas_port_alloc(&found_dev->sata_dev.ata_host, +- &sata_port_info, +- shost); ++ ata_host = kzalloc(sizeof(*ata_host), GFP_KERNEL); ++ if (!ata_host) { ++ SAS_DPRINTK("ata host alloc failed.\n"); ++ return -ENOMEM; ++ } ++ ++ ata_host_init(ata_host, ha->dev, &sas_sata_ops); ++ ++ ap = ata_sas_port_alloc(ata_host, &sata_port_info, shost); + if (!ap) { + SAS_DPRINTK("ata_sas_port_alloc failed.\n"); +- return -ENODEV; ++ rc = -ENODEV; ++ goto free_host; + } + + ap->private_data = found_dev; + ap->cbl = ATA_CBL_SATA; + ap->scsi_host = shost; + rc = ata_sas_port_init(ap); +- if (rc) { +- ata_sas_port_destroy(ap); +- return rc; +- } +- rc = ata_sas_tport_add(found_dev->sata_dev.ata_host.dev, ap); +- if (rc) { +- ata_sas_port_destroy(ap); +- return rc; +- } ++ if (rc) ++ goto destroy_port; ++ ++ rc = ata_sas_tport_add(ata_host->dev, ap); ++ if (rc) ++ goto destroy_port; ++ ++ found_dev->sata_dev.ata_host = ata_host; + found_dev->sata_dev.ap = ap; + + return 0; ++ ++destroy_port: ++ ata_sas_port_destroy(ap); ++free_host: ++ ata_host_put(ata_host); ++ return rc; + } + + void sas_ata_task_abort(struct sas_task *task) +--- a/drivers/scsi/libsas/sas_discover.c ++++ b/drivers/scsi/libsas/sas_discover.c +@@ -316,6 +316,8 @@ void sas_free_device(struct kref *kref) + if (dev_is_sata(dev) && dev->sata_dev.ap) { + ata_sas_tport_delete(dev->sata_dev.ap); + ata_sas_port_destroy(dev->sata_dev.ap); ++ ata_host_put(dev->sata_dev.ata_host); ++ dev->sata_dev.ata_host = NULL; + dev->sata_dev.ap = NULL; + } + +--- a/include/linux/libata.h ++++ b/include/linux/libata.h +@@ -1111,6 +1111,8 @@ extern struct ata_host *ata_host_alloc(s + extern struct ata_host *ata_host_alloc_pinfo(struct device *dev, + const struct ata_port_info * const * ppi, int n_ports); + extern int ata_slave_link_init(struct ata_port *ap); ++extern void ata_host_get(struct ata_host *host); ++extern void ata_host_put(struct ata_host *host); + extern int ata_host_start(struct ata_host *host); + extern int ata_host_register(struct ata_host *host, + struct scsi_host_template *sht); +--- a/include/scsi/libsas.h ++++ b/include/scsi/libsas.h +@@ -161,7 +161,7 @@ struct sata_device { + u8 port_no; /* port number, if this is a PM (Port) */ + + struct ata_port *ap; +- struct ata_host ata_host; ++ struct ata_host *ata_host; + struct smp_resp rps_resp ____cacheline_aligned; /* report_phy_sata_resp */ + u8 fis[ATA_RESP_FIS_SIZE]; + }; diff --git a/queue-4.18/series b/queue-4.18/series index 260128e0c68..db8424a3406 100644 --- a/queue-4.18/series +++ b/queue-4.18/series @@ -1,2 +1,3 @@ patch-scripts-kernel-doc.patch scripts-kernel-doc-escape-all-literal-braces-in-regexes.patch +scsi-libsas-dynamically-allocate-and-free-ata-host.patch