Re: [PATCH net v3 2/2] sctp: fix stream->outcnt underflow on duplicate RECONF responses
Xin Long <[email protected]>
| Newsgroups | org.kernel.vger.netdev,org.kernel.vger.linux-sctp |
|---|---|
| Message-ID | <CADvbK_dABUb_=VnO60AO=pj=jvjCG5dCBRj3wDAt7kM4=71_zQ@mail.gmail.com> |
On Fri, Aug 21, 2026 at 5:14 AM Jun Yang <[email protected]> wrote: > > A cached RECONF chunk may contain more than one request parameter. A > duplicate response can therefore find and process the same ADD_OUT request > again while another parameter is still outstanding, rolling back outcnt > twice and possibly underflowing it. > > Track outstanding request types as bits and clear each bit after its first > response. Later responses for the same request are then ignored. > > Fixes: 11ae76e67a17 ("sctp: implement receiver-side procedures for the Reconf Response Parameter") > Cc: [email protected] > Reported-by: TencentOS Corvus AI <[email protected]> > Link: https://lore.kernel.org/netdev/[email protected]/ > Suggested-by: Xin Long <[email protected]> > Assisted-by: tencentos-corvus-ai:kimi-k3 > Signed-off-by: Jun Yang <[email protected]> > --- > v3: > - Split the response_seq == 0 lookup fix into patch 1 (Simon Horman). > - Keep the request bit helper local to stream.c. > > v2: > - Track outstanding requests by type instead of sequence (Xin Long). > - Drop the redundant arithmetic guard (Xin Long). > > v1: https://lore.kernel.org/netdev/[email protected]/ > > include/net/sctp/structs.h | 2 +- > net/sctp/stream.c | 39 +++++++++++++++++++++++++++----------- > 2 files changed, 29 insertions(+), 12 deletions(-) > > diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h > index cccc662..b21f23b 100644 > --- a/include/net/sctp/structs.h > +++ b/include/net/sctp/structs.h > @@ -2057,7 +2057,7 @@ struct sctp_association { > force_delay:1; > > __u8 strreset_enable; > - __u8 strreset_outstanding; /* request param count on the fly */ > + __u8 strreset_outstanding; /* request param bitmask on the fly */ > > __u32 strreset_outseq; /* Update after receiving response */ > __u32 strreset_inseq; /* Update after receiving request */ > diff --git a/net/sctp/stream.c b/net/sctp/stream.c > index cfca5aa..e1a215d 100644 > --- a/net/sctp/stream.c > +++ b/net/sctp/stream.c > @@ -22,6 +22,9 @@ > #include <net/sctp/sm.h> > #include <net/sctp/stream_sched.h> > > +#define SCTP_STRRESET_BIT(type) \ > + BIT(ntohs(type) - ntohs(SCTP_PARAM_RESET_OUT_REQUEST)) > + Hi, Jun Yang, Any reason for removing SCTP_STRRESET_RET/CLEAR/TEST()? Each of these macros is used at least twice below, so removing them increases the number of lines of code. Note: The pre-existing issue reported in sashiko [1] is false positive, as a peer can't answers an IN_REQUEST or ADD_IN_STREAMS with SCTP_STRRESET_PERFORMED according to rfc6525. [1] https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260821091440.6496-1-junvyyang%40tencent.com Thanks. > static void sctp_stream_shrink_out(struct sctp_stream *stream, __u16 outcnt) > { > struct sctp_association *asoc; > @@ -372,7 +375,9 @@ int sctp_send_reset_streams(struct sctp_association *asoc, > goto out; > } > > - asoc->strreset_outstanding = out + in; > + asoc->strreset_outstanding = > + (out ? SCTP_STRRESET_BIT(SCTP_PARAM_RESET_OUT_REQUEST) : 0) | > + (in ? SCTP_STRRESET_BIT(SCTP_PARAM_RESET_IN_REQUEST) : 0); > > out: > return retval; > @@ -417,7 +422,8 @@ int sctp_send_reset_assoc(struct sctp_association *asoc) > return retval; > } > > - asoc->strreset_outstanding = 1; > + asoc->strreset_outstanding = > + SCTP_STRRESET_BIT(SCTP_PARAM_RESET_TSN_REQUEST); > > return 0; > } > @@ -474,7 +480,9 @@ int sctp_send_add_streams(struct sctp_association *asoc, > goto out; > } > > - asoc->strreset_outstanding = !!out + !!in; > + asoc->strreset_outstanding = > + (out ? SCTP_STRRESET_BIT(SCTP_PARAM_RESET_ADD_OUT_STREAMS) : 0) | > + (in ? SCTP_STRRESET_BIT(SCTP_PARAM_RESET_ADD_IN_STREAMS) : 0); > > out: > return retval; > @@ -564,13 +572,16 @@ struct sctp_chunk *sctp_process_strreset_outreq( > if (asoc->strreset_chunk) { > if (!sctp_chunk_lookup_strreset_param( > asoc, outreq->response_seq, > - SCTP_PARAM_RESET_IN_REQUEST, true)) { > + SCTP_PARAM_RESET_IN_REQUEST, true) || > + !(asoc->strreset_outstanding & > + SCTP_STRRESET_BIT(SCTP_PARAM_RESET_IN_REQUEST))) { > /* same process with outstanding isn't 0 */ > result = SCTP_STRRESET_ERR_IN_PROGRESS; > goto out; > } > > - asoc->strreset_outstanding--; > + asoc->strreset_outstanding &= > + ~SCTP_STRRESET_BIT(SCTP_PARAM_RESET_IN_REQUEST); > asoc->strreset_outseq++; > > if (!asoc->strreset_outstanding) { > @@ -669,7 +680,8 @@ struct sctp_chunk *sctp_process_strreset_inreq( > SCTP_SO(stream, i)->state = SCTP_STREAM_CLOSED; > > asoc->strreset_chunk = chunk; > - asoc->strreset_outstanding = 1; > + asoc->strreset_outstanding = > + SCTP_STRRESET_BIT(SCTP_PARAM_RESET_OUT_REQUEST); > sctp_chunk_hold(asoc->strreset_chunk); > > result = SCTP_STRRESET_PERFORMED; > @@ -816,13 +828,16 @@ struct sctp_chunk *sctp_process_strreset_addstrm_out( > > if (asoc->strreset_chunk) { > if (!sctp_chunk_lookup_strreset_param( > - asoc, 0, SCTP_PARAM_RESET_ADD_IN_STREAMS, false)) { > + asoc, 0, SCTP_PARAM_RESET_ADD_IN_STREAMS, false) || > + !(asoc->strreset_outstanding & > + SCTP_STRRESET_BIT(SCTP_PARAM_RESET_ADD_IN_STREAMS))) { > /* same process with outstanding isn't 0 */ > result = SCTP_STRRESET_ERR_IN_PROGRESS; > goto out; > } > > - asoc->strreset_outstanding--; > + asoc->strreset_outstanding &= > + ~SCTP_STRRESET_BIT(SCTP_PARAM_RESET_ADD_IN_STREAMS); > asoc->strreset_outseq++; > > if (!asoc->strreset_outstanding) { > @@ -899,7 +914,8 @@ struct sctp_chunk *sctp_process_strreset_addstrm_in( > goto out; > > asoc->strreset_chunk = chunk; > - asoc->strreset_outstanding = 1; > + asoc->strreset_outstanding = > + SCTP_STRRESET_BIT(SCTP_PARAM_RESET_ADD_OUT_STREAMS); > sctp_chunk_hold(asoc->strreset_chunk); > > stream->outcnt = outcnt; > @@ -929,7 +945,8 @@ struct sctp_chunk *sctp_process_strreset_resp( > > req = sctp_chunk_lookup_strreset_param(asoc, resp->response_seq, 0, > true); > - if (!req) > + if (!req || !(asoc->strreset_outstanding & > + SCTP_STRRESET_BIT(req->type))) > return NULL; > > result = ntohl(resp->result); > @@ -1079,7 +1096,7 @@ struct sctp_chunk *sctp_process_strreset_resp( > nums, 0, GFP_ATOMIC); > } > > - asoc->strreset_outstanding--; > + asoc->strreset_outstanding &= ~SCTP_STRRESET_BIT(req->type); > asoc->strreset_outseq++; > > /* remove everything for this reconf request */ > -- > 2.55.0 >