--- /dev/null
+From eae5783908042a762c24e1bd11876edb91d314b1 Mon Sep 17 00:00:00 2001
+From: Xin Long <lucien.xin@gmail.com>
+Date: Wed, 20 Oct 2021 07:42:42 -0400
+Subject: sctp: fix the processing for INIT chunk
+
+From: Xin Long <lucien.xin@gmail.com>
+
+commit eae5783908042a762c24e1bd11876edb91d314b1 upstream.
+
+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>
+Signed-off-by: Ovidiu Panait <ovidiu.panait@windriver.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/sctp/sm_statefuns.c | 71 +++++++++++++++++++++++++++++++-----------------
+ 1 file changed, 46 insertions(+), 25 deletions(-)
+
+--- a/net/sctp/sm_statefuns.c
++++ b/net/sctp/sm_statefuns.c
+@@ -161,6 +161,12 @@ static enum sctp_disposition __sctp_sf_d
+ 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_in
+ 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_in
+ 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 isses close().
+@@ -1460,19 +1466,16 @@ static enum sctp_disposition sctp_sf_do_
+ 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);
+ /* Grab the INIT header. */
+ chunk->subh.init_hdr = (struct sctp_inithdr *)chunk->skb->data;
+
+@@ -1787,9 +1790,9 @@ static enum sctp_disposition sctp_sf_do_
+ * 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;
+
+@@ -2847,13 +2850,11 @@ enum sctp_disposition sctp_sf_do_9_2_shu
+ * 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;
+@@ -2887,6 +2888,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
+ *
--- /dev/null
+From 438b95a7c98f77d51cbf4db021f41b602d750a3f Mon Sep 17 00:00:00 2001
+From: Xin Long <lucien.xin@gmail.com>
+Date: Wed, 20 Oct 2021 07:42:43 -0400
+Subject: sctp: fix the processing for INIT_ACK chunk
+
+From: Xin Long <lucien.xin@gmail.com>
+
+commit 438b95a7c98f77d51cbf4db021f41b602d750a3f upstream.
+
+Currently INIT_ACK chunk in non-cookie_echoed state is processed in
+sctp_sf_discard_chunk() to send an abort with the existent asoc's
+vtag if the chunk length is not valid. But the vtag in the chunk's
+sctphdr is not verified, which may be exploited by one to cook a
+malicious chunk to terminal a SCTP asoc.
+
+sctp_sf_discard_chunk() also is called in many other places to send
+an abort, and most of those have this problem. This patch is to fix
+it by sending abort with the existent asoc's vtag only if the vtag
+from the chunk's sctphdr is verified in sctp_sf_discard_chunk().
+
+Note on sctp_sf_do_9_1_abort() and sctp_sf_shutdown_pending_abort(),
+the chunk length has been verified before sctp_sf_discard_chunk(),
+so replace it with sctp_sf_discard(). On sctp_sf_do_asconf_ack() and
+sctp_sf_do_asconf(), move the sctp_chunk_length_valid check ahead of
+sctp_sf_discard_chunk(), then replace it with sctp_sf_discard().
+
+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>
+Signed-off-by: Ovidiu Panait <ovidiu.panait@windriver.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/sctp/sm_statefuns.c | 37 +++++++++++++++++++------------------
+ 1 file changed, 19 insertions(+), 18 deletions(-)
+
+--- a/net/sctp/sm_statefuns.c
++++ b/net/sctp/sm_statefuns.c
+@@ -2221,7 +2221,7 @@ enum sctp_disposition sctp_sf_shutdown_p
+ */
+ if (SCTP_ADDR_DEL ==
+ sctp_bind_addr_state(&asoc->base.bind_addr, &chunk->dest))
+- return sctp_sf_discard_chunk(net, ep, asoc, type, arg, commands);
++ return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands);
+
+ if (!sctp_err_chunk_valid(chunk))
+ return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands);
+@@ -2267,7 +2267,7 @@ enum sctp_disposition sctp_sf_shutdown_s
+ */
+ if (SCTP_ADDR_DEL ==
+ sctp_bind_addr_state(&asoc->base.bind_addr, &chunk->dest))
+- return sctp_sf_discard_chunk(net, ep, asoc, type, arg, commands);
++ return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands);
+
+ if (!sctp_err_chunk_valid(chunk))
+ return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands);
+@@ -2537,7 +2537,7 @@ enum sctp_disposition sctp_sf_do_9_1_abo
+ */
+ if (SCTP_ADDR_DEL ==
+ sctp_bind_addr_state(&asoc->base.bind_addr, &chunk->dest))
+- return sctp_sf_discard_chunk(net, ep, asoc, type, arg, commands);
++ return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands);
+
+ if (!sctp_err_chunk_valid(chunk))
+ return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands);
+@@ -3702,6 +3702,11 @@ enum sctp_disposition sctp_sf_do_asconf(
+ return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands);
+ }
+
++ /* Make sure that the ASCONF ADDIP chunk has a valid length. */
++ if (!sctp_chunk_length_valid(chunk, sizeof(struct sctp_addip_chunk)))
++ return sctp_sf_violation_chunklen(net, ep, asoc, type, arg,
++ commands);
++
+ /* ADD-IP: Section 4.1.1
+ * This chunk MUST be sent in an authenticated way by using
+ * the mechanism defined in [I-D.ietf-tsvwg-sctp-auth]. If this chunk
+@@ -3709,13 +3714,7 @@ enum sctp_disposition sctp_sf_do_asconf(
+ * described in [I-D.ietf-tsvwg-sctp-auth].
+ */
+ if (!net->sctp.addip_noauth && !chunk->auth)
+- return sctp_sf_discard_chunk(net, ep, asoc, type, arg,
+- commands);
+-
+- /* Make sure that the ASCONF ADDIP chunk has a valid length. */
+- if (!sctp_chunk_length_valid(chunk, sizeof(struct sctp_addip_chunk)))
+- return sctp_sf_violation_chunklen(net, ep, asoc, type, arg,
+- commands);
++ return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands);
+
+ hdr = (struct sctp_addiphdr *)chunk->skb->data;
+ serial = ntohl(hdr->serial);
+@@ -3844,6 +3843,12 @@ enum sctp_disposition sctp_sf_do_asconf_
+ return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands);
+ }
+
++ /* Make sure that the ADDIP chunk has a valid length. */
++ if (!sctp_chunk_length_valid(asconf_ack,
++ sizeof(struct sctp_addip_chunk)))
++ return sctp_sf_violation_chunklen(net, ep, asoc, type, arg,
++ commands);
++
+ /* ADD-IP, Section 4.1.2:
+ * This chunk MUST be sent in an authenticated way by using
+ * the mechanism defined in [I-D.ietf-tsvwg-sctp-auth]. If this chunk
+@@ -3851,14 +3856,7 @@ enum sctp_disposition sctp_sf_do_asconf_
+ * described in [I-D.ietf-tsvwg-sctp-auth].
+ */
+ if (!net->sctp.addip_noauth && !asconf_ack->auth)
+- return sctp_sf_discard_chunk(net, ep, asoc, type, arg,
+- commands);
+-
+- /* Make sure that the ADDIP chunk has a valid length. */
+- if (!sctp_chunk_length_valid(asconf_ack,
+- sizeof(struct sctp_addip_chunk)))
+- return sctp_sf_violation_chunklen(net, ep, asoc, type, arg,
+- commands);
++ return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands);
+
+ addip_hdr = (struct sctp_addiphdr *)asconf_ack->skb->data;
+ rcvd_serial = ntohl(addip_hdr->serial);
+@@ -4435,6 +4433,9 @@ enum sctp_disposition sctp_sf_discard_ch
+ {
+ struct sctp_chunk *chunk = arg;
+
++ if (asoc && !sctp_vtag_verify(chunk, asoc))
++ return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands);
++
+ /* Make sure that the chunk has a valid length.
+ * Since we don't know the chunk type, we use a general
+ * chunkhdr structure to make a comparison.