Linux Serial subsystem development
 help / color / mirror / Atom feed
* [PATCH v2 0/2] serial: tegra: fix RX/TX DMA descriptor use-after-free
@ 2026-10-15  3:29 Austin Schlegel via B4 Relay
  2026-10-15  3:30 ` [PATCH v2 1/2] serial: tegra: fix RX " Austin Schlegel via B4 Relay
  2026-10-15  3:30 ` [PATCH v2 2/2] serial: tegra: fix TX " Austin Schlegel via B4 Relay
  0 siblings, 2 replies; 5+ messages in thread
From: Austin Schlegel via B4 Relay @ 2026-10-15  3:29 UTC (permalink / raw)
  To: Laxman Dewangan, Greg Kroah-Hartman, Jiri Slaby, Thierry Reding,
	Jonathan Hunter
  Cc: linux-kernel, linux-serial, linux-tegra, Austin Schlegel,
	Austin Schlegel, stable

Both patches in this series fix the same bug pattern in
drivers/tty/serial/serial-tegra.c: dmaengine_terminate_all() is called
before async_tx_ack() on the DMA descriptor it just terminated. With
the GPC DMA driver (tegra186-gpc-dma.c, used on Tegra194/Tegra234),
terminating a channel frees the active descriptor synchronously via
vchan_dma_desc_free_list(), so the subsequent ack touches freed
memory. dmaengine clients are not allowed to touch a descriptor once
it has been terminated.

Patch 1/2 (RX) was found via a KFENCE use-after-free report on a
Jetson AGX Orin (Tegra234) under UART loopback traffic, and the fix
has since been soak tested for 24+ hours with
kfence.sample_interval=1 with zero further reports. It keeps the
descriptor-leak fix from commit b31245b94207 ("serial: tegra: ack
the rx dma desc after transfer terminated") intact: the ack still
happens for every terminated RX transfer, just before the descriptor
is freed instead of after.

Patch 2/2 (TX) is the same ordering bug in the analogous TX path
(tegra_uart_stop_tx()), present since the driver was added, and found
by code inspection once the RX bug was understood. It has not been
reproduced on hardware - triggering it requires stopping an in-flight
TX DMA transfer at the right moment, which did not occur during RX
testing - but it is the identical dmaengine_terminate_all()-then-ack
pattern in the same driver against the same DMA engine, so I'm sending
it alongside the RX fix rather than leaving it unfixed.

Testing was done on Tegra234 (Jetson AGX Orin) only.

Signed-off-by: Austin Schlegel <schlegelas@outlook.com>
---
Changes in v2:
- Commit message updates only; no code changes.
- Patch 1: removed an inaccurate statement that the freed descriptor
  is accessed in tegra_uart_rx_dma_complete(); updated the soak test
  duration to 24+ hours.
- Patch 2: removed an inaccurate flush example; added Fixes: tag.
- Switched to my personal email address (v1 was sent from
  austin.schlegel@arthrex.com).
- Link to v1: https://lore.kernel.org/r/20261001-tty-linus-v1-0-ecb8576c802d@arthrex.com

---
Austin Schlegel (2):
      serial: tegra: fix RX DMA descriptor use-after-free
      serial: tegra: fix TX DMA descriptor use-after-free

 drivers/tty/serial/serial-tegra.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)
---
base-commit: a90ee4305c4a5df72c11b31dacfdc76e00fcf78a
change-id: 20261014-serial-tegra-dma-uaf-39ed62701ab6

Best regards,
-- 
Austin Schlegel <schlegelas@outlook.com>



^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH v2 1/2] serial: tegra: fix RX DMA descriptor use-after-free
  2026-10-15  3:29 [PATCH v2 0/2] serial: tegra: fix RX/TX DMA descriptor use-after-free Austin Schlegel via B4 Relay
@ 2026-10-15  3:30 ` Austin Schlegel via B4 Relay
  2026-10-15  3:43   ` sashiko-bot
  2026-10-15  3:30 ` [PATCH v2 2/2] serial: tegra: fix TX " Austin Schlegel via B4 Relay
  1 sibling, 1 reply; 5+ messages in thread
From: Austin Schlegel via B4 Relay @ 2026-10-15  3:30 UTC (permalink / raw)
  To: Laxman Dewangan, Greg Kroah-Hartman, Jiri Slaby, Thierry Reding,
	Jonathan Hunter
  Cc: linux-kernel, linux-serial, linux-tegra, Austin Schlegel,
	Austin Schlegel, stable

From: Austin Schlegel <schlegelas@outlook.com>

tegra_uart_terminate_rx_dma() calls dmaengine_terminate_all() and then
tegra_uart_rx_buffer_push(), which calls async_tx_ack(tup->rx_dma_desc)
on the just-terminated descriptor. With the GPC DMA driver,
dmaengine_terminate_all() frees the active descriptor immediately, so
the subsequent async_tx_ack() touches freed memory. dmaengine clients
are not allowed to touch a descriptor once it has been terminated.

Move the ack into the two call sites that still own a live descriptor:
tegra_uart_rx_dma_complete() (before the completion callback hands the
descriptor back) and tegra_uart_terminate_rx_dma() (before terminating),
and drop it from tegra_uart_rx_buffer_push() itself, since that function
no longer has a descriptor it's safe to ack.

The ack was originally added in commit b31245b94207 ("serial: tegra:
ack the rx dma desc after transfer terminated") to avoid a descriptor
leak with the Tegra20 APB DMA driver. Keep that fix intact: the ack
still happens for every terminated RX transfer, just before the
descriptor is freed instead of after.

Found by inspection while chasing the KFENCE report below on a Jetson
AGX Orin (Tegra234) with UART loopback:

BUG: KFENCE: use-after-free read in tegra_uart_rx_buffer_push+0x38/0x168
 tegra_uart_rx_buffer_push+0x38/0x168
 tegra_uart_terminate_rx_dma+0x88/0xf8
 tegra_uart_isr+0x380/0x470

allocated by task 0 on cpu 0 (~9 ms earlier):
 tegra_dma_prep_slave_sg+0x134/0x3c0
 tegra_uart_start_rx_dma.isra.0+0xc4/0x138
 tegra_uart_isr+0x340/0x470

freed by task 0 on cpu 0:
 tegra_dma_desc_free+0x1c/0x30
 vchan_dma_desc_free_list+0x10c/0x160
 tegra_dma_terminate_all+0x21c/0x290
 tegra_uart_terminate_rx_dma+0x7c/0xf8

Soak tested on Tegra234 (Jetson AGX Orin) for 24+ hours with
kfence.sample_interval=1 and continuous UART loopback traffic: zero
KFENCE reports, tx/rx byte counts in /proc/tty/driver/tegra_hsuart
remained equal throughout with no framing or break errors.

Fixes: b31245b94207 ("serial: tegra: ack the rx dma desc after transfer terminated")
Cc: stable@vger.kernel.org
Assisted-by: LLM
Signed-off-by: Austin Schlegel <schlegelas@outlook.com>
---
v2:
 - Reworded commit: Removed inaccurate statement claiming the freed-descriptor is accessed
   in tegra_uart_rx_dma_complete().
 - Reworded commit: Soak test ran for 24+ hours, so updated from 13 to 24.
 - Switched to personal email (v1 sent from austin.schlegel@arthrex.com)

v1: https://lore.kernel.org/all/20261001-tty-linus-v1-1-ecb8576c802d@arthrex.com/
---
 drivers/tty/serial/serial-tegra.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/tty/serial/serial-tegra.c b/drivers/tty/serial/serial-tegra.c
index b0fc4ae3686f..a23a92cebe0d 100644
--- a/drivers/tty/serial/serial-tegra.c
+++ b/drivers/tty/serial/serial-tegra.c
@@ -730,7 +730,6 @@ static void tegra_uart_rx_buffer_push(struct tegra_uart_port *tup,
 	struct tty_port *port = &tup->uport.state->port;
 	unsigned int count;
 
-	async_tx_ack(tup->rx_dma_desc);
 	count = tup->rx_bytes_requested - residue;
 
 	/* If we are here, DMA is stopped */
@@ -761,6 +760,7 @@ static void tegra_uart_rx_dma_complete(void *args)
 		set_rts(tup, false);
 
 	tup->rx_dma_active = false;
+	async_tx_ack(tup->rx_dma_desc);
 	tegra_uart_rx_buffer_push(tup, 0);
 	tegra_uart_start_rx_dma(tup);
 
@@ -783,6 +783,7 @@ static void tegra_uart_terminate_rx_dma(struct tegra_uart_port *tup)
 
 	dmaengine_pause(tup->rx_dma_chan);
 	dmaengine_tx_status(tup->rx_dma_chan, tup->rx_cookie, &state);
+	async_tx_ack(tup->rx_dma_desc);
 	dmaengine_terminate_all(tup->rx_dma_chan);
 
 	tegra_uart_rx_buffer_push(tup, state.residue);

-- 
2.53.0



^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH v2 2/2] serial: tegra: fix TX DMA descriptor use-after-free
  2026-10-15  3:29 [PATCH v2 0/2] serial: tegra: fix RX/TX DMA descriptor use-after-free Austin Schlegel via B4 Relay
  2026-10-15  3:30 ` [PATCH v2 1/2] serial: tegra: fix RX " Austin Schlegel via B4 Relay
@ 2026-10-15  3:30 ` Austin Schlegel via B4 Relay
  2026-10-15  3:41   ` sashiko-bot
  1 sibling, 1 reply; 5+ messages in thread
From: Austin Schlegel via B4 Relay @ 2026-10-15  3:30 UTC (permalink / raw)
  To: Laxman Dewangan, Greg Kroah-Hartman, Jiri Slaby, Thierry Reding,
	Jonathan Hunter
  Cc: linux-kernel, linux-serial, linux-tegra, Austin Schlegel,
	Austin Schlegel, stable

From: Austin Schlegel <schlegelas@outlook.com>

tegra_uart_stop_tx() calls dmaengine_terminate_all() and then
async_tx_ack(tup->tx_dma_desc) on the descriptor that was just
terminated. With the GPC DMA driver, dmaengine_terminate_all() frees
the active descriptor immediately, so the ack call that follows
touches freed memory, the same use-after-free pattern fixed for the
RX path in tegra_uart_terminate_rx_dma() ("serial: tegra: fix RX DMA
descriptor use-after-free"). dmaengine clients must not touch a
descriptor once dmaengine_terminate_all() has returned.

Move the ack before dmaengine_terminate_all(), while the descriptor
is still owned by the driver. tegra_uart_tx_dma_complete() already
acks inside the completion callback, where the descriptor is valid,
and is unaffected.

Found by code inspection while fixing the analogous RX bug; not
reproduced on hardware, since triggering it requires stopping an
in-flight TX DMA transfer at the right moment. The RX and TX
paths share the same dmaengine_terminate_all()-then-ack structure
and the same root cause.

Fixes: e9ea096dd225 ("serial: tegra: add serial driver")
Assisted-by: LLM
Cc: stable@vger.kernel.org
Signed-off-by: Austin Schlegel <schlegelas@outlook.com>
---
v2:
 - Reworded commit: Removed inaccurate flush example
 - Switched to personal email (v1 sent from austin.schlegel@arthrex.com)

v1: https://lore.kernel.org/all/20261001-tty-linus-v1-2-ecb8576c802d@arthrex.com/
---
 drivers/tty/serial/serial-tegra.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/tty/serial/serial-tegra.c b/drivers/tty/serial/serial-tegra.c
index a23a92cebe0d..306a38b2fb1e 100644
--- a/drivers/tty/serial/serial-tegra.c
+++ b/drivers/tty/serial/serial-tegra.c
@@ -639,9 +639,9 @@ static void tegra_uart_stop_tx(struct uart_port *u)
 
 	dmaengine_pause(tup->tx_dma_chan);
 	dmaengine_tx_status(tup->tx_dma_chan, tup->tx_cookie, &state);
+	async_tx_ack(tup->tx_dma_desc);
 	dmaengine_terminate_all(tup->tx_dma_chan);
 	count = tup->tx_bytes_requested - state.residue;
-	async_tx_ack(tup->tx_dma_desc);
 	uart_xmit_advance(&tup->uport, count);
 	tup->tx_in_progress = 0;
 }

-- 
2.53.0



^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: [PATCH v2 2/2] serial: tegra: fix TX DMA descriptor use-after-free
  2026-10-15  3:30 ` [PATCH v2 2/2] serial: tegra: fix TX " Austin Schlegel via B4 Relay
@ 2026-10-15  3:41   ` sashiko-bot
  0 siblings, 0 replies; 5+ messages in thread
From: sashiko-bot @ 2026-10-15  3:41 UTC (permalink / raw)
  To: Austin Schlegel; +Cc: linux-serial

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] serial: tegra: UAF and double xmit advance in tx_dma_complete
- [High] serial: tegra: UAF in tegra_uart_stop_tx() due to stale tx_in_progress after flush
--

commit 7f4e5d2d923132e41e93a227ae34dba2a8eda9f6
Author: Austin Schlegel <schlegelas@outlook.com>

serial: tegra: fix TX DMA descriptor use-after-free

This patch moves async_tx_ack() before dmaengine_terminate_all() in
tegra_uart_stop_tx() to prevent a use-after-free when the DMA descriptor is
immediately freed by the termination.

> diff --git a/drivers/tty/serial/serial-tegra.c b/drivers/tty/serial/serial-tegra.c
> index a23a92cebe0df..306a38b2fb1e7 100644
> --- a/drivers/tty/serial/serial-tegra.c
> +++ b/drivers/tty/serial/serial-tegra.c
> @@ -639,9 +639,9 @@ static void tegra_uart_stop_tx(struct uart_port *u)
>  
>  	dmaengine_pause(tup->tx_dma_chan);
>  	dmaengine_tx_status(tup->tx_dma_chan, tup->tx_cookie, &state);
> +	async_tx_ack(tup->tx_dma_desc);

[Severity: High]
Does this create a use-after-free if tegra_uart_flush_buffer() runs first?

If tegra_uart_flush_buffer() is called via the TCFLSH ioctl, it terminates
the DMA channel and frees the descriptor:

drivers/tty/serial/serial-tegra.c:tegra_uart_flush_buffer() {
	...
	if (tup->tx_dma_chan)
		dmaengine_terminate_all(tup->tx_dma_chan);
}

But it fails to clear tup->tx_in_progress. If tegra_uart_stop_tx() is then
called, it bypasses the tx_in_progress check and calls async_tx_ack() on
the freed tx_dma_desc.

>  	dmaengine_terminate_all(tup->tx_dma_chan);

[Severity: High]
Can this termination race with tegra_uart_tx_dma_complete() and cause a UAF
and double accounting?

tegra_uart_tx_dma_complete() runs asynchronously and accesses tup->tx_dma_desc
outside of the UART port lock:

drivers/tty/serial/serial-tegra.c:tegra_uart_tx_dma_complete() {
	...
	async_tx_ack(tup->tx_dma_desc);
	uart_port_lock_irqsave(&tup->uport, &flags);
	uart_xmit_advance(&tup->uport, count);
	...
}

tegra_uart_stop_tx() runs under the port lock and terminates the DMA channel,
which immediately frees the descriptor. If tegra_uart_stop_tx() runs
concurrently after tegra_uart_tx_dma_complete() starts but before it acquires
the lock, the completion callback will trigger a use-after-free on the
descriptor.

Furthermore, both functions calculate count and advance the FIFO. If they
race, tegra_uart_tx_dma_complete() will blindly call uart_xmit_advance() a
second time inside the lock, skipping untransmitted characters and corrupting
the ring buffer state.

>  	count = tup->tx_bytes_requested - state.residue;
> -	async_tx_ack(tup->tx_dma_desc);
>  	uart_xmit_advance(&tup->uport, count);
>  	tup->tx_in_progress = 0;
>  }

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261014-serial-tegra-dma-uaf-v2-0-b142b31b67e3@outlook.com?part=2

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH v2 1/2] serial: tegra: fix RX DMA descriptor use-after-free
  2026-10-15  3:30 ` [PATCH v2 1/2] serial: tegra: fix RX " Austin Schlegel via B4 Relay
@ 2026-10-15  3:43   ` sashiko-bot
  0 siblings, 0 replies; 5+ messages in thread
From: sashiko-bot @ 2026-10-15  3:43 UTC (permalink / raw)
  To: Austin Schlegel; +Cc: linux-serial

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] tty: serial: tegra: DMA tasklets race with termination causing corruption
--

commit 829540ff4c493e2669ac25a22f37fd2da4f2c018
Author: Austin Schlegel <schlegelas@outlook.com>

serial: tegra: fix RX DMA descriptor use-after-free

This commit fixes a use-after-free by moving the async_tx_ack() call to
tegra_uart_rx_dma_complete() and tegra_uart_terminate_rx_dma() before the
descriptors are freed. It removes the ack from tegra_uart_rx_buffer_push()
where the descriptor is no longer safe to access.

Link: https://lore.kernel.org/all/20261001-tty-linus-v1-1-ecb8576c802d@arthrex.com/

> --- a/drivers/tty/serial/serial-tegra.c
> +++ b/drivers/tty/serial/serial-tegra.c

[ ... ]

> @@ -761,6 +760,7 @@ static void tegra_uart_rx_dma_complete(void *args)
>  		set_rts(tup, false);
>  
>  	tup->rx_dma_active = false;
> +	async_tx_ack(tup->rx_dma_desc);
>  	tegra_uart_rx_buffer_push(tup, 0);
>  	tegra_uart_start_rx_dma(tup);
>  

[Severity: High]
Can this callback race with concurrent DMA termination?

If tegra_uart_stop_rx() or tegra_uart_terminate_rx_dma() is called
concurrently (for instance, when userspace calls tcsetattr() or closes
the port while data is transferring), CPU 0 might terminate the DMA,
acknowledge the descriptor, and update the state. Meanwhile, CPU 1's DMA
completion tasklet could be spinning on the uart_port_lock.

Once CPU 1 acquires the lock, it appears to unconditionally execute this
block without checking if the channel was already intentionally terminated
(e.g., by checking if tup->rx_dma_active is still true). Could this
redundantly push buffers, erroneously access the potentially freed
descriptor via async_tx_ack(), and incorrectly restart the DMA?

Additionally, does a similar race condition exist in the TX path? In
tegra_uart_tx_dma_complete(), the callback accesses the descriptor outside
the lock and unconditionally advances the TX ring, which seems like it could
race destructively with tegra_uart_stop_tx().

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261014-serial-tegra-dma-uaf-v2-0-b142b31b67e3@outlook.com?part=1

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2026-10-15  3:43 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-15  3:29 [PATCH v2 0/2] serial: tegra: fix RX/TX DMA descriptor use-after-free Austin Schlegel via B4 Relay
2026-10-15  3:30 ` [PATCH v2 1/2] serial: tegra: fix RX " Austin Schlegel via B4 Relay
2026-10-15  3:43   ` sashiko-bot
2026-10-15  3:30 ` [PATCH v2 2/2] serial: tegra: fix TX " Austin Schlegel via B4 Relay
2026-10-15  3:41   ` sashiko-bot

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox