From: Yuqi Xu 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: stable@kernel.org Reported-by: Yuan Tan Reported-by: Yifan Wu Reported-by: Juefei Pu Reported-by: Zhengchuan Liang Reported-by: Xin Liu Assisted-by: Codex:GPT-5.4 Signed-off-by: Yuqi Xu Signed-off-by: Ren Wei --- 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); -- 2.54.0