sctp: deny peeloff operation on asocs with threads sleeping on it
[pandora-kernel.git] / net / sctp / socket.c
index 27eb73d..93ea5ac 100644 (file)
@@ -1223,9 +1223,12 @@ static int __sctp_connect(struct sock* sk,
 
        timeo = sock_sndtimeo(sk, f_flags & O_NONBLOCK);
 
-       err = sctp_wait_for_connect(asoc, &timeo);
-       if ((err == 0 || err == -EINPROGRESS) && assoc_id)
+       if (assoc_id)
                *assoc_id = asoc->assoc_id;
+       err = sctp_wait_for_connect(asoc, &timeo);
+       /* Note: the asoc may be freed after the return of
+        * sctp_wait_for_connect.
+        */
 
        /* Don't free association on exit. */
        asoc = NULL;
@@ -1524,8 +1527,7 @@ SCTP_STATIC void sctp_close(struct sock *sk, long timeout)
                        struct sctp_chunk *chunk;
 
                        chunk = sctp_make_abort_user(asoc, NULL, 0);
-                       if (chunk)
-                               sctp_primitive_ABORT(asoc, chunk);
+                       sctp_primitive_ABORT(asoc, chunk);
                } else
                        sctp_primitive_SHUTDOWN(asoc, NULL);
        }
@@ -4194,7 +4196,7 @@ static int sctp_getsockopt_disable_fragments(struct sock *sk, int len,
 static int sctp_getsockopt_events(struct sock *sk, int len, char __user *optval,
                                  int __user *optlen)
 {
-       if (len <= 0)
+       if (len == 0)
                return -EINVAL;
        if (len > sizeof(struct sctp_event_subscribe))
                len = sizeof(struct sctp_event_subscribe);
@@ -4240,6 +4242,12 @@ SCTP_STATIC int sctp_do_peeloff(struct sctp_association *asoc,
        struct sctp_af *af;
        int err = 0;
 
+       /* If there is a thread waiting on more sndbuf space for
+        * sending on this asoc, it cannot be peeled.
+        */
+       if (waitqueue_active(&asoc->wait))
+               return -EBUSY;
+
        /* An association cannot be branched off from an already peeled-off
         * socket, nor is this supported for tcp style sockets.
         */
@@ -5309,6 +5317,7 @@ static int sctp_getsockopt_hmac_ident(struct sock *sk, int len,
        struct sctp_hmac_algo_param *hmacs;
        __u16 data_len = 0;
        u32 num_idents;
+       int i;
 
        if (!sctp_auth_enable)
                return -EACCES;
@@ -5326,8 +5335,12 @@ static int sctp_getsockopt_hmac_ident(struct sock *sk, int len,
                return -EFAULT;
        if (put_user(num_idents, &p->shmac_num_idents))
                return -EFAULT;
-       if (copy_to_user(p->shmac_idents, hmacs->hmac_ids, data_len))
-               return -EFAULT;
+       for (i = 0; i < num_idents; i++) {
+               __u16 hmacid = ntohs(hmacs->hmac_ids[i]);
+
+               if (copy_to_user(&p->shmac_idents[i], &hmacid, sizeof(__u16)))
+                       return -EFAULT;
+       }
        return 0;
 }
 
@@ -5582,6 +5595,9 @@ SCTP_STATIC int sctp_getsockopt(struct sock *sk, int level, int optname,
        if (get_user(len, optlen))
                return -EFAULT;
 
+       if (len < 0)
+               return -EINVAL;
+
        sctp_lock_sock(sk);
 
        switch (optname) {
@@ -6241,6 +6257,7 @@ SCTP_STATIC int sctp_msghdr_parse(const struct msghdr *msg,
                        /* Minimally, validate the sinfo_flags. */
                        if (cmsgs->info->sinfo_flags &
                            ~(SCTP_UNORDERED | SCTP_ADDR_OVER |
+                             SCTP_SACK_IMMEDIATELY |
                              SCTP_ABORT | SCTP_EOF))
                                return -EINVAL;
                        break;
@@ -6481,7 +6498,6 @@ static int sctp_wait_for_sndbuf(struct sctp_association *asoc, long *timeo_p,
                 */
                sctp_release_sock(sk);
                current_timeo = schedule_timeout(current_timeo);
-               BUG_ON(sk != asoc->base.sk);
                sctp_lock_sock(sk);
 
                *timeo_p = current_timeo;
@@ -6731,6 +6747,9 @@ void sctp_copy_sock(struct sock *newsk, struct sock *sk,
        newinet->mc_ttl = 1;
        newinet->mc_index = 0;
        newinet->mc_list = NULL;
+
+       if (newsk->sk_flags & SK_FLAGS_TIMESTAMP)
+               net_enable_timestamp();
 }
 
 static inline void sctp_copy_descendant(struct sock *sk_to,