* serial: pch_uart: possible TX DMA use-after-free race
@ 2026-10-19 10:54 LI xiao
2026-10-19 11:48 ` gregkh
0 siblings, 1 reply; 2+ messages in thread
From: LI xiao @ 2026-10-19 10:54 UTC (permalink / raw)
To: jirislaby, gregkh; +Cc: linux-serial, linux-kernel
Hello,
I would like to report a possible race in the PCH UART TX DMA path that may lead to a use-after-free.
I reviewed the code in Linux v7.3 (commit c03c1a14507dfaf8b856c2608f9ac216021fc222).
The relevant code paths are as follows.
pch_uart_interrupt() takes the UART port lock and calls dma_handle_tx() for a transmitter-holding-register-empty interrupt in drivers/tty/serial/pch_uart.c.
dma_handle_tx() checks tx_dma_use and, if no transfer is in progress, sets it to 1, allocates and prepares priv->sg_tx_p, maps the scatterlist, and calls dmaengine_prep_slave_sg() to prepare a new DMA transfer.
pch_dma_tx_complete() unmaps the completed transfer, sets tx_dma_use to 0, clears nent and orig_nent, frees priv->sg_tx_p, and re-enables the UART TX interrupt.
The UART interrupt handler takes uart_port_lock(), but pch_dma_tx_complete() does not appear to take the same lock.
I also noticed that the PCH DMA driver invokes the completion callback from its tasklet processing path while holding the DMA channel lock. The preparation path in pd_prep_slave_sg() calls pdc_desc_get(), which also acquires that lock.
A possible interleaving is as follows.
1. The completion callback finishes unmapping the old transfer and sets tx_dma_use to 0, but has not yet cleared the remaining state or freed priv->sg_tx_p.
2. A UART TX interrupt runs on another CPU and observes tx_dma_use == 0. If it proceeds far enough to allocate a new scatterlist and update priv->sg_tx_p before the old callback reaches its kfree() call, the shared pointer will refer to the new transfer's scatterlist.
3. The old completion callback then clears nent and orig_nent and calls kfree(priv->sg_tx_p). If the shared pointer has been overwritten as described above, this could free the new transfer's scatterlist rather than the scatterlist associated with the completed transfer.
4. The new transfer subsequently enters pd_prep_slave_sg() through dmaengine_prep_slave_sg(). Its call to pdc_desc_get() may have to wait for the DMA channel lock held by the completion callback. After the callback frees the scatterlist and releases the lock, the preparation path could continue and access the freed scatterlist, for example when reading sg_dma_address() or sg_dma_len(). This would constitute a potential use-after-free.
The same interleaving could also cause the old callback to overwrite the new transfer's nent and orig_nent values.
This scenario depends on the new transfer updating the shared scatterlist pointer before the old callback frees it. I have not established whether the required timing is reachable on the actual hardware, and I have not reproduced this issue at runtime. The available QEMU setup does not emulate the EG20T PCH UART/DMA device, and I do not have access to matching hardware. In particular, I have not verified whether the UART interrupt state and hardware timing permit a new TX interrupt to enter and progress far enough during the cleanup window.
Could you please advise whether this interleaving is possible and whether the TX state requires additional synchronization or a different ordering of the completion cleanup?
Thank you,
LI Xiao
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-10-19 12:10 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-19 10:54 serial: pch_uart: possible TX DMA use-after-free race LI xiao
2026-10-19 11:48 ` gregkh
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®