]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
4.18-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sun, 26 Aug 2018 06:46:01 +0000 (08:46 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sun, 26 Aug 2018 06:46:01 +0000 (08:46 +0200)
added patches:
scsi-libsas-dynamically-allocate-and-free-ata-host.patch

queue-4.18/scsi-libsas-dynamically-allocate-and-free-ata-host.patch [new file with mode: 0644]
queue-4.18/series

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 (file)
index 0000000..54f6219
--- /dev/null
@@ -0,0 +1,184 @@
+From 2fa4a32613c9182b00e46872755b0662374424a7 Mon Sep 17 00:00:00 2001
+From: Jason Yan <yanaijie@huawei.com>
+Date: Thu, 10 May 2018 11:05:16 +0800
+Subject: scsi: libsas: dynamically allocate and free ata host
+
+From: Jason Yan <yanaijie@huawei.com>
+
+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 <yanaijie@huawei.com>
+CC: John Garry <john.garry@huawei.com>
+CC: Taras Kondratiuk <takondra@cisco.com>
+CC: Tejun Heo <tj@kernel.org>
+Acked-by: Tejun Heo <tj@kernel.org>
+Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ 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];
+ };
index 260128e0c68cb89c291c984548beb0bdf393351a..db8424a34061c5de48f0bf8b001b78e52113bcc8 100644 (file)
@@ -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