mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
sctp: hold transport reference across connect wait
sctp_sendmsg_to_asoc() selects a transport before waiting for a new
interleaved association to become established. sctp_wait_for_connect()
drops the socket lock while sleeping.
While the lock is dropped, backlog processing can handle a COOKIE ACK and
an ASCONF DEL-IP that removes the selected transport. sctp_assoc_rm_peer()
then marks the transport dead and drops its association-owned reference.
The transport can be freed through RCU before the send path resumes.
After the wait, the stale pointer is assigned to newly created DATA or
I-DATA chunks and later dereferenced by sctp_outq_select_transport().
Hold a reference to the selected transport across
sctp_wait_for_connect(). After the socket lock is reacquired, abort the
send if the transport was marked dead. Callers without a selected
transport pass NULL and retain their existing behavior.
Cc: stable@vger.kernel.org
Fixes: 668c9beb90 ("sctp: implement assign_number for sctp_stream_interleave")
Acked-by: Xin Long <lucien.xin@gmail.com>
Signed-off-by: Seung Min Shin <guncraft2000@naver.com>
Link: https://patch.msgid.link/20261011162750.1529631-1-guncraft2000@naver.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
parent
a7c6027ed8
commit
5bef8649bb
|
|
@ -75,7 +75,8 @@ 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_connect(struct sctp_association *,
|
||||
struct sctp_transport *, long *timeo_p);
|
||||
static int sctp_wait_for_accept(struct sock *sk, long timeo);
|
||||
static void sctp_wait_for_close(struct sock *sk, long timeo);
|
||||
static void sctp_destruct_sock(struct sock *sk);
|
||||
|
|
@ -1282,7 +1283,7 @@ static int __sctp_connect(struct sock *sk, struct sockaddr *kaddrs,
|
|||
*assoc_id = asoc->assoc_id;
|
||||
|
||||
timeo = sock_sndtimeo(sk, flags & O_NONBLOCK);
|
||||
return sctp_wait_for_connect(asoc, &timeo);
|
||||
return sctp_wait_for_connect(asoc, NULL, &timeo);
|
||||
|
||||
out_free:
|
||||
pr_debug("%s: took out_free path with asoc:%p kaddrs:%p err:%d\n",
|
||||
|
|
@ -1884,7 +1885,7 @@ static int sctp_sendmsg_to_asoc(struct sctp_association *asoc,
|
|||
|
||||
if (asoc->ep->intl_enable) {
|
||||
timeo = sock_sndtimeo(sk, 0);
|
||||
err = sctp_wait_for_connect(asoc, &timeo);
|
||||
err = sctp_wait_for_connect(asoc, transport, &timeo);
|
||||
if (err || sk->sk_shutdown & RCV_SHUTDOWN) {
|
||||
err = -ESRCH;
|
||||
goto err;
|
||||
|
|
@ -1927,7 +1928,7 @@ static int sctp_sendmsg_to_asoc(struct sctp_association *asoc,
|
|||
|
||||
if (unlikely(wait_connect)) {
|
||||
timeo = sock_sndtimeo(sk, msg->msg_flags & MSG_DONTWAIT);
|
||||
sctp_wait_for_connect(asoc, &timeo);
|
||||
sctp_wait_for_connect(asoc, NULL, &timeo);
|
||||
}
|
||||
|
||||
err = msg_len;
|
||||
|
|
@ -9427,7 +9428,9 @@ static bool sctp_writeable(const struct sock *sk)
|
|||
/* Wait for an association to go into ESTABLISHED state. If timeout is 0,
|
||||
* returns immediately with EINPROGRESS.
|
||||
*/
|
||||
static int sctp_wait_for_connect(struct sctp_association *asoc, long *timeo_p)
|
||||
static int sctp_wait_for_connect(struct sctp_association *asoc,
|
||||
struct sctp_transport *transport,
|
||||
long *timeo_p)
|
||||
{
|
||||
struct sock *sk = asoc->base.sk;
|
||||
int err = 0;
|
||||
|
|
@ -9436,12 +9439,16 @@ static int sctp_wait_for_connect(struct sctp_association *asoc, long *timeo_p)
|
|||
|
||||
pr_debug("%s: asoc:%p, timeo:%ld\n", __func__, asoc, *timeo_p);
|
||||
|
||||
/* Increment the association's refcnt. */
|
||||
/* Hold references while the socket lock is dropped. */
|
||||
if (transport)
|
||||
sctp_transport_hold(transport);
|
||||
sctp_association_hold(asoc);
|
||||
|
||||
for (;;) {
|
||||
prepare_to_wait_exclusive(&asoc->wait, &wait,
|
||||
TASK_INTERRUPTIBLE);
|
||||
if (transport && transport->dead)
|
||||
goto do_transport_dead;
|
||||
if (!*timeo_p)
|
||||
goto do_nonblock;
|
||||
if (sk->sk_shutdown & RCV_SHUTDOWN)
|
||||
|
|
@ -9470,7 +9477,8 @@ static int sctp_wait_for_connect(struct sctp_association *asoc, long *timeo_p)
|
|||
out:
|
||||
finish_wait(&asoc->wait, &wait);
|
||||
|
||||
/* Release the association's refcnt. */
|
||||
if (transport)
|
||||
sctp_transport_put(transport);
|
||||
sctp_association_put(asoc);
|
||||
|
||||
return err;
|
||||
|
|
@ -9489,6 +9497,10 @@ static int sctp_wait_for_connect(struct sctp_association *asoc, long *timeo_p)
|
|||
do_nonblock:
|
||||
err = -EINPROGRESS;
|
||||
goto out;
|
||||
|
||||
do_transport_dead:
|
||||
err = -EAGAIN;
|
||||
goto out;
|
||||
}
|
||||
|
||||
static int sctp_wait_for_accept(struct sock *sk, long timeo)
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user