Re: [PATCH net 1/1] sctp: stream: rebind out_curr when updating streams

Xin Long <[email protected]> Wed, 27 May 2026 17:53:12 -0400
Newsgroups org.kernel.vger.linux-sctp,org.kernel.vger.netdev
Message-ID <CADvbK_eOSCO6760yYbSVyn7eCo5a78jMjMomocbR+z4Nqcd=6g@mail.gmail.com>
On Wed, May 27, 2026 at 1:28 AM Ren Wei <[email protected]> wrote:
>
> From: Yuqi Xu <[email protected]>
>
> sctp_stream_update() replaces the outbound stream table while
> out_curr may still point to the current stream selected for an
> unfinished fragmented message.
>
> Remember the current stream id before freeing the old table and
> rebind out_curr after the new table is installed. If that stream
> no longer exists, clear the cached pointer instead.
>
> This keeps the scheduler cursor valid across stream table
> replacement without losing the current stream when it still
> survives the update.
>
> Fixes: 5bbbbe32a431 ("sctp: introduce stream scheduler foundations")
> Cc: [email protected]
> Reported-by: Yuan Tan <[email protected]>
> Reported-by: Yifan Wu <[email protected]>
> Reported-by: Juefei Pu <[email protected]>
> Reported-by: Zhengchuan Liang <[email protected]>
> Reported-by: Xin Liu <[email protected]>
> Assisted-by: Codex:GPT-5.4
> Signed-off-by: Yuqi Xu <[email protected]>
> Signed-off-by: Ren Wei <[email protected]>
> ---
>  net/sctp/stream.c | 11 +++++++++++
>  1 file changed, 11 insertions(+)
>
> diff --git a/net/sctp/stream.c b/net/sctp/stream.c
> index c2247793c88b..fcb6c688f61d 100644
> --- a/net/sctp/stream.c
> +++ b/net/sctp/stream.c
> @@ -208,6 +208,16 @@ void sctp_stream_clear(struct sctp_stream *stream)
>  void sctp_stream_update(struct sctp_stream *stream, struct sctp_stream *new)
>  {
>         const struct sctp_sched_ops *sched = sctp_sched_ops_from_stream(stream);
> +       __u16 sid = SCTP_MAX_STREAM;
> +
> +       /* Preserve the current stream if its sid survives the table swap. */
> +       if (stream->out_curr) {
> +               for (sid = 0; sid < stream->outcnt; sid++)
> +                       if (SCTP_SO(stream, sid) == stream->out_curr)
> +                               break;
> +               if (sid == stream->outcnt)
> +                       sid = SCTP_MAX_STREAM;
> +       }
>
>         sched->unsched_all(stream);
>         sctp_stream_outq_migrate(stream, new, new->outcnt);
> @@ -217,6 +227,7 @@ void sctp_stream_update(struct sctp_stream *stream, struct sctp_stream *new)
>         stream->in  = new->in;
>         stream->outcnt = new->outcnt;
>         stream->incnt  = new->incnt;
> +       stream->out_curr = sid < stream->outcnt ? SCTP_SO(stream, sid) : NULL;
>
>         sched->sched_all(stream);
>
Thanks for the report and patch.

sctp_stream_update() is only called in COOKIE_WAIT state, where
stream->out_curr is expected to be NULL, since no user data should have
been sent at or before this state unless the state transitioned backward.

However, there is a corner case in sctp_sf_do_5_2_6_stale(): when a Stale
Cookie ERROR is received, the association may move from COOKIE_ECHOED back
to COOKIE_WAIT. If data was already bundled with COOKIE-ECHO to send out,
this issue can be triggered.

Updating stream->out_curr here is not a proper fix here, as any transmitted
data would also remain in the retransmit queue.

Since this restarts the handshake, negotiated parameters may change, so
previously queued/sent data should be dropped.

So you can fix it by purging the outqueue in sctp_sf_do_5_2_6_stale() like:

diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c
index 8e89a870780c..9b23c11cbb9e 100644
--- a/net/sctp/sm_statefuns.c
+++ b/net/sctp/sm_statefuns.c
@@ -2598,11 +2598,7 @@ static enum sctp_disposition sctp_sf_do_5_2_6_stale(
         */
        sctp_add_cmd_sf(commands, SCTP_CMD_DEL_NON_PRIMARY, SCTP_NULL());

-       /* If we've sent any data bundled with COOKIE-ECHO we will need to
-        * resend
-        */
-       sctp_add_cmd_sf(commands, SCTP_CMD_T1_RETRAN,
-                       SCTP_TRANSPORT(asoc->peer.primary_path));
+       sctp_add_cmd_sf(commands, SCTP_CMD_PURGE_OUTQUEUE, SCTP_NULL());

        /* Cast away the const modifier, as we want to just
         * rerun it through as a sideffect.

The userspace would rely on SCTP_SEND_FAILED_EVENT to get notified.

Thanks.