From: Tim Budding <tim.budding@gmail.com>
To: netdev@vger.kernel.org, linux-sctp@vger.kernel.org
Cc: Xin Long <lucien.xin@gmail.com>,
Marcelo Ricardo Leitner <marcelo.leitner@gmail.com>,
"David S . Miller" <davem@davemloft.net>,
Eric Dumazet <edumazet@kernel.org>,
Jakub Kicinski <kuba@kernel.org>, Paolo Abeni <pabeni@redhat.com>,
Simon Horman <horms@kernel.org>, Shuah Khan <shuah@kernel.org>,
linux-kselftest@vger.kernel.org, linux-kernel@vger.kernel.org,
Tim Budding <tim.budding@gmail.com>,
stable@vger.kernel.org
Subject: [PATCH net 1/4] sctp: purge abandoned I-DATA fragments by stream and MID, not by TSN
Date: Sat, 24 Oct 2026 05:06:24 -0700 [thread overview]
Message-ID: <20261024120627.837568-2-tim.budding@gmail.com> (raw)
In-Reply-To: <20261024120627.837568-1-tim.budding@gmail.com>
When an I-FORWARD-TSN is received, sctp_report_iftsn() calls
sctp_intl_reasm_flushtsn(), which frees every fragment in reasm and
reasm_uo with a TSN at or below the New Cumulative TSN. That is only
correct for DATA chunks, where the fragments of a message use
consecutive TSNs. With I-DATA, fragments of messages on different
streams can be interleaved, and RFC 8260 section 2.2.3 says the receiver
"MUST NOT make any assumption about the TSN assignments of the sender".
So when a PR-SCTP message is abandoned while a large message on another
stream is still being received, the fragments of the large message below
the abandoned TSN are freed as well. They have already been acked and
are not retransmitted, so the large message is never delivered, its
remaining fragments stay queued until a later I-FORWARD-TSN covers them
or the association goes away, and on an ordered stream every later
message is blocked behind it. Also, received fragments of the abandoned
message above the New Cumulative TSN are only freed if a later
I-FORWARD-TSN happens to cover them.
The skip entries of the I-FORWARD-TSN already identify the abandoned
messages by stream, U bit and MID. Free the fragments from
sctp_intl_skip() based on those instead. An entry carries the largest
skipped MID for its stream and U bit (RFC 8260 section 2.3.1), and a
sender fragments only one message per stream at a time (section 2.2.2),
so all fragments on that stream with a MID at or below it can be freed.
The reassembly queues are sorted by stream and MID, so the walk stops
once it is past them. Fragments that no skip entry names, such as a late
copy of an abandoned fragment, are no longer freed by an unrelated
I-FORWARD-TSN; they stay until the association is freed. The association
reset paths that also call sctp_report_iftsn() are not affected: they
pass the highest TSN seen, so sctp_intl_abort_pd() still flushes the
whole ulpq for them.
Observed in real use as unexpected message loss when sending messages of
mixed sizes over one association; the cause was found by AI-assisted
code inspection. Reproduced on an unmodified kernel with the ordered and
unordered tests of the sctp_ifwdtsn.sh selftest added later in this
series, which pass with this fix.
Fixes: 47b20a88566f ("sctp: implement report_ftsn for sctp_stream_interleave")
Cc: stable@vger.kernel.org
Signed-off-by: Tim Budding <tim.budding@gmail.com>
---
net/sctp/stream_interleave.c | 29 +++++++++++------------------
1 file changed, 11 insertions(+), 18 deletions(-)
diff --git a/net/sctp/stream_interleave.c b/net/sctp/stream_interleave.c
index 840f24045ae2..f8c818a5533c 100644
--- a/net/sctp/stream_interleave.c
+++ b/net/sctp/stream_interleave.c
@@ -1203,28 +1203,21 @@ static void sctp_report_fwdtsn(struct sctp_ulpq *ulpq, __u32 ftsn)
sctp_ulpq_abort_pd(ulpq, GFP_ATOMIC);
}
-static void sctp_intl_reasm_flushtsn(struct sctp_ulpq *ulpq, __u32 ftsn)
+static void sctp_intl_reasm_flush_mid(struct sk_buff_head *reasm, __u16 sid,
+ __u32 mid)
{
struct sk_buff *pos, *tmp;
- skb_queue_walk_safe(&ulpq->reasm, pos, tmp) {
+ skb_queue_walk_safe(reasm, pos, tmp) {
struct sctp_ulpevent *event = sctp_skb2event(pos);
- __u32 tsn = event->tsn;
- if (TSN_lte(tsn, ftsn)) {
- __skb_unlink(pos, &ulpq->reasm);
- sctp_ulpevent_free(event);
- }
- }
-
- skb_queue_walk_safe(&ulpq->reasm_uo, pos, tmp) {
- struct sctp_ulpevent *event = sctp_skb2event(pos);
- __u32 tsn = event->tsn;
+ if (event->stream < sid)
+ continue;
+ if (event->stream > sid || MID_lt(mid, event->mid))
+ break;
- if (TSN_lte(tsn, ftsn)) {
- __skb_unlink(pos, &ulpq->reasm_uo);
- sctp_ulpevent_free(event);
- }
+ __skb_unlink(pos, reasm);
+ sctp_ulpevent_free(event);
}
}
@@ -1232,8 +1225,6 @@ static void sctp_report_iftsn(struct sctp_ulpq *ulpq, __u32 ftsn)
{
/* Move the Cumulattive TSN Ack ahead. */
sctp_tsnmap_skip(&ulpq->asoc->peer.tsn_map, ftsn);
- /* purge the fragmentation queue */
- sctp_intl_reasm_flushtsn(ulpq, ftsn);
/* abort only when it's for all data */
if (ftsn == sctp_tsnmap_get_max_tsn_seen(&ulpq->asoc->peer.tsn_map))
sctp_intl_abort_pd(ulpq, GFP_ATOMIC);
@@ -1260,6 +1251,7 @@ static void sctp_intl_skip(struct sctp_ulpq *ulpq, __u16 sid, __u32 mid,
sctp_intl_stream_abort_pd(ulpq, sid, mid, 0x1,
GFP_ATOMIC);
}
+ sctp_intl_reasm_flush_mid(&ulpq->reasm_uo, sid, mid);
return;
}
@@ -1271,6 +1263,7 @@ static void sctp_intl_skip(struct sctp_ulpq *ulpq, __u16 sid, __u32 mid,
sctp_intl_stream_abort_pd(ulpq, sid, mid, 0x0, GFP_ATOMIC);
}
+ sctp_intl_reasm_flush_mid(&ulpq->reasm, sid, mid);
sctp_mid_skip(stream, in, sid, mid);
sctp_intl_reap_ordered(ulpq, sid);
--
2.34.1
next prev parent reply other threads:[~2026-10-24 12:07 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-24 12:06 [PATCH net 0/4] sctp: fix I-FORWARD-TSN handling with user message interleaving Tim Budding
2026-10-24 12:06 ` Tim Budding [this message]
2026-10-24 12:06 ` [PATCH net 2/4] sctp: abort unordered partial delivery when its message is skipped Tim Budding
2026-10-24 12:06 ` [PATCH net 3/4] sctp: don't treat an I-FORWARD-TSN as an association reset Tim Budding
2026-10-24 12:06 ` [PATCH net 4/4] selftests: net: add SCTP I-FORWARD-TSN tests with message interleaving Tim Budding
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261024120627.837568-2-tim.budding@gmail.com \
--to=tim.budding@gmail.com \
--cc=davem@davemloft.net \
--cc=edumazet@kernel.org \
--cc=horms@kernel.org \
--cc=kuba@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-kselftest@vger.kernel.org \
--cc=linux-sctp@vger.kernel.org \
--cc=lucien.xin@gmail.com \
--cc=marcelo.leitner@gmail.com \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
--cc=shuah@kernel.org \
--cc=stable@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®