staging: comedi: ni_labpc: fix possible double-free of dma_buffer
authorIan Abbott <abbotti@mev.co.uk>
Fri, 28 Jun 2013 16:09:17 +0000 (17:09 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 23 Jul 2013 21:27:50 +0000 (14:27 -0700)
If `labpc_attach()` allocates memory for `devpriv->dma_buffer` but fails
to request a DMA channel, it frees `devpriv->dma_buffer` but leaves the
pointer set.  Later, `labpc_detach()` frees `devpriv->dma_buffer` again,
which means it has been freed twice in this case.

Fix it by only setting `devpriv->dma_buffer` in `labpc_attach()` if the
DMA channel was requested successfully.

Signed-off-by: Ian Abbott <abbotti@mev.co.uk>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/staging/comedi/drivers/ni_labpc.c

index 3e95ac5..4ff1ed0 100644 (file)
@@ -1712,13 +1712,15 @@ static int labpc_attach(struct comedi_device *dev, struct comedi_devconfig *it)
 
 #ifdef CONFIG_ISA_DMA_API
        if (dev->irq && (dma_chan == 1 || dma_chan == 3)) {
-               devpriv->dma_buffer = kmalloc(dma_buffer_size,
-                                             GFP_KERNEL | GFP_DMA);
-               if (devpriv->dma_buffer) {
+               void *dma_buffer = kmalloc(dma_buffer_size,
+                                          GFP_KERNEL | GFP_DMA);
+
+               if (dma_buffer) {
                        ret = request_dma(dma_chan, dev->board_name);
                        if (ret == 0) {
                                unsigned long dma_flags;
 
+                               devpriv->dma_buffer = dma_buffer;
                                devpriv->dma_chan = dma_chan;
                                devpriv->dma_addr =
                                        virt_to_bus(devpriv->dma_buffer);
@@ -1728,7 +1730,7 @@ static int labpc_attach(struct comedi_device *dev, struct comedi_devconfig *it)
                                set_dma_mode(devpriv->dma_chan, DMA_MODE_READ);
                                release_dma_lock(dma_flags);
                        } else {
-                               kfree(devpriv->dma_buffer);
+                               kfree(dma_buffer);
                        }
                }
        }