* [PATCH net v2 1/4] sctp: abort unordered partial delivery when its message is skipped
[not found] <20261105050225.49292-1-tim.budding@gmail.com>
@ 2026-11-05 5:02 ` Tim Budding
2026-11-05 5:02 ` [PATCH net v2 2/4] sctp: report the partially delivered MID in PARTIAL_DELIVERY_ABORTED Tim Budding
2026-11-05 5:02 ` [PATCH net v2 3/4] sctp: fix I-FORWARD-TSN handling of fragments on other streams Tim Budding
2 siblings, 0 replies; 3+ messages in thread
From: Tim Budding @ 2026-11-05 5:02 UTC (permalink / raw)
To: netdev, linux-sctp
Cc: lucien.xin, marcelo.leitner, davem, edumazet, kuba, pabeni, horms,
shuah, linux-kselftest, linux-kernel, Tim Budding, stable
When an I-FORWARD-TSN skips unordered messages on a stream,
sctp_intl_skip() only aborts the stream's unordered partial delivery if
the MID being delivered is lower than the skipped MID. If the skipped
message is the one being partially delivered, partial delivery stays in
progress: no PARTIAL_DELIVERY_ABORTED is sent, and the next unordered
message on the stream is delivered as the rest of the abandoned one.
Abort the partial delivery when the MID being delivered is at or below
the skipped MID, as the ordered case already does.
Reproduced on an unmodified kernel with the uo_pd test of the
sctp_ifwdtsn.sh selftest added later in this series; its partial
delivery checks pass with this fix. Reproduction and fix done with AI
assistance.
Fixes: de60fe910543 ("sctp: implement handle_ftsn for sctp_stream_interleave")
Cc: stable@vger.kernel.org
Signed-off-by: Tim Budding <tim.budding@gmail.com>
---
net/sctp/stream_interleave.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/net/sctp/stream_interleave.c b/net/sctp/stream_interleave.c
index 840f24045ae2..869940001588 100644
--- a/net/sctp/stream_interleave.c
+++ b/net/sctp/stream_interleave.c
@@ -1255,7 +1255,7 @@ static void sctp_intl_skip(struct sctp_ulpq *ulpq, __u16 sid, __u32 mid,
struct sctp_stream *stream = &ulpq->asoc->stream;
if (flags & SCTP_FTSN_U_BIT) {
- if (sin->pd_mode_uo && MID_lt(sin->mid_uo, mid)) {
+ if (sin->pd_mode_uo && !MID_lt(mid, sin->mid_uo)) {
sin->pd_mode_uo = 0;
sctp_intl_stream_abort_pd(ulpq, sid, mid, 0x1,
GFP_ATOMIC);
--
2.34.1
^ permalink raw reply related [flat|nested] 3+ messages in thread* [PATCH net v2 2/4] sctp: report the partially delivered MID in PARTIAL_DELIVERY_ABORTED
[not found] <20261105050225.49292-1-tim.budding@gmail.com>
2026-11-05 5:02 ` [PATCH net v2 1/4] sctp: abort unordered partial delivery when its message is skipped Tim Budding
@ 2026-11-05 5:02 ` Tim Budding
2026-11-05 5:02 ` [PATCH net v2 3/4] sctp: fix I-FORWARD-TSN handling of fragments on other streams Tim Budding
2 siblings, 0 replies; 3+ messages in thread
From: Tim Budding @ 2026-11-05 5:02 UTC (permalink / raw)
To: netdev, linux-sctp
Cc: lucien.xin, marcelo.leitner, davem, edumazet, kuba, pabeni, horms,
shuah, linux-kselftest, linux-kernel, Tim Budding, stable
When an I-FORWARD-TSN skips the message being partially delivered on a
stream, sctp_intl_skip() passes the MID from the I-FORWARD-TSN to the
PARTIAL_DELIVERY_ABORTED notification as pdapi_seq. That MID is the
highest one skipped on the stream, so when more than one message is
skipped, the notification names a later message instead of the one
whose partial delivery was aborted.
Report the MID being partially delivered, sin->mid_uo or sin->mid,
instead.
Reproduced on an unmodified kernel with the pdapi and pdapi_uo tests of
the sctp_ifwdtsn.sh selftest added later in this series, which pass
with this fix. Reproduction and fix done with AI assistance.
Fixes: de60fe910543 ("sctp: implement handle_ftsn for sctp_stream_interleave")
Reported-by: Sashiko <sashiko-bot@kernel.org>
Closes: https://lore.kernel.org/all/179293012867.3134.16497481637722705271@kernel.org/
Cc: stable@vger.kernel.org
Signed-off-by: Tim Budding <tim.budding@gmail.com>
---
net/sctp/stream_interleave.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/net/sctp/stream_interleave.c b/net/sctp/stream_interleave.c
index 869940001588..e239da686f68 100644
--- a/net/sctp/stream_interleave.c
+++ b/net/sctp/stream_interleave.c
@@ -1257,7 +1257,7 @@ static void sctp_intl_skip(struct sctp_ulpq *ulpq, __u16 sid, __u32 mid,
if (flags & SCTP_FTSN_U_BIT) {
if (sin->pd_mode_uo && !MID_lt(mid, sin->mid_uo)) {
sin->pd_mode_uo = 0;
- sctp_intl_stream_abort_pd(ulpq, sid, mid, 0x1,
+ sctp_intl_stream_abort_pd(ulpq, sid, sin->mid_uo, 0x1,
GFP_ATOMIC);
}
return;
@@ -1268,7 +1268,7 @@ static void sctp_intl_skip(struct sctp_ulpq *ulpq, __u16 sid, __u32 mid,
if (sin->pd_mode) {
sin->pd_mode = 0;
- sctp_intl_stream_abort_pd(ulpq, sid, mid, 0x0, GFP_ATOMIC);
+ sctp_intl_stream_abort_pd(ulpq, sid, sin->mid, 0x0, GFP_ATOMIC);
}
sctp_mid_skip(stream, in, sid, mid);
--
2.34.1
^ permalink raw reply related [flat|nested] 3+ messages in thread* [PATCH net v2 3/4] sctp: fix I-FORWARD-TSN handling of fragments on other streams
[not found] <20261105050225.49292-1-tim.budding@gmail.com>
2026-11-05 5:02 ` [PATCH net v2 1/4] sctp: abort unordered partial delivery when its message is skipped Tim Budding
2026-11-05 5:02 ` [PATCH net v2 2/4] sctp: report the partially delivered MID in PARTIAL_DELIVERY_ABORTED Tim Budding
@ 2026-11-05 5:02 ` Tim Budding
2 siblings, 0 replies; 3+ messages in thread
From: Tim Budding @ 2026-11-05 5:02 UTC (permalink / raw)
To: netdev, linux-sctp
Cc: lucien.xin, marcelo.leitner, davem, edumazet, kuba, pabeni, horms,
shuah, linux-kselftest, linux-kernel, Tim Budding, stable
sctp_report_iftsn() incorrectly frees queued I-DATA fragments in two
cases. First, it drops any queued fragments with TSN less than or equal
to the New Cumulative TSN. This prevents reconstruction of messages
still being received on other streams and can fully block an ordered
stream. Second, it calls sctp_intl_abort_pd() if the New Cumulative TSN
equals the highest TSN seen. This check exists for association resets
but can be inadvertently triggered by any I-FORWARD-TSN that arrives
before data with a higher TSN, causing data loss and, if a partial
delivery is in progress on an ordered stream, an aborted association.
Fix the first by freeing fragments from sctp_intl_skip() based on
stream, U bit, and MID instead of TSN. Fix the second by moving the call
to sctp_intl_abort_pd() from sctp_report_iftsn() to the corresponding
reset handlers.
Observed in real use as unexpected message loss. Reproduced on an
unmodified kernel with the sctp_ifwdtsn.sh selftest added later in this
series, which passes with this fix. Reproduction and fix done with AI
assistance.
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.c | 2 ++
net/sctp/stream_interleave.c | 32 +++++++++++---------------------
2 files changed, 13 insertions(+), 21 deletions(-)
diff --git a/net/sctp/stream.c b/net/sctp/stream.c
index e4354d58f20b..d5e71a2027f5 100644
--- a/net/sctp/stream.c
+++ b/net/sctp/stream.c
@@ -749,6 +749,7 @@ struct sctp_chunk *sctp_process_strreset_tsnreq(
*/
max_tsn_seen = sctp_tsnmap_get_max_tsn_seen(&asoc->peer.tsn_map);
asoc->stream.si->report_ftsn(&asoc->ulpq, max_tsn_seen);
+ asoc->stream.si->abort_pd(&asoc->ulpq, GFP_ATOMIC);
/* G1: Compute an appropriate value for the Receiver's Next TSN -- the
* TSN that the peer should use to send the next DATA chunk. The
@@ -1028,6 +1029,7 @@ struct sctp_chunk *sctp_process_strreset_resp(
LIST_HEAD(temp);
asoc->stream.si->report_ftsn(&asoc->ulpq, mtsn);
+ asoc->stream.si->abort_pd(&asoc->ulpq, GFP_ATOMIC);
sctp_tsnmap_init(&asoc->peer.tsn_map,
SCTP_TSN_MAP_INITIAL,
diff --git a/net/sctp/stream_interleave.c b/net/sctp/stream_interleave.c
index e239da686f68..1113b3455369 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,11 +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);
}
static void sctp_handle_fwdtsn(struct sctp_ulpq *ulpq, struct sctp_chunk *chunk)
@@ -1260,6 +1248,7 @@ static void sctp_intl_skip(struct sctp_ulpq *ulpq, __u16 sid, __u32 mid,
sctp_intl_stream_abort_pd(ulpq, sid, sin->mid_uo, 0x1,
GFP_ATOMIC);
}
+ sctp_intl_reasm_flush_mid(&ulpq->reasm_uo, sid, mid);
return;
}
@@ -1271,6 +1260,7 @@ static void sctp_intl_skip(struct sctp_ulpq *ulpq, __u16 sid, __u32 mid,
sctp_intl_stream_abort_pd(ulpq, sid, sin->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
^ permalink raw reply related [flat|nested] 3+ messages in thread