]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
5.4-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 17 Apr 2025 11:54:37 +0000 (13:54 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 17 Apr 2025 11:54:37 +0000 (13:54 +0200)
added patches:
sctp-detect-and-prevent-references-to-a-freed-transport-in-sendmsg.patch

queue-5.4/sctp-detect-and-prevent-references-to-a-freed-transport-in-sendmsg.patch [new file with mode: 0644]
queue-5.4/series

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 (file)
index 0000000..49be037
--- /dev/null
@@ -0,0 +1,159 @@
+From f1a69a940de58b16e8249dff26f74c8cc59b32be Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Ricardo=20Ca=C3=B1uelo=20Navarro?= <rcn@igalia.com>
+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 <rcn@igalia.com>
+
+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 <lucien.xin@gmail.com>
+Signed-off-by: Ricardo Cañuelo Navarro <rcn@igalia.com>
+Acked-by: Xin Long <lucien.xin@gmail.com>
+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 <pabeni@redhat.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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);
index 5c78a3351d0b92b78fc970a58e76e10cda4fa28b..0e1e77d8a3a43fb19181f30ef2ee3c7fe2ea32f5 100644 (file)
@@ -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