Re: [PATCH net] sctp: fix transport UAF via the retransmit queue

From: Xin Long

Date: Mon Jul 27 2026 - 13:51:13 EST


On Sun, Jul 26, 2026 at 2:05 AM Baul Lee <baul.lee@xxxxxxxx> wrote:
>
> sctp_assoc_rm_peer() clears the cached chunk->transport back-pointer on
> only two of the output queue's chunk lists before freeing the transport:
> peer->transmitted and asoc->outqueue.out_chunk_list. A DATA chunk parked
> on asoc->outqueue.retransmit keeps pointing at the transport, so once
> sctp_transport_free() drops the last reference and the object is
> RCU-freed, that chunk is left with a dangling pointer.
>
> While the chunk sits on the retransmit queue the dangling pointer is not
> followed: sctp_check_transmitted() skips the flight-size accounting both
> for transmitted_queue == &q->retransmit and for gap-acked chunks. Two
> steps remove that cover. First, sctp_outq_flush_rtx() moves a gap-acked
> chunk onto a live transport's transmitted list without reassigning
> chunk->transport, unlike the ordinary resend path, which rebinds it in
> __sctp_packet_append_chunk(). Second, a later SACK that reneges on the
> TSN clears tsn_gap_acked. The chunk now sits on a transmitted list with
> tsn_gap_acked == 0, so the next SACK reaches
>
> tchunk->transport->flight_size -= sctp_data_size(tchunk);
>
> a read-modify-write inside the freed sctp_transport.
>
> The transport is freed by an ASCONF Delete-IP and the faulting accesses
> are driven by ordinary SACKs, both coming from the association peer, so
> an application that speaks SCTP and accepts multihoming is enough to
> reach this. KASAN reports a slab-use-after-free read of 4 bytes at
> offset 216 of a freed kmalloc-1k sctp_transport in
> sctp_check_transmitted(), the object having been freed from
> sctp_assoc_rm_peer() via sctp_process_asconf().
>
> Clear ->transport for chunks on the retransmit queue as well, mirroring
> the existing out_chunk_list handling. The sink already guards with
> if (tchunk->transport), so a cleared back-pointer is skipped exactly like
> an already-acked chunk. The sacked and abandoned queues do not need the
> same treatment: chunks there never have ->transport dereferenced and are
> never migrated onto the retransmit queue or onto a transport's
> transmitted list.
>
> Discovered by XBOW, triaged by Baul Lee <baul.lee@xxxxxxxx>
> Reported privately to the maintainers on 2026-07-10 with root-cause
> analysis, a PoC, a KASAN log and this fix; posting to the list was
> requested as the follow-up.
>
> Fixes: df132eff4638 ("sctp: clear the transport of some out_chunk_list chunks in sctp_assoc_rm_peer")
I think the issue exists since the very beginning, Fixes tag should be:

Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")

> Reported-by: Federico Kirschbaum <federico.kirschbaum@xxxxxxxx>
> Reported-by: Baul Lee <baul.lee@xxxxxxxx>
> Cc: stable@xxxxxxxxxxxxxxx
> Signed-off-by: Baul Lee <baul.lee@xxxxxxxx>
> ---
> net/sctp/associola.c | 4 ++++
> 1 file changed, 4 insertions(+)
>
> diff --git a/net/sctp/associola.c b/net/sctp/associola.c
> index 62d3cc155809..c95f68d21670 100644
> --- a/net/sctp/associola.c
> +++ b/net/sctp/associola.c
> @@ -569,6 +569,10 @@ void sctp_assoc_rm_peer(struct sctp_association *asoc,
> sctp_transport_hold(active);
> }
>
> + list_for_each_entry(ch, &asoc->outqueue.retransmit, transmitted_list)
> + if (ch->transport == peer)
> + ch->transport = NULL;
> +
```
[sashiko-genimi]
This isn't a problem introduced by this patch, but does the new loop cover
the ordering where the gap-acked migration happens before the ASCONF
Delete-IP?

[sashiko-claude]
This is a pre-existing issue, but can gap-acked data chunks bypass this
cleanup if they were already migrated to another active transport's
transmitted list?
```

Both AI reviews report that the change may not cover the case where the
gap-acked data chunks bypass this cleanup if they were already migrated
to another active transport's transmitted list.

As you described in the changelog, the issue actually was caused by:

> ... sctp_outq_flush_rtx() moves a gap-acked
> chunk onto a live transport's transmitted list without reassigning
> chunk->transport.

Can you try fixing this in sctp_outq_flush_rtx() by updating
chunk->transport after both list_move_tail() calls? This makes sure the
chunk's transport pointer always matches the list it is queued on.

Thanks.