SCTP: update sctp_getsockopt helpers to allow oversized buffers
authorNeil Horman <nhorman@tuxdriver.com>
Sat, 16 Jun 2007 18:03:45 +0000 (14:03 -0400)
committerVlad Yasevich <vladislav.yasevich@hp.com>
Tue, 19 Jun 2007 13:46:34 +0000 (09:46 -0400)
I noted the other day while looking at a bug that was ostensibly
in some perl networking library, that we strictly avoid allowing getsockopt
operations to complete if we pass in oversized buffers.  This seems to make
libraries like Perl::NET malfunction since it seems to allocate oversized
buffers for use in several operations.  It also seems to be out of line with
the way udp, tcp and ip getsockopt routines handle buffer input (since the
*optlen pointer in both an input and an output and gets set to the length
of the data that we copy into the buffer).  This patch brings our getsockopt
helpers into line with other protocols, and allows us to accept oversized
buffers for our getsockopt operations.  Tested by me with good results.

Signed-off-by: Neil Horman <nhorman@tuxdriver.com>
Acked-by: Sridhar Samudrala <sri@us.ibm.com>
Signed-off-by: Vlad Yasevich <vladislav.yasevich@hp.com>
net/sctp/socket.c

index 6edaaa009d627ed17ef559f75a950d75a9aa3dec..c1f239ac12b91c961b284829a1b51743a9700a7f 100644 (file)
@@ -3375,12 +3375,13 @@ static int sctp_getsockopt_sctp_status(struct sock *sk, int len,
        sctp_assoc_t associd;
        int retval = 0;
 
        sctp_assoc_t associd;
        int retval = 0;
 
-       if (len != sizeof(status)) {
+       if (len < sizeof(status)) {
                retval = -EINVAL;
                goto out;
        }
 
                retval = -EINVAL;
                goto out;
        }
 
-       if (copy_from_user(&status, optval, sizeof(status))) {
+       len = sizeof(status);
+       if (copy_from_user(&status, optval, len)) {
                retval = -EFAULT;
                goto out;
        }
                retval = -EFAULT;
                goto out;
        }
@@ -3452,12 +3453,13 @@ static int sctp_getsockopt_peer_addr_info(struct sock *sk, int len,
        struct sctp_transport *transport;
        int retval = 0;
 
        struct sctp_transport *transport;
        int retval = 0;
 
-       if (len != sizeof(pinfo)) {
+       if (len < sizeof(pinfo)) {
                retval = -EINVAL;
                goto out;
        }
 
                retval = -EINVAL;
                goto out;
        }
 
-       if (copy_from_user(&pinfo, optval, sizeof(pinfo))) {
+       len = sizeof(pinfo);
+       if (copy_from_user(&pinfo, optval, len)) {
                retval = -EFAULT;
                goto out;
        }
                retval = -EFAULT;
                goto out;
        }
@@ -3523,8 +3525,11 @@ 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)
 {
 static int sctp_getsockopt_events(struct sock *sk, int len, char __user *optval,
                                  int __user *optlen)
 {
-       if (len != sizeof(struct sctp_event_subscribe))
+       if (len < sizeof(struct sctp_event_subscribe))
                return -EINVAL;
                return -EINVAL;
+       len = sizeof(struct sctp_event_subscribe);
+       if (put_user(len, optlen))
+               return -EFAULT;
        if (copy_to_user(optval, &sctp_sk(sk)->subscribe, len))
                return -EFAULT;
        return 0;
        if (copy_to_user(optval, &sctp_sk(sk)->subscribe, len))
                return -EFAULT;
        return 0;
@@ -3546,9 +3551,12 @@ static int sctp_getsockopt_autoclose(struct sock *sk, int len, char __user *optv
        /* Applicable to UDP-style socket only */
        if (sctp_style(sk, TCP))
                return -EOPNOTSUPP;
        /* Applicable to UDP-style socket only */
        if (sctp_style(sk, TCP))
                return -EOPNOTSUPP;
-       if (len != sizeof(int))
+       if (len < sizeof(int))
                return -EINVAL;
                return -EINVAL;
-       if (copy_to_user(optval, &sctp_sk(sk)->autoclose, len))
+       len = sizeof(int);
+       if (put_user(len, optlen))
+               return -EFAULT;
+       if (copy_to_user(optval, &sctp_sk(sk)->autoclose, sizeof(int)))
                return -EFAULT;
        return 0;
 }
                return -EFAULT;
        return 0;
 }
@@ -3599,8 +3607,9 @@ static int sctp_getsockopt_peeloff(struct sock *sk, int len, char __user *optval
        int retval = 0;
        struct sctp_association *asoc;
 
        int retval = 0;
        struct sctp_association *asoc;
 
-       if (len != sizeof(sctp_peeloff_arg_t))
+       if (len < sizeof(sctp_peeloff_arg_t))
                return -EINVAL;
                return -EINVAL;
+       len = sizeof(sctp_peeloff_arg_t);
        if (copy_from_user(&peeloff, optval, len))
                return -EFAULT;
 
        if (copy_from_user(&peeloff, optval, len))
                return -EFAULT;
 
@@ -3628,6 +3637,8 @@ static int sctp_getsockopt_peeloff(struct sock *sk, int len, char __user *optval
 
        /* Return the fd mapped to the new socket.  */
        peeloff.sd = retval;
 
        /* Return the fd mapped to the new socket.  */
        peeloff.sd = retval;
+       if (put_user(len, optlen))
+               return -EFAULT;
        if (copy_to_user(optval, &peeloff, len))
                retval = -EFAULT;
 
        if (copy_to_user(optval, &peeloff, len))
                retval = -EFAULT;
 
@@ -3736,9 +3747,9 @@ static int sctp_getsockopt_peer_addr_params(struct sock *sk, int len,
        struct sctp_association *asoc = NULL;
        struct sctp_sock        *sp = sctp_sk(sk);
 
        struct sctp_association *asoc = NULL;
        struct sctp_sock        *sp = sctp_sk(sk);
 
-       if (len != sizeof(struct sctp_paddrparams))
+       if (len < sizeof(struct sctp_paddrparams))
                return -EINVAL;
                return -EINVAL;
-
+       len = sizeof(struct sctp_paddrparams);
        if (copy_from_user(&params, optval, len))
                return -EFAULT;
 
        if (copy_from_user(&params, optval, len))
                return -EFAULT;
 
@@ -3837,9 +3848,11 @@ static int sctp_getsockopt_delayed_ack_time(struct sock *sk, int len,
        struct sctp_association *asoc = NULL;
        struct sctp_sock        *sp = sctp_sk(sk);
 
        struct sctp_association *asoc = NULL;
        struct sctp_sock        *sp = sctp_sk(sk);
 
-       if (len != sizeof(struct sctp_assoc_value))
+       if (len < sizeof(struct sctp_assoc_value))
                return - EINVAL;
 
                return - EINVAL;
 
+       len = sizeof(struct sctp_assoc_value);
+
        if (copy_from_user(&params, optval, len))
                return -EFAULT;
 
        if (copy_from_user(&params, optval, len))
                return -EFAULT;
 
@@ -3888,8 +3901,11 @@ static int sctp_getsockopt_delayed_ack_time(struct sock *sk, int len,
  */
 static int sctp_getsockopt_initmsg(struct sock *sk, int len, char __user *optval, int __user *optlen)
 {
  */
 static int sctp_getsockopt_initmsg(struct sock *sk, int len, char __user *optval, int __user *optlen)
 {
-       if (len != sizeof(struct sctp_initmsg))
+       if (len < sizeof(struct sctp_initmsg))
                return -EINVAL;
                return -EINVAL;
+       len = sizeof(struct sctp_initmsg);
+       if (put_user(len, optlen))
+               return -EFAULT;
        if (copy_to_user(optval, &sctp_sk(sk)->initmsg, len))
                return -EFAULT;
        return 0;
        if (copy_to_user(optval, &sctp_sk(sk)->initmsg, len))
                return -EFAULT;
        return 0;
@@ -3904,7 +3920,7 @@ static int sctp_getsockopt_peer_addrs_num_old(struct sock *sk, int len,
        struct list_head *pos;
        int cnt = 0;
 
        struct list_head *pos;
        int cnt = 0;
 
-       if (len != sizeof(sctp_assoc_t))
+       if (len < sizeof(sctp_assoc_t))
                return -EINVAL;
 
        if (copy_from_user(&id, optval, sizeof(sctp_assoc_t)))
                return -EINVAL;
 
        if (copy_from_user(&id, optval, sizeof(sctp_assoc_t)))
@@ -3940,10 +3956,12 @@ static int sctp_getsockopt_peer_addrs_old(struct sock *sk, int len,
        struct sctp_sock *sp = sctp_sk(sk);
        int addrlen;
 
        struct sctp_sock *sp = sctp_sk(sk);
        int addrlen;
 
-       if (len != sizeof(struct sctp_getaddrs_old))
+       if (len < sizeof(struct sctp_getaddrs_old))
                return -EINVAL;
 
                return -EINVAL;
 
-       if (copy_from_user(&getaddrs, optval, sizeof(struct sctp_getaddrs_old)))
+       len = sizeof(struct sctp_getaddrs_old);
+
+       if (copy_from_user(&getaddrs, optval, len))
                return -EFAULT;
 
        if (getaddrs.addr_num <= 0) return -EINVAL;
                return -EFAULT;
 
        if (getaddrs.addr_num <= 0) return -EINVAL;
@@ -3966,7 +3984,9 @@ static int sctp_getsockopt_peer_addrs_old(struct sock *sk, int len,
                if (cnt >= getaddrs.addr_num) break;
        }
        getaddrs.addr_num = cnt;
                if (cnt >= getaddrs.addr_num) break;
        }
        getaddrs.addr_num = cnt;
-       if (copy_to_user(optval, &getaddrs, sizeof(struct sctp_getaddrs_old)))
+       if (put_user(len, optlen))
+               return -EFAULT;
+       if (copy_to_user(optval, &getaddrs, len))
                return -EFAULT;
 
        return 0;
                return -EFAULT;
 
        return 0;
@@ -4037,7 +4057,7 @@ static int sctp_getsockopt_local_addrs_num_old(struct sock *sk, int len,
        rwlock_t *addr_lock;
        int cnt = 0;
 
        rwlock_t *addr_lock;
        int cnt = 0;
 
-       if (len != sizeof(sctp_assoc_t))
+       if (len < sizeof(sctp_assoc_t))
                return -EINVAL;
 
        if (copy_from_user(&id, optval, sizeof(sctp_assoc_t)))
                return -EINVAL;
 
        if (copy_from_user(&id, optval, sizeof(sctp_assoc_t)))
@@ -4179,10 +4199,11 @@ static int sctp_getsockopt_local_addrs_old(struct sock *sk, int len,
        void *buf;
        int bytes_copied = 0;
 
        void *buf;
        int bytes_copied = 0;
 
-       if (len != sizeof(struct sctp_getaddrs_old))
+       if (len < sizeof(struct sctp_getaddrs_old))
                return -EINVAL;
 
                return -EINVAL;
 
-       if (copy_from_user(&getaddrs, optval, sizeof(struct sctp_getaddrs_old)))
+       len = sizeof(struct sctp_getaddrs_old);
+       if (copy_from_user(&getaddrs, optval, len))
                return -EFAULT;
 
        if (getaddrs.addr_num <= 0) return -EINVAL;
                return -EFAULT;
 
        if (getaddrs.addr_num <= 0) return -EINVAL;
@@ -4254,7 +4275,7 @@ copy_getaddrs:
 
        /* copy the leading structure back to user */
        getaddrs.addr_num = cnt;
 
        /* copy the leading structure back to user */
        getaddrs.addr_num = cnt;
-       if (copy_to_user(optval, &getaddrs, sizeof(struct sctp_getaddrs_old)))
+       if (copy_to_user(optval, &getaddrs, len))
                err = -EFAULT;
 
 error:
                err = -EFAULT;
 
 error:
@@ -4282,7 +4303,7 @@ static int sctp_getsockopt_local_addrs(struct sock *sk, int len,
        void *addrs;
        void *buf;
 
        void *addrs;
        void *buf;
 
-       if (len <= sizeof(struct sctp_getaddrs))
+       if (len < sizeof(struct sctp_getaddrs))
                return -EINVAL;
 
        if (copy_from_user(&getaddrs, optval, sizeof(struct sctp_getaddrs)))
                return -EINVAL;
 
        if (copy_from_user(&getaddrs, optval, sizeof(struct sctp_getaddrs)))
@@ -4379,10 +4400,12 @@ static int sctp_getsockopt_primary_addr(struct sock *sk, int len,
        struct sctp_association *asoc;
        struct sctp_sock *sp = sctp_sk(sk);
 
        struct sctp_association *asoc;
        struct sctp_sock *sp = sctp_sk(sk);
 
-       if (len != sizeof(struct sctp_prim))
+       if (len < sizeof(struct sctp_prim))
                return -EINVAL;
 
                return -EINVAL;
 
-       if (copy_from_user(&prim, optval, sizeof(struct sctp_prim)))
+       len = sizeof(struct sctp_prim);
+
+       if (copy_from_user(&prim, optval, len))
                return -EFAULT;
 
        asoc = sctp_id2assoc(sk, prim.ssp_assoc_id);
                return -EFAULT;
 
        asoc = sctp_id2assoc(sk, prim.ssp_assoc_id);
@@ -4398,7 +4421,9 @@ static int sctp_getsockopt_primary_addr(struct sock *sk, int len,
        sctp_get_pf_specific(sk->sk_family)->addr_v4map(sp,
                        (union sctp_addr *)&prim.ssp_addr);
 
        sctp_get_pf_specific(sk->sk_family)->addr_v4map(sp,
                        (union sctp_addr *)&prim.ssp_addr);
 
-       if (copy_to_user(optval, &prim, sizeof(struct sctp_prim)))
+       if (put_user(len, optlen))
+               return -EFAULT;
+       if (copy_to_user(optval, &prim, len))
                return -EFAULT;
 
        return 0;
                return -EFAULT;
 
        return 0;
@@ -4415,10 +4440,15 @@ static int sctp_getsockopt_adaptation_layer(struct sock *sk, int len,
 {
        struct sctp_setadaptation adaptation;
 
 {
        struct sctp_setadaptation adaptation;
 
-       if (len != sizeof(struct sctp_setadaptation))
+       if (len < sizeof(struct sctp_setadaptation))
                return -EINVAL;
 
                return -EINVAL;
 
+       len = sizeof(struct sctp_setadaptation);
+
        adaptation.ssb_adaptation_ind = sctp_sk(sk)->adaptation_ind;
        adaptation.ssb_adaptation_ind = sctp_sk(sk)->adaptation_ind;
+
+       if (put_user(len, optlen))
+               return -EFAULT;
        if (copy_to_user(optval, &adaptation, len))
                return -EFAULT;
 
        if (copy_to_user(optval, &adaptation, len))
                return -EFAULT;
 
@@ -4452,9 +4482,12 @@ static int sctp_getsockopt_default_send_param(struct sock *sk,
        struct sctp_association *asoc;
        struct sctp_sock *sp = sctp_sk(sk);
 
        struct sctp_association *asoc;
        struct sctp_sock *sp = sctp_sk(sk);
 
-       if (len != sizeof(struct sctp_sndrcvinfo))
+       if (len < sizeof(struct sctp_sndrcvinfo))
                return -EINVAL;
                return -EINVAL;
-       if (copy_from_user(&info, optval, sizeof(struct sctp_sndrcvinfo)))
+
+       len = sizeof(struct sctp_sndrcvinfo);
+
+       if (copy_from_user(&info, optval, len))
                return -EFAULT;
 
        asoc = sctp_id2assoc(sk, info.sinfo_assoc_id);
                return -EFAULT;
 
        asoc = sctp_id2assoc(sk, info.sinfo_assoc_id);
@@ -4475,7 +4508,9 @@ static int sctp_getsockopt_default_send_param(struct sock *sk,
                info.sinfo_timetolive = sp->default_timetolive;
        }
 
                info.sinfo_timetolive = sp->default_timetolive;
        }
 
-       if (copy_to_user(optval, &info, sizeof(struct sctp_sndrcvinfo)))
+       if (put_user(len, optlen))
+               return -EFAULT;
+       if (copy_to_user(optval, &info, len))
                return -EFAULT;
 
        return 0;
                return -EFAULT;
 
        return 0;
@@ -4526,10 +4561,12 @@ static int sctp_getsockopt_rtoinfo(struct sock *sk, int len,
        struct sctp_rtoinfo rtoinfo;
        struct sctp_association *asoc;
 
        struct sctp_rtoinfo rtoinfo;
        struct sctp_association *asoc;
 
-       if (len != sizeof (struct sctp_rtoinfo))
+       if (len < sizeof (struct sctp_rtoinfo))
                return -EINVAL;
 
                return -EINVAL;
 
-       if (copy_from_user(&rtoinfo, optval, sizeof (struct sctp_rtoinfo)))
+       len = sizeof(struct sctp_rtoinfo);
+
+       if (copy_from_user(&rtoinfo, optval, len))
                return -EFAULT;
 
        asoc = sctp_id2assoc(sk, rtoinfo.srto_assoc_id);
                return -EFAULT;
 
        asoc = sctp_id2assoc(sk, rtoinfo.srto_assoc_id);
@@ -4581,11 +4618,12 @@ static int sctp_getsockopt_associnfo(struct sock *sk, int len,
        struct list_head *pos;
        int cnt = 0;
 
        struct list_head *pos;
        int cnt = 0;
 
-       if (len != sizeof (struct sctp_assocparams))
+       if (len < sizeof (struct sctp_assocparams))
                return -EINVAL;
 
                return -EINVAL;
 
-       if (copy_from_user(&assocparams, optval,
-                       sizeof (struct sctp_assocparams)))
+       len = sizeof(struct sctp_assocparams);
+
+       if (copy_from_user(&assocparams, optval, len))
                return -EFAULT;
 
        asoc = sctp_id2assoc(sk, assocparams.sasoc_assoc_id);
                return -EFAULT;
 
        asoc = sctp_id2assoc(sk, assocparams.sasoc_assoc_id);
@@ -4671,9 +4709,11 @@ static int sctp_getsockopt_context(struct sock *sk, int len,
        struct sctp_sock *sp;
        struct sctp_association *asoc;
 
        struct sctp_sock *sp;
        struct sctp_association *asoc;
 
-       if (len != sizeof(struct sctp_assoc_value))
+       if (len < sizeof(struct sctp_assoc_value))
                return -EINVAL;
 
                return -EINVAL;
 
+       len = sizeof(struct sctp_assoc_value);
+
        if (copy_from_user(&params, optval, len))
                return -EFAULT;
 
        if (copy_from_user(&params, optval, len))
                return -EFAULT;