5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Hyunwoo Kim <redacted>
commit 03a9d10ecf71f54b2af8020935f2033d4a132be5 upstream.
sctp_rcv() resolves the transport once per packet and leaves it in
chunk->transport. The lookup reference, or the one sctp_add_backlog() takes
if the socket is owned by userspace, keeps it around until the chunk has
been processed.
An authenticated ASCONF DEL-IP can remove it in the meantime.
sctp_assoc_rm_peer() takes the transport out of the association and calls
sctp_transport_free(), which tags it dead and drops the reference the
association held. There is a window on both paths: the packet can sit on
the socket backlog, and on the direct path the lookup completes before
bh_lock_sock().
The DATA chunk in that packet puts the removed transport back into
asoc->peer.last_data_from. Once the packet is done that reference goes
away and the transport is freed by RCU, so the next delayed SACK carries
the pointer into the SACK chunk and sctp_outq_select_transport() reads the
freed transport's state.
Drop the chunk in sctp_inq_push(), next to the existing rcvr->dead check.
Both paths reach it with the association's socket lock held. The peer
retransmits it.
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Cc: <redacted>
Signed-off-by: Hyunwoo Kim <redacted>
Acked-by: Xin Long <lucien.xin@gmail.com>
Link: https://patch.msgid.link/aoUJHQmxL0LFIMCw@v4bel (local)
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
net/sctp/inqueue.c | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
--- a/net/sctp/inqueue.c
+++ b/net/sctp/inqueue.c
@@ -63,8 +63,11 @@ void sctp_inq_free(struct sctp_inq *queu
*/
void sctp_inq_push(struct sctp_inq *q, struct sctp_chunk *chunk)
{
- /* Directly call the packet handling routine. */
- if (chunk->rcvr->dead) {
+ /* Directly call the packet handling routine. Drop the chunk if the
+ * receiver or the transport it was looked up on is gone.
+ */
+ if (chunk->rcvr->dead ||
+ (chunk->transport && chunk->transport->dead)) {
sctp_chunk_free(chunk);
return;
}