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
>
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.