Re: [PATCH v1] dmaengine: qcom: gpi: Fix channel cleanup in unwind path
[email protected] Mon, 03 Aug 2026 12:16:42 +0000
| Newsgroups | org.kernel.vger.dmaengine |
|---|---|
| Message-ID | <[email protected]> |
Thank you for your contribution! Sashiko AI review found 5 potential issue(= s) to consider: New issues: - [High] Incomplete Error Cleanup / Missing State Restoration causes 750ms = kernel stalls per channel. - [High] Missing state reversion combined with ring freeing causes IOMMU ha= rdware faults (base address 0x0) on reallocation. - [High] Hardware state leak and DMA Use-After-Free of the event ring. Pre-existing issues: - [High] Dangling pointer in `gchan->config` leads to Use-After-Free upon c= hannel reallocation. - [High] Deferred shared hardware initialization leaves a dangling channel = vulnerable to MMIO bus faults. -- --- Patch [1]: [PATCH v1] dmaengine: qcom: gpi: Fix channel cleanup in unwi= nd path --- commit a2ee269fe4b77d9be025b81af8f6b123417e0e5c Author: Aniket Randive <[email protected]> dmaengine: qcom: gpi: Fix channel cleanup in unwind path This commit attempts to fix channel cleanup in the gpi_ch_init() error path. It corrects the channel indices used in the unwind loops, restores pm_state to DISABLE_STATE, and frees ch_ring on initialization failure. > diff --git a/drivers/dma/qcom/gpi.c b/drivers/dma/qcom/gpi.c > index a5055a6273af6..c41bfac65ddf3 100644 > --- a/drivers/dma/qcom/gpi.c > +++ b/drivers/dma/qcom/gpi.c > @@ -1965,16 +1965,19 @@ static int gpi_ch_init(struct gchan *gchan) > error_start_chan: > for (i =3D i - 1; i >=3D 0; i--) { > gpi_stop_chan(&gpii->gchan[i]); > - gpi_send_cmd(gpii, gchan, GPI_CH_CMD_RESET); > + gpi_send_cmd(gpii, &gpii->gchan[i], GPI_CH_CMD_RESET); [Severity: High] If gpi_start_chan() fails for channel 1, this error path stops and resets channel 0, but does it leave gpii->gchan[0].pm_state as ACTIVE_STATE? If gpii->pm_state is forced to DISABLE_STATE later in this unwind path, when the client releases channel 0, gpi_free_chan_resources() might see gchan[0].pm_state =3D=3D ACTIVE_STATE and attempt to send STOP, RESET, and = DE_ALLOC commands. Because gpii->pm_state is DISABLE_STATE, won't the interrupt handler drop completion interrupts, causing these commands to time out and stall for 750= ms? > } > i =3D 2; > error_alloc_chan: > for (i =3D i - 1; i >=3D 0; i--) > - gpi_reset_chan(gchan, GPI_CH_CMD_DE_ALLOC); > + gpi_reset_chan(&gpii->gchan[i], GPI_CH_CMD_DE_ALLOC); > error_alloc_ev_ring: > gpi_disable_interrupts(gpii); > error_config_int: > gpi_free_ring(&gpii->ev_ring, gpii); > + write_lock_irq(&gpii->pm_lock); > + gpii->pm_state =3D DISABLE_STATE; > + write_unlock_irq(&gpii->pm_lock); [Severity: High] If initialization fails after the hardware event channel is allocated, we f= ree the event ring DMA memory here and force gpii->pm_state to DISABLE_STATE. Later, when gpi_free_chan_resources() is called for the active channel, will it skip sending GPI_EV_CMD_DEALLOC because cur_state is no longer ACTIVE_ST= ATE? Could this leave the hardware event channel active, leading to a DMA use-after-free on the freed event ring memory? > exit_gpi_init: > return ret; > } > @@ -2065,6 +2068,8 @@ static int gpi_alloc_chan_resources(struct dma_chan= *chan) > goto xfer_alloc_err; > =20 > ret =3D gpi_ch_init(gchan); > + if (ret) > + gpi_free_ring(&gchan->ch_ring, gpii); [Severity: High] When gpi_ch_init() fails, it appears gchan->pm_state remains set to CONFIG_STATE. If we free and zero the ch_ring here without reverting gchan->pm_state, what happens if a DMA client retries channel allocation? Would a subsequent call to gpi_ch_init() bypass hardware initialization because pm_state is already CONFIG_STATE, and program the hardware with a zeroed physical address from the freed ring, leading to an IOMMU fault? > =20 > mutex_unlock(&gpii->ctrl_lock); > =20 [Severity: High] This is a pre-existing issue, but I noticed a potential dangling pointer wh= en a DMA channel is released. In drivers/dma/qcom/gpi.c:gpi_free_chan_resources(): vchan_free_chan_resources(&gchan->vc); kfree(gchan->config); Since gchan persists across clients, if a new client allocates the same cha= nnel and calls dmaengine_slave_config(), this executes: drivers/dma/qcom/gpi.c:gpi_peripheral_config(): new_config =3D krealloc(gchan->config, config->peripheral_size, GFP= _NOWAIT); Does this pass a freed, non-null pointer into krealloc() if gchan->config w= asn't set to NULL after kfree()? [Severity: High] This is a pre-existing issue, but could deferred hardware initialization cr= ash the system if a client only requests one channel? In drivers/dma/qcom/gpi.c:gpi_ch_init(): for (i =3D 0; i < MAX_CHANNELS_PER_GPII; i++) if (gpii->gchan[i].pm_state !=3D CONFIG_STATE) goto exit_gpi_init; If the first channel reaches CONFIG_STATE, it returns success immediately. If a client then issues a transfer without initializing a second channel: drivers/dma/qcom/gpi.c:gpi_issue_pending(): gpi_desc->db =3D ch_ring->wp; gpi_write_ch_db(gchan, &gchan->ch_ring, gpi_desc->db); Won't writing to the MMIO doorbell of an uninitialized (and potentially unclocked) block cause a bus fault? --=20 Sashiko AI review =C2=B7 https://sashiko.dev/#/patchset/20260803120316.2703= [email protected]?part=3D1