]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
ata,scsi: libata-core: Do not leak memory for ata_port struct members
authorNiklas Cassel <cassel@kernel.org>
Sat, 29 Jun 2024 12:42:12 +0000 (14:42 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 5 Jul 2024 07:38:07 +0000 (09:38 +0200)
[ Upstream commit f6549f538fe0b2c389e1a7037f4e21039e25137a ]

libsas is currently not freeing all the struct ata_port struct members,
e.g. ncq_sense_buf for a driver supporting Command Duration Limits (CDL).

Add a function, ata_port_free(), that is used to free a ata_port,
including its struct members. It makes sense to keep the code related to
freeing a ata_port in its own function, which will also free all the
struct members of struct ata_port.

Fixes: 18bd7718b5c4 ("scsi: ata: libata: Handle completion of CDL commands using policy 0xD")
Reviewed-by: John Garry <john.g.garry@oracle.com>
Link: https://lore.kernel.org/r/20240629124210.181537-8-cassel@kernel.org
Signed-off-by: Niklas Cassel <cassel@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
drivers/ata/libata-core.c
drivers/scsi/libsas/sas_ata.c
drivers/scsi/libsas/sas_discover.c
include/linux/libata.h

index 2cf597edc013b7bdb034382704d816d3e4cc7290..62237df4fe77e39bacf84e70a052eefbf570f8f4 100644 (file)
@@ -5534,6 +5534,18 @@ struct ata_port *ata_port_alloc(struct ata_host *host)
        return ap;
 }
 
+void ata_port_free(struct ata_port *ap)
+{
+       if (!ap)
+               return;
+
+       kfree(ap->pmp_link);
+       kfree(ap->slave_link);
+       kfree(ap->ncq_sense_buf);
+       kfree(ap);
+}
+EXPORT_SYMBOL_GPL(ata_port_free);
+
 static void ata_devres_release(struct device *gendev, void *res)
 {
        struct ata_host *host = dev_get_drvdata(gendev);
@@ -5560,15 +5572,7 @@ static void ata_host_release(struct kref *kref)
        int i;
 
        for (i = 0; i < host->n_ports; i++) {
-               struct ata_port *ap = host->ports[i];
-
-               if (!ap)
-                       continue;
-
-               kfree(ap->pmp_link);
-               kfree(ap->slave_link);
-               kfree(ap->ncq_sense_buf);
-               kfree(ap);
+               ata_port_free(host->ports[i]);
                host->ports[i] = NULL;
        }
        kfree(host);
@@ -5951,7 +5955,7 @@ int ata_host_register(struct ata_host *host, const struct scsi_host_template *sh
         * allocation time.
         */
        for (i = host->n_ports; host->ports[i]; i++)
-               kfree(host->ports[i]);
+               ata_port_free(host->ports[i]);
 
        /* give ports names and add SCSI hosts */
        for (i = 0; i < host->n_ports; i++) {
index 12e2653846e3f0d93a9dee938eee0ba65df169e2..70891a1e98a0179a7abf3cdb8a80abbc769f8d52 100644 (file)
@@ -610,15 +610,15 @@ int sas_ata_init(struct domain_device *found_dev)
 
        rc = ata_sas_tport_add(ata_host->dev, ap);
        if (rc)
-               goto destroy_port;
+               goto free_port;
 
        found_dev->sata_dev.ata_host = ata_host;
        found_dev->sata_dev.ap = ap;
 
        return 0;
 
-destroy_port:
-       kfree(ap);
+free_port:
+       ata_port_free(ap);
 free_host:
        ata_host_put(ata_host);
        return rc;
index 8fb7c41c096245077c46b671209623d902fe9c3f..48d975c6dbf2cd99a8df6e447ac807889e19a51a 100644 (file)
@@ -301,7 +301,7 @@ void sas_free_device(struct kref *kref)
 
        if (dev_is_sata(dev) && dev->sata_dev.ap) {
                ata_sas_tport_delete(dev->sata_dev.ap);
-               kfree(dev->sata_dev.ap);
+               ata_port_free(dev->sata_dev.ap);
                ata_host_put(dev->sata_dev.ata_host);
                dev->sata_dev.ata_host = NULL;
                dev->sata_dev.ap = NULL;
index 324d792e7c7864c662b5f1af6cdedfa8a143c60f..186a6cbbbfbc688fcf591bde274895fcb7d68468 100644 (file)
@@ -1242,6 +1242,7 @@ extern int ata_slave_link_init(struct ata_port *ap);
 extern struct ata_port *ata_sas_port_alloc(struct ata_host *,
                                           struct ata_port_info *, struct Scsi_Host *);
 extern void ata_port_probe(struct ata_port *ap);
+extern void ata_port_free(struct ata_port *ap);
 extern int ata_sas_tport_add(struct device *parent, struct ata_port *ap);
 extern void ata_sas_tport_delete(struct ata_port *ap);
 extern int ata_sas_slave_configure(struct scsi_device *, struct ata_port *);