| From foo@baz Wed May 31 09:13:34 JST 2017 |
| From: Davide Caratti <dcaratti@redhat.com> |
| Date: Thu, 25 May 2017 19:14:56 +0200 |
| Subject: sctp: fix ICMP processing if skb is non-linear |
| |
| From: Davide Caratti <dcaratti@redhat.com> |
| |
| |
| [ Upstream commit 804ec7ebe8ea003999ca8d1bfc499edc6a9e07df ] |
| |
| sometimes ICMP replies to INIT chunks are ignored by the client, even if |
| the encapsulated SCTP headers match an open socket. This happens when the |
| ICMP packet is carried by a paged skb: use skb_header_pointer() to read |
| packet contents beyond the SCTP header, so that chunk header and initiate |
| tag are validated correctly. |
| |
| v2: |
| - don't use skb_header_pointer() to read the transport header, since |
| icmp_socket_deliver() already puts these 8 bytes in the linear area. |
| - change commit message to make specific reference to INIT chunks. |
| |
| Signed-off-by: Davide Caratti <dcaratti@redhat.com> |
| Acked-by: Marcelo Ricardo Leitner <marcelo.leitner@gmail.com> |
| Acked-by: Vlad Yasevich <vyasevich@gmail.com> |
| Reviewed-by: Xin Long <lucien.xin@gmail.com> |
| Signed-off-by: David S. Miller <davem@davemloft.net> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| net/sctp/input.c | 16 +++++++++------- |
| 1 file changed, 9 insertions(+), 7 deletions(-) |
| |
| --- a/net/sctp/input.c |
| +++ b/net/sctp/input.c |
| @@ -473,15 +473,14 @@ struct sock *sctp_err_lookup(struct net |
| struct sctp_association **app, |
| struct sctp_transport **tpp) |
| { |
| + struct sctp_init_chunk *chunkhdr, _chunkhdr; |
| union sctp_addr saddr; |
| union sctp_addr daddr; |
| struct sctp_af *af; |
| struct sock *sk = NULL; |
| struct sctp_association *asoc; |
| struct sctp_transport *transport = NULL; |
| - struct sctp_init_chunk *chunkhdr; |
| __u32 vtag = ntohl(sctphdr->vtag); |
| - int len = skb->len - ((void *)sctphdr - (void *)skb->data); |
| |
| *app = NULL; *tpp = NULL; |
| |
| @@ -516,13 +515,16 @@ struct sock *sctp_err_lookup(struct net |
| * discard the packet. |
| */ |
| if (vtag == 0) { |
| - chunkhdr = (void *)sctphdr + sizeof(struct sctphdr); |
| - if (len < sizeof(struct sctphdr) + sizeof(sctp_chunkhdr_t) |
| - + sizeof(__be32) || |
| + /* chunk header + first 4 octects of init header */ |
| + chunkhdr = skb_header_pointer(skb, skb_transport_offset(skb) + |
| + sizeof(struct sctphdr), |
| + sizeof(struct sctp_chunkhdr) + |
| + sizeof(__be32), &_chunkhdr); |
| + if (!chunkhdr || |
| chunkhdr->chunk_hdr.type != SCTP_CID_INIT || |
| - ntohl(chunkhdr->init_hdr.init_tag) != asoc->c.my_vtag) { |
| + ntohl(chunkhdr->init_hdr.init_tag) != asoc->c.my_vtag) |
| goto out; |
| - } |
| + |
| } else if (vtag != asoc->c.peer_vtag) { |
| goto out; |
| } |