From acbddc71e987931802d6555e11cb1e8a802b786f Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 17 Apr 2025 13:54:37 +0200 Subject: [PATCH] 5.4-stable patches added patches: sctp-detect-and-prevent-references-to-a-freed-transport-in-sendmsg.patch --- ...nces-to-a-freed-transport-in-sendmsg.patch | 159 ++++++++++++++++++ queue-5.4/series | 1 + 2 files changed, 160 insertions(+) create mode 100644 queue-5.4/sctp-detect-and-prevent-references-to-a-freed-transport-in-sendmsg.patch diff --git a/queue-5.4/sctp-detect-and-prevent-references-to-a-freed-transport-in-sendmsg.patch b/queue-5.4/sctp-detect-and-prevent-references-to-a-freed-transport-in-sendmsg.patch new file mode 100644 index 0000000000..49be037b0a --- /dev/null +++ b/queue-5.4/sctp-detect-and-prevent-references-to-a-freed-transport-in-sendmsg.patch @@ -0,0 +1,159 @@ +From f1a69a940de58b16e8249dff26f74c8cc59b32be Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ricardo=20Ca=C3=B1uelo=20Navarro?= +Date: Fri, 4 Apr 2025 16:53:21 +0200 +Subject: sctp: detect and prevent references to a freed transport in sendmsg +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Ricardo Cañuelo Navarro + +commit f1a69a940de58b16e8249dff26f74c8cc59b32be upstream. + +sctp_sendmsg() re-uses associations and transports when possible by +doing a lookup based on the socket endpoint and the message destination +address, and then sctp_sendmsg_to_asoc() sets the selected transport in +all the message chunks to be sent. + +There's a possible race condition if another thread triggers the removal +of that selected transport, for instance, by explicitly unbinding an +address with setsockopt(SCTP_SOCKOPT_BINDX_REM), after the chunks have +been set up and before the message is sent. This can happen if the send +buffer is full, during the period when the sender thread temporarily +releases the socket lock in sctp_wait_for_sndbuf(). + +This causes the access to the transport data in +sctp_outq_select_transport(), when the association outqueue is flushed, +to result in a use-after-free read. + +This change avoids this scenario by having sctp_transport_free() signal +the freeing of the transport, tagging it as "dead". In order to do this, +the patch restores the "dead" bit in struct sctp_transport, which was +removed in +commit 47faa1e4c50e ("sctp: remove the dead field of sctp_transport"). + +Then, in the scenario where the sender thread has released the socket +lock in sctp_wait_for_sndbuf(), the bit is checked again after +re-acquiring the socket lock to detect the deletion. This is done while +holding a reference to the transport to prevent it from being freed in +the process. + +If the transport was deleted while the socket lock was relinquished, +sctp_sendmsg_to_asoc() will return -EAGAIN to let userspace retry the +send. + +The bug was found by a private syzbot instance (see the error report [1] +and the C reproducer that triggers it [2]). + +Link: https://people.igalia.com/rcn/kernel_logs/20250402__KASAN_slab-use-after-free_Read_in_sctp_outq_select_transport.txt [1] +Link: https://people.igalia.com/rcn/kernel_logs/20250402__KASAN_slab-use-after-free_Read_in_sctp_outq_select_transport__repro.c [2] +Cc: stable@vger.kernel.org +Fixes: df132eff4638 ("sctp: clear the transport of some out_chunk_list chunks in sctp_assoc_rm_peer") +Suggested-by: Xin Long +Signed-off-by: Ricardo Cañuelo Navarro +Acked-by: Xin Long +Link: https://patch.msgid.link/20250404-kasan_slab-use-after-free_read_in_sctp_outq_select_transport__20250404-v1-1-5ce4a0b78ef2@igalia.com +Signed-off-by: Paolo Abeni +Signed-off-by: Greg Kroah-Hartman +--- + include/net/sctp/structs.h | 3 ++- + net/sctp/socket.c | 22 ++++++++++++++-------- + net/sctp/transport.c | 2 ++ + 3 files changed, 18 insertions(+), 9 deletions(-) + +--- a/include/net/sctp/structs.h ++++ b/include/net/sctp/structs.h +@@ -779,6 +779,7 @@ struct sctp_transport { + + /* Reference counting. */ + refcount_t refcnt; ++ __u32 dead:1, + /* RTO-Pending : A flag used to track if one of the DATA + * chunks sent to this address is currently being + * used to compute a RTT. If this flag is 0, +@@ -788,7 +789,7 @@ struct sctp_transport { + * calculation completes (i.e. the DATA chunk + * is SACK'd) clear this flag. + */ +- __u32 rto_pending:1, ++ rto_pending:1, + + /* + * hb_sent : a flag that signals that we have a pending +--- a/net/sctp/socket.c ++++ b/net/sctp/socket.c +@@ -70,8 +70,9 @@ + /* Forward declarations for internal helper functions. */ + static bool sctp_writeable(const struct sock *sk); + static void sctp_wfree(struct sk_buff *skb); +-static int sctp_wait_for_sndbuf(struct sctp_association *asoc, long *timeo_p, +- size_t msg_len); ++static int sctp_wait_for_sndbuf(struct sctp_association *asoc, ++ struct sctp_transport *transport, ++ long *timeo_p, size_t msg_len); + static int sctp_wait_for_packet(struct sock *sk, int *err, long *timeo_p); + static int sctp_wait_for_connect(struct sctp_association *, long *timeo_p); + static int sctp_wait_for_accept(struct sock *sk, long timeo); +@@ -1847,7 +1848,7 @@ static int sctp_sendmsg_to_asoc(struct s + + if (sctp_wspace(asoc) <= 0 || !sk_wmem_schedule(sk, msg_len)) { + timeo = sock_sndtimeo(sk, msg->msg_flags & MSG_DONTWAIT); +- err = sctp_wait_for_sndbuf(asoc, &timeo, msg_len); ++ err = sctp_wait_for_sndbuf(asoc, transport, &timeo, msg_len); + if (err) + goto err; + if (unlikely(sinfo->sinfo_stream >= asoc->stream.outcnt)) { +@@ -9060,8 +9061,9 @@ void sctp_sock_rfree(struct sk_buff *skb + + + /* Helper function to wait for space in the sndbuf. */ +-static int sctp_wait_for_sndbuf(struct sctp_association *asoc, long *timeo_p, +- size_t msg_len) ++static int sctp_wait_for_sndbuf(struct sctp_association *asoc, ++ struct sctp_transport *transport, ++ long *timeo_p, size_t msg_len) + { + struct sock *sk = asoc->base.sk; + long current_timeo = *timeo_p; +@@ -9071,7 +9073,9 @@ static int sctp_wait_for_sndbuf(struct s + pr_debug("%s: asoc:%p, timeo:%ld, msg_len:%zu\n", __func__, asoc, + *timeo_p, msg_len); + +- /* Increment the association's refcnt. */ ++ /* Increment the transport and association's refcnt. */ ++ if (transport) ++ sctp_transport_hold(transport); + sctp_association_hold(asoc); + + /* Wait on the association specific sndbuf space. */ +@@ -9080,7 +9084,7 @@ static int sctp_wait_for_sndbuf(struct s + TASK_INTERRUPTIBLE); + if (asoc->base.dead) + goto do_dead; +- if (!*timeo_p) ++ if ((!*timeo_p) || (transport && transport->dead)) + goto do_nonblock; + if (sk->sk_err || asoc->state >= SCTP_STATE_SHUTDOWN_PENDING) + goto do_error; +@@ -9107,7 +9111,9 @@ static int sctp_wait_for_sndbuf(struct s + out: + finish_wait(&asoc->wait, &wait); + +- /* Release the association's refcnt. */ ++ /* Release the transport and association's refcnt. */ ++ if (transport) ++ sctp_transport_put(transport); + sctp_association_put(asoc); + + return err; +--- a/net/sctp/transport.c ++++ b/net/sctp/transport.c +@@ -116,6 +116,8 @@ fail: + */ + void sctp_transport_free(struct sctp_transport *transport) + { ++ transport->dead = 1; ++ + /* Try to delete the heartbeat timer. */ + if (del_timer(&transport->hb_timer)) + sctp_transport_put(transport); diff --git a/queue-5.4/series b/queue-5.4/series index 5c78a3351d..0e1e77d8a3 100644 --- a/queue-5.4/series +++ b/queue-5.4/series @@ -65,3 +65,4 @@ mtd-rawnand-add-status-chack-in-r852_ready.patch arm64-dts-mediatek-mt8173-fix-disp-pwm-compatible-string.patch sparc-mm-disable-preemption-in-lazy-mmu-mode.patch mm-add-missing-release-barrier-on-pgdat_reclaim_locked-unlock.patch +sctp-detect-and-prevent-references-to-a-freed-transport-in-sendmsg.patch -- 2.47.3