Thread (6 messages) 6 messages, 2 authors, 13d ago

Re: [PATCH net v2 2/2] sctp: add INIT verification after cookie unpacking

From: Xin Long <lucien.xin@gmail.com>
Date: 2026-06-23 14:45:34
Also in: linux-sctp
Subsystem: networking [general], sctp protocol, the rest · Maintainers: "David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, Marcelo Ricardo Leitner, Xin Long, Linus Torvalds

On Mon, Jun 22, 2026 at 12:52 PM Simon Horman [off-list ref] wrote:
This is an AI-generated review of your patch. The human sending this
email has considered the AI review valid, or at least plausible.
Full review at: https://netdev-ai.bots.linux.dev/sashiko/
---
sctp: add INIT verification after cookie unpacking

This commit adds sctp_verify_init() checks in the COOKIE-ECHO processing
paths sctp_sf_do_5_1D_ce() and sctp_sf_do_5_2_4_dupcook() when
cookie_auth_enable is disabled, and updates sctp_verify_init() to bound
the parameter walk against peer_init length rather than chunk->chunk_end.
On verification failure the new association is freed and an ABORT is
generated via sctp_abort_on_init_err().
quoted
diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c
index 8c636f045e45..6967e889d1bd 100644
--- a/net/sctp/sm_statefuns.c
+++ b/net/sctp/sm_statefuns.c
@@ -650,11 +650,12 @@ enum sctp_disposition sctp_sf_do_5_1D_ce(struct net *net,
                                       struct sctp_cmd_seq *commands)
 {
      struct sctp_ulpevent *ev, *ai_ev = NULL, *auth_ev = NULL;
+     struct sctp_chunk *err_chk_p = NULL;
      struct sctp_association *new_asoc;
      struct sctp_init_chunk *peer_init;
      struct sctp_chunk *chunk = arg;
-     struct sctp_chunk *err_chk_p;
      struct sctp_chunk *repl;
+     enum sctp_cid cid;
      struct sock *sk;
      int error = 0;
@@ -728,6 +729,18 @@ enum sctp_disposition sctp_sf_do_5_1D_ce(struct net *net,
              }
      }

+     peer_init = (struct sctp_init_chunk *)(chunk->subh.cookie_hdr + 1);
+     cid = peer_init->chunk_hdr.type;
+     if (!sctp_sk(sk)->cookie_auth_enable &&
+         !sctp_verify_init(net, ep, asoc, cid, peer_init, chunk,
+                           &err_chk_p)) {
+             sctp_association_free(new_asoc);
+             return sctp_abort_on_init_err(net, ep, asoc, cid, arg, commands,
+                                           err_chk_p);
+     }
[Critical]
When cookie_auth_enable is disabled the cookie HMAC is not validated by
sctp_unpack_cookie(), so every byte of the embedded INIT (including
peer_init->chunk_hdr.type) is attacker-controlled.  Is it safe to feed
that byte directly into both sctp_verify_init() and
sctp_abort_on_init_err() as cid?

Inside the COOKIE-ECHO body the embedded chunk is by RFC always an INIT.
Could the cid argument be hard-coded to SCTP_CID_INIT (or
peer_init->chunk_hdr.type be validated against SCTP_CID_INIT before use)
instead of trusting the wire byte?

Two attacker-reachable consequences look possible if cid is forged to
SCTP_CID_INIT_ACK (3) and the STATE_COOKIE parameter is omitted:

sctp_verify_init() then returns 0 via this branch with *errp populated:

        if ((SCTP_CID_INIT_ACK == cid) && !has_cookie)
                return sctp_process_missing_param(asoc, SCTP_PARAM_STATE_COOKIE,
                                                  chunk, errp);

control transfers to sctp_abort_on_init_err(net, ep, asoc, cid, ...) with
cid == SCTP_CID_INIT_ACK and a non-NULL err_chunk.  After sending the
ABORT packet, that helper falls through to its out: label because
cid == SCTP_CID_INIT_ACK:

        if (cid != SCTP_CID_INIT_ACK) {
                if (!packet)
                        return SCTP_DISPOSITION_NOMEM;
                return SCTP_DISPOSITION_CONSUME;
        }

out:
        ...
        return sctp_stop_t1_and_abort(net, commands, error, ECONNREFUSED,
                                      asoc, chunk->transport);

In the sctp_sf_do_5_1D_ce() context, asoc is typically NULL (the function
itself opens with if (asoc && !sctp_vtag_verify(chunk, asoc))).  Can the
SCTP_CMD_NEW_STATE / SCTP_CMD_TIMER_STOP / SCTP_CMD_INIT_FAILED commands
queued by sctp_stop_t1_and_abort() against a NULL asoc be dispatched
without the sideeffect interpreter dereferencing asoc?
quoted
@@ -2205,6 +2219,19 @@ enum sctp_disposition sctp_sf_do_5_2_4_dupcook(
      switch (action) {
      case 'A': /* Association restart. */
      case 'B': /* Collision case B. */
+             peer_init = (struct sctp_init_chunk *)
+                             (chunk->subh.cookie_hdr + 1);
+             cid = peer_init->chunk_hdr.type;
+             if (!sctp_sk(ep->base.sk)->cookie_auth_enable &&
+                 !sctp_verify_init(net, ep, asoc, cid, peer_init, chunk,
+                                   &err_chk_p)) {
+                     sctp_association_free(new_asoc);
+                     return sctp_abort_on_init_err(net, ep, asoc, cid, arg,
+                                                   commands, err_chk_p);
+             }
+             if (err_chk_p)
+                     sctp_chunk_free(err_chk_p);
+             fallthrough;
[Critical]
The same attacker-controlled cid is propagated here as well.  In
sctp_sf_do_5_2_4_dupcook() asoc is the existing established association.
With a forged cid of SCTP_CID_INIT_ACK and missing STATE_COOKIE, can a
single unauthenticated COOKIE-ECHO drive sctp_abort_on_init_err() into
its out: path and have sctp_stop_t1_and_abort() tear down the established
asoc and report ECONNREFUSED to the application?

Would constraining the verification to the well-defined INIT case (for
example, hard-coding SCTP_CID_INIT or rejecting any cid !=
SCTP_CID_INIT before calling sctp_verify_init()/sctp_abort_on_init_err())
avoid both of these paths?
I will place a check in sctp_unpack_cookie() for such malformed packets:
diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c
index d5ee81934d93..8adac9e0cd66 100644
--- a/net/sctp/sm_make_chunk.c
+++ b/net/sctp/sm_make_chunk.c
@@ -1761,6 +1761,8 @@ struct sctp_association *sctp_unpack_cookie(
        bear_cookie = &cookie->c;

        ch = (struct sctp_chunkhdr *)(bear_cookie + 1);
+       if (ch->type != SCTP_CID_INIT)
+               goto malformed;
        chlen = ntohs(ch->length);
        if (chlen < sizeof(struct sctp_init_chunk))
                goto malformed;
Thanks.
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help