sctp: Catch bogus stream sequence numbers
authorVlad Yasevich <vladislav.yasevich@hp.com>
Fri, 4 Sep 2009 22:21:03 +0000 (18:21 -0400)
committerVlad Yasevich <vladislav.yasevich@hp.com>
Fri, 4 Sep 2009 22:21:03 +0000 (18:21 -0400)
Since our TSN map is capable of holding at most a 4K chunk gap,
there is no way that during this gap, a stream sequence number
(unsigned short) can wrap such that the new number is smaller
then the next expected one.  If such a case is encountered,
this is a protocol violation.

Signed-off-by: Vlad Yasevich <vladislav.yasevich@hp.com>
net/sctp/sm_statefuns.c

index a7f18a3523641cc462187c8546802ec8d2a443d7..c8fae1983dd15153625d4505500a5030402524d7 100644 (file)
@@ -2891,6 +2891,9 @@ sctp_disposition_t sctp_sf_eat_data_6_2(const struct sctp_endpoint *ep,
                goto discard_force;
        case SCTP_IERROR_NO_DATA:
                goto consume;
+       case SCTP_IERROR_PROTO_VIOLATION:
+               return sctp_sf_abort_violation(ep, asoc, chunk, commands,
+                       (u8 *)chunk->subh.data_hdr, sizeof(sctp_datahdr_t));
        default:
                BUG();
        }
@@ -3001,6 +3004,9 @@ sctp_disposition_t sctp_sf_eat_data_fast_4_4(const struct sctp_endpoint *ep,
                break;
        case SCTP_IERROR_NO_DATA:
                goto consume;
+       case SCTP_IERROR_PROTO_VIOLATION:
+               return sctp_sf_abort_violation(ep, asoc, chunk, commands,
+                       (u8 *)chunk->subh.data_hdr, sizeof(sctp_datahdr_t));
        default:
                BUG();
        }
@@ -5877,6 +5883,9 @@ static int sctp_eat_data(const struct sctp_association *asoc,
        __u32 tsn;
        struct sctp_tsnmap *map = (struct sctp_tsnmap *)&asoc->peer.tsn_map;
        struct sock *sk = asoc->base.sk;
+       u16 ssn;
+       u16 sid;
+       u8 ordered = 0;
 
        data_hdr = chunk->subh.data_hdr = (sctp_datahdr_t *)chunk->skb->data;
        skb_pull(chunk->skb, sizeof(sctp_datahdr_t));
@@ -6016,8 +6025,10 @@ static int sctp_eat_data(const struct sctp_association *asoc,
         */
        if (chunk->chunk_hdr->flags & SCTP_DATA_UNORDERED)
                SCTP_INC_STATS(SCTP_MIB_INUNORDERCHUNKS);
-       else
+       else {
                SCTP_INC_STATS(SCTP_MIB_INORDERCHUNKS);
+               ordered = 1;
+       }
 
        /* RFC 2960 6.5 Stream Identifier and Stream Sequence Number
         *
@@ -6027,7 +6038,8 @@ static int sctp_eat_data(const struct sctp_association *asoc,
         * with cause set to "Invalid Stream Identifier" (See Section 3.3.10)
         * and discard the DATA chunk.
         */
-       if (ntohs(data_hdr->stream) >= asoc->c.sinit_max_instreams) {
+       sid = ntohs(data_hdr->stream);
+       if (sid >= asoc->c.sinit_max_instreams) {
                /* Mark tsn as received even though we drop it */
                sctp_add_cmd_sf(commands, SCTP_CMD_REPORT_TSN, SCTP_U32(tsn));
 
@@ -6040,6 +6052,18 @@ static int sctp_eat_data(const struct sctp_association *asoc,
                return SCTP_IERROR_BAD_STREAM;
        }
 
+       /* Check to see if the SSN is possible for this TSN.
+        * The biggest gap we can record is 4K wide.  Since SSNs wrap
+        * at an unsigned short, there is no way that an SSN can
+        * wrap and for a valid TSN.  We can simply check if the current
+        * SSN is smaller then the next expected one.  If it is, it wrapped
+        * and is invalid.
+        */
+       ssn = ntohs(data_hdr->ssn);
+       if (ordered && SSN_lt(ssn, sctp_ssn_peek(&asoc->ssnmap->in, sid))) {
+               return SCTP_IERROR_PROTO_VIOLATION;
+       }
+
        /* Send the data up to the user.  Note:  Schedule  the
         * SCTP_CMD_CHUNK_ULP cmd before the SCTP_CMD_GEN_SACK, as the SACK
         * chunk needs the updated rwnd.