| 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 |
| @@ -164,6 +164,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. |
| @@ -345,6 +351,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. |
| */ |
| @@ -359,14 +373,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(). |
| @@ -1499,19 +1505,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; |
| |
| @@ -1829,9 +1832,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; |
| |
| @@ -2930,13 +2933,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; |
| @@ -2970,6 +2971,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 |
| * |