Re: [PATCH net] sctp: validate chunk length in the inqueue parser
From: Xin Long
Date: Thu Aug 27 2026 - 11:35:23 EST
On Wed, Aug 26, 2026 at 9:01 PM Charles Vosburgh via B4 Relay
<devnull+theminershive.gmail.com@xxxxxxxxxx> wrote:
>
> From: Charles Vosburgh <theminershive@xxxxxxxxx>
>
> SCTP chunks always include a four-byte generic header, but
> sctp_inq_pop() currently accepts shorter declared lengths. A zero-length
> chunk leaves chunk_end at the current header.
>
> When ASCONF is covered by the association's SCTP-AUTH policy,
> sctp_assoc_bh_rcv() can continue before the state machine performs its
> normal chunk-length check. sctp_inq_pop() then returns the same malformed
> chunk repeatedly and the receive softirq can lock up.
>
> A remote SCTP peer can trigger this after establishing an association on
> a kernel built with CONFIG_IP_SCTP and configured with
> net.sctp.addip_enable=1 and net.sctp.auth_enable=1. The reproducer did
> not require application credentials, a shared SCTP AUTH key, or
> net.sctp.addip_noauth_enable=1.
>
> On commit f967455fb2a5 ("seg6: reset IP6CB after IPv6 decapsulation"),
> one zero-length ASCONF caused repeated
> watchdog soft-lockup reports in a two-vCPU KVM guest. All 3 pre-trigger
> health probes succeeded, while 36 of 37 post-trigger probes failed. With
> this change, all 37 post-trigger probes succeeded and no equivalent
> soft-lockup signature appeared.
>
> Reject chunks shorter than the generic SCTP header at the shared inqueue
> parser boundary. Mark the packet for discard before either caller can
> continue processing it, while preserving the four-byte generic minimum.
> Declared-length 1 through 4 controls and kernel-generated ASCONF traffic
> remained healthy. The patched sctp_hello selftest passed for IPv4 and
> IPv6.
>
> The complete private reproducer and validation evidence are available
> directly to maintainers on request.
Please share the PoC with maintainers.
>
> Fixes: bbd0d59809f9 ("[SCTP]: Implement the receive and verification of AUTH chunk")
> Cc: stable@xxxxxxxxxxxxxxx
> Assisted-by: ChatGPT:GPT-5.6-Sol
> Assisted-by: Vantix:claude-opus-5
> Assisted-by: Codex:GPT-5
> Signed-off-by: Charles Vosburgh <theminershive@xxxxxxxxx>
> ---
> net/sctp/inqueue.c | 5 +++++
> 1 file changed, 5 insertions(+)
>
> diff --git a/net/sctp/inqueue.c b/net/sctp/inqueue.c
> index 5f988b3a8814..12a46868165a 100644
> --- a/net/sctp/inqueue.c
> +++ b/net/sctp/inqueue.c
> @@ -212,6 +212,11 @@ struct sctp_chunk *sctp_inq_pop(struct sctp_inq *queue)
> chunk->chunk_end = ((__u8 *)ch) + SCTP_PAD4(ntohs(ch->length));
> skb_pull(chunk->skb, sizeof(*ch));
> chunk->subh.v = NULL; /* Subheader is no longer valid. */
> + if (unlikely(ntohs(ch->length) < sizeof(*ch))) {
> + chunk->pdiscard = 1;
> + chunk->chunk_end = chunk->skb->data;
> + return chunk;
> + }
An early return of the chunk will skip the pr_debug(), which may not be
what we want. Also, I don't think we have to update chunk->chunk_end.
>
> if (chunk->chunk_end + sizeof(*ch) <= skb_tail_pointer(chunk->skb)) {
> /* This is not a singleton */
>
Maybe you can merge if (unlikely(ntohs(ch->length) < sizeof(*ch))) into the
check here, like:
if (unlikely(ntohs(ch->length) < sizeof(*ch))) {
chunk->pdiscard = 1;
} else if (chunk->chunk_end + sizeof(*ch) <=
skb_tail_pointer(chunk->skb)) {
...
Thanks.
> ---
> base-commit: f967455fb2a5a2079b9eb5823e9ccf359174bf9f
> change-id: 20260826-sctp-zero-chunk-inqueue-b478dca552c6
>
> Best regards,
> --
> Charles Vosburgh <theminershive@xxxxxxxxx>
>
>