Re: [PATCH v2] gpib: fmh_gpib: Fix resource leaks in fmh_gpib_attach_impl
From: Dominik Karol Piątkowski
Date: Thu May 28 2026 - 13:08:12 EST
Hi Hongling Zeng,
On Thursday, May 28th, 2026 at 03:50, Hongling Zeng <zenghongling@xxxxxxxxxx> wrote:
> The fmh_gpib_attach_impl() function has multiple resource leaks in its
> error handling paths. When any initialization step fails, the function
> returns early without properly releasing previously acquired resources.
>
> Fix by adding proper error handling labels and cleanup code that releases
> resources in the reverse order they were acquired.
>
> Fixes: 8e4841a0888c7 ("staging: gpib: Add Frank Mori Hess FPGA PCI GPIB driver")
> Signed-off-by: Hongling Zeng <zenghongling@xxxxxxxxxx>
> Cc: stable@xxxxxxxxxxxxxxx
> Suggested-by: Dominik Karol Piątkowski <dominik.karol.piatkowski@xxxxxxxxxxxxxx>
>
> ---
> Changes in v2:
> - Fixed unnecessary retval assignments in early error returns
> - Removed extra newline
> - Kept e_priv->irq assignment after request_irq() succeeds,as suggested by Dominik Karol
> ---
> drivers/gpib/fmh_gpib/fmh_gpib.c | 37 +++++++++++++++++++++++++-------
> 1 file changed, 29 insertions(+), 8 deletions(-)
>
> diff --git a/drivers/gpib/fmh_gpib/fmh_gpib.c b/drivers/gpib/fmh_gpib/fmh_gpib.c
> index fcafdc02ea2e..404379cd1565 100644
> --- a/drivers/gpib/fmh_gpib/fmh_gpib.c
> +++ b/drivers/gpib/fmh_gpib/fmh_gpib.c
> @@ -1418,7 +1418,8 @@ static int fmh_gpib_attach_impl(struct gpib_board *board, const struct gpib_boar
> resource_size(e_priv->gpib_iomem_res));
> if (!nec_priv->mmiobase) {
> dev_err(board->dev, "Could not map I/O memory\n");
> - return -ENOMEM;
> + retval = -ENOMEM;
> + goto err_release_gpib_region;
> }
> dev_dbg(board->dev, "iobase %pr remapped to %p\n",
> e_priv->gpib_iomem_res, nec_priv->mmiobase);
> @@ -1426,34 +1427,39 @@ static int fmh_gpib_attach_impl(struct gpib_board *board, const struct gpib_boar
> res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dma_fifos");
> if (!res) {
> dev_err(board->dev, "Unable to locate mmio resource for gpib dma port\n");
> - return -ENODEV;
> + retval = -ENODEV;
> + goto err_iounmap_gpib;
> }
> if (request_mem_region(res->start,
> resource_size(res),
> pdev->name) == NULL) {
> dev_err(board->dev, "cannot claim registers\n");
> - return -ENXIO;
> + retval = -ENXIO;
> + goto err_iounmap_gpib;
> }
> e_priv->dma_port_res = res;
> e_priv->fifo_base = ioremap(e_priv->dma_port_res->start,
> resource_size(e_priv->dma_port_res));
> if (!e_priv->fifo_base) {
> dev_err(board->dev, "Could not map I/O memory for fifos\n");
> - return -ENOMEM;
> + retval = -ENOMEM;
> + goto err_release_dma_region;
> }
> dev_dbg(board->dev, "dma fifos 0x%lx remapped to %p, length=%ld\n",
> (unsigned long)e_priv->dma_port_res->start, e_priv->fifo_base,
> (unsigned long)resource_size(e_priv->dma_port_res));
>
> irq = platform_get_irq(pdev, 0);
> - if (irq < 0)
> - return -EBUSY;
> + if (irq < 0) {
> + retval = -EBUSY;
> + goto err_iounmap_fifo;
> + }
> retval = request_irq(irq, fmh_gpib_interrupt, IRQF_SHARED, pdev->name, board);
> if (retval) {
> dev_err(board->dev,
> "cannot register interrupt handler err=%d\n",
> retval);
> - return retval;
> + goto err_iounmap_fifo;
> }
> e_priv->irq = irq;
>
> @@ -1461,7 +1467,8 @@ static int fmh_gpib_attach_impl(struct gpib_board *board, const struct gpib_boar
> e_priv->dma_channel = dma_request_slave_channel(board->dev, "rxtx");
> if (!e_priv->dma_channel) {
> dev_err(board->dev, "failed to acquire dma channel \"rxtx\".\n");
> - return -EIO;
> + retval = -EIO;
> + goto err_free_irq;
> }
> }
> /*
> @@ -1473,6 +1480,20 @@ static int fmh_gpib_attach_impl(struct gpib_board *board, const struct gpib_boar
> fifo_max_burst_length_mask;
>
> return fmh_gpib_init(e_priv, board, handshake_mode);
> +
> +err_free_irq:
> + free_irq(e_priv->irq, board);
> +err_iounmap_fifo:
> + iounmap(e_priv->fifo_base);
> +err_release_dma_region:
> + release_mem_region(e_priv->dma_port_res->start,
> + resource_size(e_priv->dma_port_res));
> +err_iounmap_gpib:
> + iounmap(nec_priv->mmiobase);
> +err_release_gpib_region:
> + release_mem_region(e_priv->gpib_iomem_res->start,
> + resource_size(e_priv->gpib_iomem_res));
> + return retval;
I see a problem with this patch.
fmh_gpib_attach_impl is called from fmh_gpib_attach_holdoff_(all|end), and these
are passed as attach through fmh_gpib_(unaccel_)interface. The only place where
I see attach is called, is in common/iblib.c, in ibonline function. If attach
fails (that is, returns with -errno), detach is called (fmh_gpib_detach for
this case), where a proper cleanup is performed.
If we release the resources in attach and not clear corresponding e_priv fields,
we will fail badly in detach, as we do double free/unmap and use after free.
Clearing e_priv fields will result in having two competing cleanup routines,
and I don't like that idea - let's have one proper cleanup in one place. The
only way to keep this approach would be to rewrite the core gpib logic for
attach/detach, and I don't know if it's worth the effort - probably not.
Thanks,
Dominik Karol
> }
>
> int fmh_gpib_attach_holdoff_all(struct gpib_board *board, const struct gpib_board_config *config)
> --
> 2.25.1
>
>