sctp: fix the processing for INIT chunk
authorXin Long <lucien.xin@gmail.com>
Wed, 20 Oct 2021 11:42:42 +0000 (07:42 -0400)
committerJakub Kicinski <kuba@kernel.org>
Fri, 22 Oct 2021 19:36:43 +0000 (12:36 -0700)
This patch fixes the problems below:

1. In non-shutdown_ack_sent states: in sctp_sf_do_5_1B_init() and
   sctp_sf_do_5_2_2_dupinit():

  chunk length check should be done before any checks that may cause
  to send abort, as making packet for abort will access the init_tag
  from init_hdr in sctp_ootb_pkt_new().

2. In shutdown_ack_sent state: in sctp_sf_do_9_2_reshutack():

  The same checks as does in sctp_sf_do_5_2_2_dupinit() is needed
  for sctp_sf_do_9_2_reshutack().

Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Signed-off-by: Xin Long <lucien.xin@gmail.com>
Acked-by: Marcelo Ricardo Leitner <marcelo.leitner@gmail.com>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
net/sctp/sm_statefuns.c

index 7f83069..9bfa8cc 100644 (file)
@@ -156,6 +156,12 @@ static enum sctp_disposition __sctp_sf_do_9_1_abort(
                                        void *arg,
                                        struct sctp_cmd_seq *commands);
 
+static enum sctp_disposition
+__sctp_sf_do_9_2_reshutack(struct net *net, const struct sctp_endpoint *ep,
+                          const struct sctp_association *asoc,
+                          const union sctp_subtype type, void *arg,
+                          struct sctp_cmd_seq *commands);
+
 /* Small helper function that checks if the chunk length
  * is of the appropriate length.  The 'required_length' argument
  * is set to be the size of a specific chunk we are testing.
@@ -337,6 +343,14 @@ enum sctp_disposition sctp_sf_do_5_1B_init(struct net *net,
        if (!chunk->singleton)
                return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands);
 
+       /* Make sure that the INIT chunk has a valid length.
+        * Normally, this would cause an ABORT with a Protocol Violation
+        * error, but since we don't have an association, we'll
+        * just discard the packet.
+        */
+       if (!sctp_chunk_length_valid(chunk, sizeof(struct sctp_init_chunk)))
+               return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands);
+
        /* If the packet is an OOTB packet which is temporarily on the
         * control endpoint, respond with an ABORT.
         */
@@ -351,14 +365,6 @@ enum sctp_disposition sctp_sf_do_5_1B_init(struct net *net,
        if (chunk->sctp_hdr->vtag != 0)
                return sctp_sf_tabort_8_4_8(net, ep, asoc, type, arg, commands);
 
-       /* Make sure that the INIT chunk has a valid length.
-        * Normally, this would cause an ABORT with a Protocol Violation
-        * error, but since we don't have an association, we'll
-        * just discard the packet.
-        */
-       if (!sctp_chunk_length_valid(chunk, sizeof(struct sctp_init_chunk)))
-               return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands);
-
        /* If the INIT is coming toward a closing socket, we'll send back
         * and ABORT.  Essentially, this catches the race of INIT being
         * backloged to the socket at the same time as the user issues close().
@@ -1524,20 +1530,16 @@ static enum sctp_disposition sctp_sf_do_unexpected_init(
        if (!chunk->singleton)
                return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands);
 
+       /* Make sure that the INIT chunk has a valid length. */
+       if (!sctp_chunk_length_valid(chunk, sizeof(struct sctp_init_chunk)))
+               return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands);
+
        /* 3.1 A packet containing an INIT chunk MUST have a zero Verification
         * Tag.
         */
        if (chunk->sctp_hdr->vtag != 0)
                return sctp_sf_tabort_8_4_8(net, ep, asoc, type, arg, commands);
 
-       /* Make sure that the INIT chunk has a valid length.
-        * In this case, we generate a protocol violation since we have
-        * an association established.
-        */
-       if (!sctp_chunk_length_valid(chunk, sizeof(struct sctp_init_chunk)))
-               return sctp_sf_violation_chunklen(net, ep, asoc, type, arg,
-                                                 commands);
-
        if (SCTP_INPUT_CB(chunk->skb)->encap_port != chunk->transport->encap_port)
                return sctp_sf_new_encap_port(net, ep, asoc, type, arg, commands);
 
@@ -1882,9 +1884,9 @@ static enum sctp_disposition sctp_sf_do_dupcook_a(
         * its peer.
        */
        if (sctp_state(asoc, SHUTDOWN_ACK_SENT)) {
-               disposition = sctp_sf_do_9_2_reshutack(net, ep, asoc,
-                               SCTP_ST_CHUNK(chunk->chunk_hdr->type),
-                               chunk, commands);
+               disposition = __sctp_sf_do_9_2_reshutack(net, ep, asoc,
+                                                        SCTP_ST_CHUNK(chunk->chunk_hdr->type),
+                                                        chunk, commands);
                if (SCTP_DISPOSITION_NOMEM == disposition)
                        goto nomem;
 
@@ -2970,13 +2972,11 @@ enum sctp_disposition sctp_sf_do_9_2_shut_ctsn(
  * that belong to this association, it should discard the INIT chunk and
  * retransmit the SHUTDOWN ACK chunk.
  */
-enum sctp_disposition sctp_sf_do_9_2_reshutack(
-                                       struct net *net,
-                                       const struct sctp_endpoint *ep,
-                                       const struct sctp_association *asoc,
-                                       const union sctp_subtype type,
-                                       void *arg,
-                                       struct sctp_cmd_seq *commands)
+static enum sctp_disposition
+__sctp_sf_do_9_2_reshutack(struct net *net, const struct sctp_endpoint *ep,
+                          const struct sctp_association *asoc,
+                          const union sctp_subtype type, void *arg,
+                          struct sctp_cmd_seq *commands)
 {
        struct sctp_chunk *chunk = arg;
        struct sctp_chunk *reply;
@@ -3010,6 +3010,26 @@ nomem:
        return SCTP_DISPOSITION_NOMEM;
 }
 
+enum sctp_disposition
+sctp_sf_do_9_2_reshutack(struct net *net, const struct sctp_endpoint *ep,
+                        const struct sctp_association *asoc,
+                        const union sctp_subtype type, void *arg,
+                        struct sctp_cmd_seq *commands)
+{
+       struct sctp_chunk *chunk = arg;
+
+       if (!chunk->singleton)
+               return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands);
+
+       if (!sctp_chunk_length_valid(chunk, sizeof(struct sctp_init_chunk)))
+               return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands);
+
+       if (chunk->sctp_hdr->vtag != 0)
+               return sctp_sf_tabort_8_4_8(net, ep, asoc, type, arg, commands);
+
+       return __sctp_sf_do_9_2_reshutack(net, ep, asoc, type, arg, commands);
+}
+
 /*
  * sctp_sf_do_ecn_cwr
  *