]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/commitdiff
greybus: sdio: fix work-queue leak and use-after-free
authorJohan Hovold <johan@hovoldconsulting.com>
Mon, 14 Sep 2015 18:19:04 +0000 (20:19 +0200)
committerGreg Kroah-Hartman <gregkh@google.com>
Tue, 15 Sep 2015 04:57:25 +0000 (21:57 -0700)
A single global work-queue pointer was used for the per-connection
workqueue, something which would lead to memory leaks and all sorts of
bad things if there are ever more than one SDIO connection in a system.

Also add the missing error handling when allocating the queue.

Signed-off-by: Johan Hovold <johan@hovoldconsulting.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
drivers/staging/greybus/sdio.c

index 14617e31782e66d7aa2edcacac0d873c466f4487..bfa1181074d670cd3645706a98f91f90061e9cc8 100644 (file)
@@ -25,6 +25,7 @@ struct gb_sdio_host {
        void                    *xfer_buffer;
        spinlock_t              xfer;   /* lock to cancel ongoing transfer */
        bool                    xfer_stop;
+       struct workqueue_struct *mrq_workqueue;
        struct work_struct      mrqwork;
        u8                      queued_events;
        bool                    removed;
@@ -32,7 +33,6 @@ struct gb_sdio_host {
        bool                    read_only;
 };
 
-static struct workqueue_struct *gb_sdio_mrq_workqueue;
 
 #define GB_SDIO_RSP_R1_R5_R6_R7        (GB_SDIO_RSP_PRESENT | GB_SDIO_RSP_CRC | \
                                 GB_SDIO_RSP_OPCODE)
@@ -497,7 +497,7 @@ static void gb_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
                goto out;
        }
 
-       queue_work(gb_sdio_mrq_workqueue, &host->mrqwork);
+       queue_work(host->mrq_workqueue, &host->mrqwork);
 
        mutex_unlock(&host->lock);
        return;
@@ -710,7 +710,12 @@ static int gb_sdio_connection_init(struct gb_connection *connection)
        }
        mutex_init(&host->lock);
        spin_lock_init(&host->xfer);
-       gb_sdio_mrq_workqueue = alloc_workqueue("gb_sdio_mrq", 0, 1);
+       host->mrq_workqueue = alloc_workqueue("mmc-%s", 0, 1,
+                                               dev_name(&connection->dev));
+       if (!host->mrq_workqueue) {
+               ret = -ENOMEM;
+               goto free_buffer;
+       }
        INIT_WORK(&host->mrqwork, gb_sdio_mrq_work);
 
        ret = mmc_add_host(mmc);
@@ -723,9 +728,9 @@ static int gb_sdio_connection_init(struct gb_connection *connection)
        return ret;
 
 free_work:
-       destroy_workqueue(gb_sdio_mrq_workqueue);
+       destroy_workqueue(host->mrq_workqueue);
+free_buffer:
        kfree(host->xfer_buffer);
-
 free_mmc:
        connection->private = NULL;
        mmc_free_host(mmc);
@@ -747,8 +752,8 @@ static void gb_sdio_connection_exit(struct gb_connection *connection)
        connection->private = NULL;
        mutex_unlock(&host->lock);
 
-       flush_workqueue(gb_sdio_mrq_workqueue);
-       destroy_workqueue(gb_sdio_mrq_workqueue);
+       flush_workqueue(host->mrq_workqueue);
+       destroy_workqueue(host->mrq_workqueue);
        mmc_remove_host(mmc);
        kfree(host->xfer_buffer);
        mmc_free_host(mmc);