Pull the user copies out of the setsockopt() sub-functions.
Diff minimised by using #define params (*params).
The #define are removed in the next patch.

Signed-off-by: David Laight <[email protected]>

---
 net/sctp/socket.c | 535 +++++++++++++++++++-----------------------------------
 1 file changed, 187 insertions(+), 348 deletions(-)

diff --git a/net/sctp/socket.c b/net/sctp/socket.c
index c1c8215..639d7da 100644
--- a/net/sctp/socket.c
+++ b/net/sctp/socket.c
@@ -965,10 +965,9 @@ int sctp_asconf_mgmt(struct sctp_sock *sp, struct 
sctp_sockaddr_entry *addrw)
  * Returns 0 if ok, <0 errno code on error.
  */
 static int sctp_setsockopt_bindx(struct sock *sk,
-                                struct sockaddr __user *addrs,
+                                struct sockaddr *kaddrs,
                                 int addrs_size, int op)
 {
-       struct sockaddr *kaddrs;
        int err;
        int addrcnt = 0;
        int walk_size = 0;
@@ -977,20 +976,15 @@ static int sctp_setsockopt_bindx(struct sock *sk,
        struct sctp_af *af;
 
        pr_debug("%s: sk:%p addrs:%p addrs_size:%d opt:%d\n",
-                __func__, sk, addrs, addrs_size, op);
+                __func__, sk, kaddrs, addrs_size, op);
 
        if (unlikely(addrs_size <= 0))
                return -EINVAL;
 
-       kaddrs = memdup_user(addrs, addrs_size);
-       if (IS_ERR(kaddrs))
-               return PTR_ERR(kaddrs);
-
        /* Walk through the addrs buffer and count the number of addresses. */
        addr_buf = kaddrs;
        while (walk_size < addrs_size) {
                if (walk_size + sizeof(sa_family_t) > addrs_size) {
-                       kfree(kaddrs);
                        return -EINVAL;
                }
 
@@ -1037,8 +1031,6 @@ static int sctp_setsockopt_bindx(struct sock *sk,
        }
 
 out:
-       kfree(kaddrs);
-
        return err;
 }
 
@@ -1287,24 +1279,19 @@ static int __sctp_connect(struct sock *sk, struct 
sockaddr *kaddrs,
  * Returns >=0 if ok, <0 errno code on error.
  */
 static int __sctp_setsockopt_connectx(struct sock *sk,
-                                     struct sockaddr __user *addrs,
+                                     struct sockaddr *kaddrs,
                                      int addrs_size,
                                      sctp_assoc_t *assoc_id)
 {
-       struct sockaddr *kaddrs;
        int err = 0, flags = 0;
 
        pr_debug("%s: sk:%p addrs:%p addrs_size:%d\n",
-                __func__, sk, addrs, addrs_size);
+                __func__, sk, kaddrs, addrs_size);
 
        /* make sure the 1st addr's sa_family is accessible later */
        if (unlikely(addrs_size < sizeof(sa_family_t)))
                return -EINVAL;
 
-       kaddrs = memdup_user(addrs, addrs_size);
-       if (IS_ERR(kaddrs))
-               return PTR_ERR(kaddrs);
-
        /* Allow security module to validate connectx addresses. */
        err = security_sctp_bind_connect(sk, SCTP_SOCKOPT_CONNECTX,
                                         (struct sockaddr *)kaddrs,
@@ -1321,8 +1308,6 @@ static int __sctp_setsockopt_connectx(struct sock *sk,
        err = __sctp_connect(sk, kaddrs, addrs_size, flags, assoc_id);
 
 out_free:
-       kfree(kaddrs);
-
        return err;
 }
 
@@ -1331,7 +1316,7 @@ static int __sctp_setsockopt_connectx(struct sock *sk,
  * to the option that doesn't provide association id.
  */
 static int sctp_setsockopt_connectx_old(struct sock *sk,
-                                       struct sockaddr __user *addrs,
+                                       struct sockaddr *addrs,
                                        int addrs_size)
 {
        return __sctp_setsockopt_connectx(sk, addrs, addrs_size, NULL);
@@ -1344,7 +1329,7 @@ static int sctp_setsockopt_connectx_old(struct sock *sk,
  * always positive.
  */
 static int sctp_setsockopt_connectx(struct sock *sk,
-                                   struct sockaddr __user *addrs,
+                                   struct sockaddr *addrs,
                                    int addrs_size)
 {
        sctp_assoc_t assoc_id = 0;
@@ -1380,6 +1365,7 @@ static int sctp_getsockopt_connectx3(struct sock *sk, int 
len,
 {
        struct sctp_getaddrs_old param;
        sctp_assoc_t assoc_id = 0;
+       struct sockaddr *addrs;
        int err = 0;
 
 #ifdef CONFIG_COMPAT
@@ -1403,9 +1389,13 @@ static int sctp_getsockopt_connectx3(struct sock *sk, 
int len,
                        return -EFAULT;
        }
 
-       err = __sctp_setsockopt_connectx(sk, (struct sockaddr __user *)
-                                        param.addrs, param.addr_num,
+       addrs = memdup_user(param.addrs, param.addr_num);
+       if (IS_ERR(addrs))
+               return PTR_ERR(addrs);
+
+       err = __sctp_setsockopt_connectx(sk, addrs, param.addr_num,
                                         &assoc_id);
+       kfree(addrs);
        if (err == 0 || err == -EINPROGRESS) {
                if (copy_to_user(optval, &assoc_id, sizeof(assoc_id)))
                        return -EFAULT;
@@ -2188,27 +2178,20 @@ static int sctp_recvmsg(struct sock *sk, struct msghdr 
*msg, size_t len,
  * instead a error will be indicated to the user.
  */
 static int sctp_setsockopt_disable_fragments(struct sock *sk,
-                                            char __user *optval,
+                                            int *optval,
                                             unsigned int optlen)
 {
-       int val;
-
        if (optlen < sizeof(int))
                return -EINVAL;
 
-       if (get_user(val, (int __user *)optval))
-               return -EFAULT;
-
-       sctp_sk(sk)->disable_fragments = (val == 0) ? 0 : 1;
+       sctp_sk(sk)->disable_fragments = (*optval == 0) ? 0 : 1;
 
        return 0;
 }
 
-static int sctp_setsockopt_events(struct sock *sk, char __user *optval,
+static int sctp_setsockopt_events(struct sock *sk, __u8 *sn_type,
                                  unsigned int optlen)
 {
-       struct sctp_event_subscribe subscribe;
-       __u8 *sn_type = (__u8 *)&subscribe;
        struct sctp_sock *sp = sctp_sk(sk);
        struct sctp_association *asoc;
        int i;
@@ -2216,9 +2199,6 @@ static int sctp_setsockopt_events(struct sock *sk, char 
__user *optval,
        if (optlen > sizeof(struct sctp_event_subscribe))
                return -EINVAL;
 
-       if (copy_from_user(&subscribe, optval, optlen))
-               return -EFAULT;
-
        for (i = 0; i < optlen; i++)
                sctp_ulpevent_type_set(&sp->subscribe, SCTP_SN_TYPE_BASE + i,
                                       sn_type[i]);
@@ -2258,7 +2238,7 @@ static int sctp_setsockopt_events(struct sock *sk, char 
__user *optval,
  * integer defining the number of seconds of idle time before an
  * association is closed.
  */
-static int sctp_setsockopt_autoclose(struct sock *sk, char __user *optval,
+static int sctp_setsockopt_autoclose(struct sock *sk, int *optval,
                                     unsigned int optlen)
 {
        struct sctp_sock *sp = sctp_sk(sk);
@@ -2269,8 +2249,8 @@ static int sctp_setsockopt_autoclose(struct sock *sk, 
char __user *optval,
                return -EOPNOTSUPP;
        if (optlen != sizeof(int))
                return -EINVAL;
-       if (copy_from_user(&sp->autoclose, optval, optlen))
-               return -EFAULT;
+
+       sp->autoclose = *optval;
 
        if (sp->autoclose > net->sctp.max_autoclose)
                sp->autoclose = net->sctp.max_autoclose;
@@ -2605,28 +2585,23 @@ static int sctp_apply_peer_addr_params(struct 
sctp_paddrparams *params,
        return 0;
 }
 
+#define params (*params)
 static int sctp_setsockopt_peer_addr_params(struct sock *sk,
-                                           char __user *optval,
+                                           struct sctp_paddrparams params,
                                            unsigned int optlen)
 {
-       struct sctp_paddrparams  params;
        struct sctp_transport   *trans = NULL;
        struct sctp_association *asoc = NULL;
        struct sctp_sock        *sp = sctp_sk(sk);
        int error;
        int hb_change, pmtud_change, sackdelay_change;
 
-       if (optlen == sizeof(params)) {
-               if (copy_from_user(&params, optval, optlen))
-                       return -EFAULT;
-       } else if (optlen == ALIGN(offsetof(struct sctp_paddrparams,
-                                           spp_ipv6_flowlabel), 4)) {
-               if (copy_from_user(&params, optval, optlen))
-                       return -EFAULT;
+       if (optlen != sizeof(params)) {
+               if (optlen != ALIGN(offsetof(struct sctp_paddrparams,
+                                                   spp_ipv6_flowlabel), 4))
+                       return -EINVAL;
                if (params.spp_flags & (SPP_DSCP | SPP_IPV6_FLOWLABEL))
                        return -EINVAL;
-       } else {
-               return -EINVAL;
        }
 
        /* Validate flags and value parameters. */
@@ -2689,6 +2664,7 @@ static int sctp_setsockopt_peer_addr_params(struct sock 
*sk,
 
        return 0;
 }
+#undef params
 
 static inline __u32 sctp_spp_sackdelay_enable(__u32 param_flags)
 {
@@ -2773,17 +2749,15 @@ static void sctp_apply_asoc_delayed_ack(struct 
sctp_sack_info *params,
  *    value to 1 will disable the delayed sack algorithm.
  */
 
+#define params (*params)
 static int sctp_setsockopt_delayed_ack(struct sock *sk,
-                                      char __user *optval, unsigned int optlen)
+                                      struct sctp_sack_info params,
+                                      unsigned int optlen)
 {
        struct sctp_sock *sp = sctp_sk(sk);
        struct sctp_association *asoc;
-       struct sctp_sack_info params;
 
        if (optlen == sizeof(struct sctp_sack_info)) {
-               if (copy_from_user(&params, optval, optlen))
-                       return -EFAULT;
-
                if (params.sack_delay == 0 && params.sack_freq == 0)
                        return 0;
        } else if (optlen == sizeof(struct sctp_assoc_value)) {
@@ -2792,9 +2766,6 @@ static int sctp_setsockopt_delayed_ack(struct sock *sk,
                                    "Use of struct sctp_assoc_value in 
delayed_ack socket option.\n"
                                    "Use struct sctp_sack_info instead\n",
                                    current->comm, task_pid_nr(current));
-               if (copy_from_user(&params, optval, optlen))
-                       return -EFAULT;
-
                if (params.sack_delay == 0)
                        params.sack_freq = 1;
                else
@@ -2860,15 +2831,14 @@ static int sctp_setsockopt_delayed_ack(struct sock *sk,
  * by the change).  With TCP-style sockets, this option is inherited by
  * sockets derived from a listener socket.
  */
-static int sctp_setsockopt_initmsg(struct sock *sk, char __user *optval, 
unsigned int optlen)
+#define sinit (*sinit)
+static int sctp_setsockopt_initmsg(struct sock *sk, struct sctp_initmsg sinit,
+                                  unsigned int optlen)
 {
-       struct sctp_initmsg sinit;
        struct sctp_sock *sp = sctp_sk(sk);
 
        if (optlen != sizeof(struct sctp_initmsg))
                return -EINVAL;
-       if (copy_from_user(&sinit, optval, optlen))
-               return -EFAULT;
 
        if (sinit.sinit_num_ostreams)
                sp->initmsg.sinit_num_ostreams = sinit.sinit_num_ostreams;
@@ -2881,6 +2851,7 @@ static int sctp_setsockopt_initmsg(struct sock *sk, char 
__user *optval, unsigne
 
        return 0;
 }
+#undef sinit
 
 /*
  * 7.1.14 Set default send parameters (SCTP_DEFAULT_SEND_PARAM)
@@ -2896,18 +2867,16 @@ static int sctp_setsockopt_initmsg(struct sock *sk, 
char __user *optval, unsigne
  *   sinfo_timetolive.  The user must provide the sinfo_assoc_id field in
  *   to this call if the caller is using the UDP model.
  */
+#define info (*info)
 static int sctp_setsockopt_default_send_param(struct sock *sk,
-                                             char __user *optval,
+                                             struct sctp_sndrcvinfo info,
                                              unsigned int optlen)
 {
        struct sctp_sock *sp = sctp_sk(sk);
        struct sctp_association *asoc;
-       struct sctp_sndrcvinfo info;
 
        if (optlen != sizeof(info))
                return -EINVAL;
-       if (copy_from_user(&info, optval, optlen))
-               return -EFAULT;
        if (info.sinfo_flags &
            ~(SCTP_UNORDERED | SCTP_ADDR_OVER |
              SCTP_ABORT | SCTP_EOF))
@@ -2958,17 +2927,14 @@ static int sctp_setsockopt_default_send_param(struct 
sock *sk,
  * (SCTP_DEFAULT_SNDINFO)
  */
 static int sctp_setsockopt_default_sndinfo(struct sock *sk,
-                                          char __user *optval,
+                                          struct sctp_sndinfo info,
                                           unsigned int optlen)
 {
        struct sctp_sock *sp = sctp_sk(sk);
        struct sctp_association *asoc;
-       struct sctp_sndinfo info;
 
        if (optlen != sizeof(info))
                return -EINVAL;
-       if (copy_from_user(&info, optval, optlen))
-               return -EFAULT;
        if (info.snd_flags &
            ~(SCTP_UNORDERED | SCTP_ADDR_OVER |
              SCTP_ABORT | SCTP_EOF))
@@ -3018,10 +2984,10 @@ static int sctp_setsockopt_default_sndinfo(struct sock 
*sk,
  * the association primary.  The enclosed address must be one of the
  * association peer's addresses.
  */
-static int sctp_setsockopt_primary_addr(struct sock *sk, char __user *optval,
+#define prim (*prim)
+static int sctp_setsockopt_primary_addr(struct sock *sk, struct sctp_prim prim,
                                        unsigned int optlen)
 {
-       struct sctp_prim prim;
        struct sctp_transport *trans;
        struct sctp_af *af;
        int err;
@@ -3029,9 +2995,6 @@ static int sctp_setsockopt_primary_addr(struct sock *sk, 
char __user *optval,
        if (optlen != sizeof(struct sctp_prim))
                return -EINVAL;
 
-       if (copy_from_user(&prim, optval, sizeof(struct sctp_prim)))
-               return -EFAULT;
-
        /* Allow security module to validate address but need address len. */
        af = sctp_get_af_specific(prim.ssp_addr.ss_family);
        if (!af)
@@ -3051,6 +3014,7 @@ static int sctp_setsockopt_primary_addr(struct sock *sk, 
char __user *optval,
 
        return 0;
 }
+#undef prim
 
 /*
  * 7.1.5 SCTP_NODELAY
@@ -3060,17 +3024,13 @@ static int sctp_setsockopt_primary_addr(struct sock 
*sk, char __user *optval,
  * introduced, at the cost of more packets in the network.  Expects an
  *  integer boolean flag.
  */
-static int sctp_setsockopt_nodelay(struct sock *sk, char __user *optval,
+static int sctp_setsockopt_nodelay(struct sock *sk, int *optval,
                                   unsigned int optlen)
 {
-       int val;
-
        if (optlen < sizeof(int))
                return -EINVAL;
-       if (get_user(val, (int __user *)optval))
-               return -EFAULT;
 
-       sctp_sk(sk)->nodelay = (val == 0) ? 0 : 1;
+       sctp_sk(sk)->nodelay = (*optval == 0) ? 0 : 1;
        return 0;
 }
 
@@ -3086,9 +3046,10 @@ static int sctp_setsockopt_nodelay(struct sock *sk, char 
__user *optval,
  * be changed.
  *
  */
-static int sctp_setsockopt_rtoinfo(struct sock *sk, char __user *optval, 
unsigned int optlen)
+static int sctp_setsockopt_rtoinfo(struct sock *sk,
+                                  struct sctp_rtoinfo params,
+                                  unsigned int optlen)
 {
-       struct sctp_rtoinfo params;
        struct sctp_association *asoc;
        unsigned long rto_min, rto_max;
        struct sctp_sock *sp = sctp_sk(sk);
@@ -3096,9 +3057,6 @@ static int sctp_setsockopt_rtoinfo(struct sock *sk, char 
__user *optval, unsigne
        if (optlen != sizeof (struct sctp_rtoinfo))
                return -EINVAL;
 
-       if (copy_from_user(&params, optval, optlen))
-               return -EFAULT;
-
        asoc = sctp_id2assoc(sk, params.srto_assoc_id);
 
        /* Set the values to the specific association */
@@ -3152,16 +3110,15 @@ static int sctp_setsockopt_rtoinfo(struct sock *sk, 
char __user *optval, unsigne
  * See [SCTP] for more information.
  *
  */
-static int sctp_setsockopt_associnfo(struct sock *sk, char __user *optval, 
unsigned int optlen)
+static int sctp_setsockopt_associnfo(struct sock *sk,
+                                    struct sctp_assocparams params,
+                                    unsigned int optlen)
 {
 
-       struct sctp_assocparams params;
        struct sctp_association *asoc;
 
        if (optlen != sizeof(struct sctp_assocparams))
                return -EINVAL;
-       if (copy_from_user(&params, optval, optlen))
-               return -EFAULT;
 
        asoc = sctp_id2assoc(sk, params.sasoc_assoc_id);
 
@@ -3220,16 +3177,14 @@ static int sctp_setsockopt_associnfo(struct sock *sk, 
char __user *optval, unsig
  * addresses and a user will receive both PF_INET6 and PF_INET type
  * addresses on the socket.
  */
-static int sctp_setsockopt_mappedv4(struct sock *sk, char __user *optval, 
unsigned int optlen)
+static int sctp_setsockopt_mappedv4(struct sock *sk, int *optval,
+                                   unsigned int optlen)
 {
-       int val;
        struct sctp_sock *sp = sctp_sk(sk);
 
        if (optlen < sizeof(int))
                return -EINVAL;
-       if (get_user(val, (int __user *)optval))
-               return -EFAULT;
-       if (val)
+       if (*optval)
                sp->v4mapped = 1;
        else
                sp->v4mapped = 0;
@@ -3264,10 +3219,11 @@ static int sctp_setsockopt_mappedv4(struct sock *sk, 
char __user *optval, unsign
  *    changed (effecting future associations only).
  * assoc_value:  This parameter specifies the maximum size in bytes.
  */
-static int sctp_setsockopt_maxseg(struct sock *sk, char __user *optval, 
unsigned int optlen)
+static int sctp_setsockopt_maxseg(struct sock *sk,
+                                 struct sctp_assoc_value params,
+                                 unsigned int optlen)
 {
        struct sctp_sock *sp = sctp_sk(sk);
-       struct sctp_assoc_value params;
        struct sctp_association *asoc;
        int val;
 
@@ -3277,12 +3233,9 @@ static int sctp_setsockopt_maxseg(struct sock *sk, char 
__user *optval, unsigned
                                    "Use of int in maxseg socket option.\n"
                                    "Use struct sctp_assoc_value instead\n",
                                    current->comm, task_pid_nr(current));
-               if (copy_from_user(&val, optval, optlen))
-                       return -EFAULT;
+               val = *(int *)&params;
                params.assoc_id = SCTP_FUTURE_ASSOC;
-       } else if (optlen == sizeof(struct sctp_assoc_value)) {
-               if (copy_from_user(&params, optval, optlen))
-                       return -EFAULT;
+       } else if (optlen != sizeof(struct sctp_assoc_value)) {
                val = params.assoc_value;
        } else {
                return -EINVAL;
@@ -3324,12 +3277,13 @@ static int sctp_setsockopt_maxseg(struct sock *sk, char 
__user *optval, unsigned
  *   locally bound addresses. The following structure is used to make a
  *   set primary request:
  */
-static int sctp_setsockopt_peer_primary_addr(struct sock *sk, char __user 
*optval,
+#define prim (*prim)
+static int sctp_setsockopt_peer_primary_addr(struct sock *sk,
+                                            struct sctp_setpeerprim prim,
                                             unsigned int optlen)
 {
        struct sctp_sock        *sp;
        struct sctp_association *asoc = NULL;
-       struct sctp_setpeerprim prim;
        struct sctp_chunk       *chunk;
        struct sctp_af          *af;
        int                     err;
@@ -3342,9 +3296,6 @@ static int sctp_setsockopt_peer_primary_addr(struct sock 
*sk, char __user *optva
        if (optlen != sizeof(struct sctp_setpeerprim))
                return -EINVAL;
 
-       if (copy_from_user(&prim, optval, optlen))
-               return -EFAULT;
-
        asoc = sctp_id2assoc(sk, prim.sspp_assoc_id);
        if (!asoc)
                return -EINVAL;
@@ -3387,18 +3338,16 @@ static int sctp_setsockopt_peer_primary_addr(struct 
sock *sk, char __user *optva
 
        return err;
 }
+#undef prim
 
-static int sctp_setsockopt_adaptation_layer(struct sock *sk, char __user 
*optval,
+static int sctp_setsockopt_adaptation_layer(struct sock *sk,
+                                           struct sctp_setadaptation 
*adaptation,
                                            unsigned int optlen)
 {
-       struct sctp_setadaptation adaptation;
-
        if (optlen != sizeof(struct sctp_setadaptation))
                return -EINVAL;
-       if (copy_from_user(&adaptation, optval, optlen))
-               return -EFAULT;
 
-       sctp_sk(sk)->adaptation_ind = adaptation.ssb_adaptation_ind;
+       sctp_sk(sk)->adaptation_ind = adaptation->ssb_adaptation_ind;
 
        return 0;
 }
@@ -3417,17 +3366,15 @@ static int sctp_setsockopt_adaptation_layer(struct sock 
*sk, char __user *optval
  * received messages from the peer and does not effect the value that is
  * saved with outbound messages.
  */
-static int sctp_setsockopt_context(struct sock *sk, char __user *optval,
+static int sctp_setsockopt_context(struct sock *sk,
+                                  struct sctp_assoc_value params,
                                   unsigned int optlen)
 {
        struct sctp_sock *sp = sctp_sk(sk);
-       struct sctp_assoc_value params;
        struct sctp_association *asoc;
 
        if (optlen != sizeof(struct sctp_assoc_value))
                return -EINVAL;
-       if (copy_from_user(&params, optval, optlen))
-               return -EFAULT;
 
        asoc = sctp_id2assoc(sk, params.assoc_id);
        if (!asoc && params.assoc_id > SCTP_ALL_ASSOC &&
@@ -3480,17 +3427,13 @@ static int sctp_setsockopt_context(struct sock *sk, 
char __user *optval,
  * incorrectly.
  */
 static int sctp_setsockopt_fragment_interleave(struct sock *sk,
-                                              char __user *optval,
+                                              int *optval,
                                               unsigned int optlen)
 {
-       int val;
-
        if (optlen != sizeof(int))
                return -EINVAL;
-       if (get_user(val, (int __user *)optval))
-               return -EFAULT;
 
-       sctp_sk(sk)->frag_interleave = !!val;
+       sctp_sk(sk)->frag_interleave = !!*optval;
 
        if (!sctp_sk(sk)->frag_interleave)
                sctp_sk(sk)->ep->intl_enable = 0;
@@ -3516,23 +3459,19 @@ static int sctp_setsockopt_fragment_interleave(struct 
sock *sk,
  * message.
  */
 static int sctp_setsockopt_partial_delivery_point(struct sock *sk,
-                                                 char __user *optval,
+                                                 u32 *optval,
                                                  unsigned int optlen)
 {
-       u32 val;
-
        if (optlen != sizeof(u32))
                return -EINVAL;
-       if (get_user(val, (int __user *)optval))
-               return -EFAULT;
 
        /* Note: We double the receive buffer from what the user sets
         * it to be, also initial rwnd is based on rcvbuf/2.
         */
-       if (val > (sk->sk_rcvbuf >> 1))
+       if (*optval > (sk->sk_rcvbuf >> 1))
                return -EINVAL;
 
-       sctp_sk(sk)->pd_point = val;
+       sctp_sk(sk)->pd_point = *optval;
 
        return 0; /* is this the right error code? */
 }
@@ -3549,11 +3488,10 @@ static int 
sctp_setsockopt_partial_delivery_point(struct sock *sk,
  * future associations inheriting the socket value.
  */
 static int sctp_setsockopt_maxburst(struct sock *sk,
-                                   char __user *optval,
+                                   struct sctp_assoc_value params,
                                    unsigned int optlen)
 {
        struct sctp_sock *sp = sctp_sk(sk);
-       struct sctp_assoc_value params;
        struct sctp_association *asoc;
 
        if (optlen == sizeof(int)) {
@@ -3562,14 +3500,11 @@ static int sctp_setsockopt_maxburst(struct sock *sk,
                                    "Use of int in max_burst socket option 
deprecated.\n"
                                    "Use struct sctp_assoc_value instead\n",
                                    current->comm, task_pid_nr(current));
-               if (copy_from_user(&params.assoc_value, optval, optlen))
-                       return -EFAULT;
+               params.assoc_value = *(int *)&params;
                params.assoc_id = SCTP_FUTURE_ASSOC;
-       } else if (optlen == sizeof(struct sctp_assoc_value)) {
-               if (copy_from_user(&params, optval, optlen))
-                       return -EFAULT;
-       } else
+       } else if (optlen != sizeof(struct sctp_assoc_value)) {
                return -EINVAL;
+       }
 
        asoc = sctp_id2assoc(sk, params.assoc_id);
        if (!asoc && params.assoc_id > SCTP_ALL_ASSOC &&
@@ -3604,20 +3539,18 @@ static int sctp_setsockopt_maxburst(struct sock *sk,
  * received only in an authenticated way.  Changes to the list of chunks
  * will only effect future associations on the socket.
  */
+#define val (*val)
 static int sctp_setsockopt_auth_chunk(struct sock *sk,
-                                     char __user *optval,
+                                     struct sctp_authchunk val,
                                      unsigned int optlen)
 {
        struct sctp_endpoint *ep = sctp_sk(sk)->ep;
-       struct sctp_authchunk val;
 
        if (!ep->auth_enable)
                return -EACCES;
 
        if (optlen != sizeof(struct sctp_authchunk))
                return -EINVAL;
-       if (copy_from_user(&val, optval, optlen))
-               return -EFAULT;
 
        switch (val.sauth_chunk) {
        case SCTP_CID_INIT:
@@ -3638,11 +3571,10 @@ static int sctp_setsockopt_auth_chunk(struct sock *sk,
  * endpoint requires the peer to use.
  */
 static int sctp_setsockopt_hmac_ident(struct sock *sk,
-                                     char __user *optval,
+                                     struct sctp_hmacalgo *hmacs,
                                      unsigned int optlen)
 {
        struct sctp_endpoint *ep = sctp_sk(sk)->ep;
-       struct sctp_hmacalgo *hmacs;
        u32 idents;
        int err;
 
@@ -3654,10 +3586,6 @@ static int sctp_setsockopt_hmac_ident(struct sock *sk,
        optlen = min_t(unsigned int, optlen, sizeof(struct sctp_hmacalgo) +
                                             SCTP_AUTH_NUM_HMACS * sizeof(u16));
 
-       hmacs = memdup_user(optval, optlen);
-       if (IS_ERR(hmacs))
-               return PTR_ERR(hmacs);
-
        idents = hmacs->shmac_num_idents;
        if (idents == 0 || idents > SCTP_AUTH_NUM_HMACS ||
            (idents * sizeof(u16)) > (optlen - sizeof(struct sctp_hmacalgo))) {
@@ -3667,7 +3595,6 @@ static int sctp_setsockopt_hmac_ident(struct sock *sk,
 
        err = sctp_auth_ep_set_hmacs(ep, hmacs);
 out:
-       kfree(hmacs);
        return err;
 }
 
@@ -3678,11 +3605,10 @@ static int sctp_setsockopt_hmac_ident(struct sock *sk,
  * association shared key.
  */
 static int sctp_setsockopt_auth_key(struct sock *sk,
-                                   char __user *optval,
+                                   struct sctp_authkey *authkey,
                                    unsigned int optlen)
 {
        struct sctp_endpoint *ep = sctp_sk(sk)->ep;
-       struct sctp_authkey *authkey;
        struct sctp_association *asoc;
        int ret = -EINVAL;
 
@@ -3693,10 +3619,6 @@ static int sctp_setsockopt_auth_key(struct sock *sk,
         */
        optlen = min_t(unsigned int, optlen, USHRT_MAX + sizeof(*authkey));
 
-       authkey = memdup_user(optval, optlen);
-       if (IS_ERR(authkey))
-               return PTR_ERR(authkey);
-
        if (authkey->sca_keylength > optlen - sizeof(*authkey))
                goto out;
 
@@ -3733,7 +3655,7 @@ static int sctp_setsockopt_auth_key(struct sock *sk,
        }
 
 out:
-       kzfree(authkey);
+       memzero_explicit(authkey, optlen);
        return ret;
 }
 
@@ -3744,18 +3666,15 @@ static int sctp_setsockopt_auth_key(struct sock *sk,
  * the association shared key.
  */
 static int sctp_setsockopt_active_key(struct sock *sk,
-                                     char __user *optval,
+                                     struct sctp_authkeyid val,
                                      unsigned int optlen)
 {
        struct sctp_endpoint *ep = sctp_sk(sk)->ep;
        struct sctp_association *asoc;
-       struct sctp_authkeyid val;
        int ret = 0;
 
        if (optlen != sizeof(struct sctp_authkeyid))
                return -EINVAL;
-       if (copy_from_user(&val, optval, optlen))
-               return -EFAULT;
 
        asoc = sctp_id2assoc(sk, val.scact_assoc_id);
        if (!asoc && val.scact_assoc_id > SCTP_ALL_ASSOC &&
@@ -3795,18 +3714,15 @@ static int sctp_setsockopt_active_key(struct sock *sk,
  * This set option will delete a shared secret key from use.
  */
 static int sctp_setsockopt_del_key(struct sock *sk,
-                                  char __user *optval,
+                                  struct sctp_authkeyid val,
                                   unsigned int optlen)
 {
        struct sctp_endpoint *ep = sctp_sk(sk)->ep;
        struct sctp_association *asoc;
-       struct sctp_authkeyid val;
        int ret = 0;
 
        if (optlen != sizeof(struct sctp_authkeyid))
                return -EINVAL;
-       if (copy_from_user(&val, optval, optlen))
-               return -EFAULT;
 
        asoc = sctp_id2assoc(sk, val.scact_assoc_id);
        if (!asoc && val.scact_assoc_id > SCTP_ALL_ASSOC &&
@@ -3845,18 +3761,16 @@ static int sctp_setsockopt_del_key(struct sock *sk,
  *
  * This set option will deactivate a shared secret key.
  */
-static int sctp_setsockopt_deactivate_key(struct sock *sk, char __user *optval,
+static int sctp_setsockopt_deactivate_key(struct sock *sk,
+                                         struct sctp_authkeyid val,
                                          unsigned int optlen)
 {
        struct sctp_endpoint *ep = sctp_sk(sk)->ep;
        struct sctp_association *asoc;
-       struct sctp_authkeyid val;
        int ret = 0;
 
        if (optlen != sizeof(struct sctp_authkeyid))
                return -EINVAL;
-       if (copy_from_user(&val, optval, optlen))
-               return -EFAULT;
 
        asoc = sctp_id2assoc(sk, val.scact_assoc_id);
        if (!asoc && val.scact_assoc_id > SCTP_ALL_ASSOC &&
@@ -3889,6 +3803,7 @@ static int sctp_setsockopt_deactivate_key(struct sock 
*sk, char __user *optval,
 
        return ret;
 }
+#undef val
 
 /*
  * 8.1.23 SCTP_AUTO_ASCONF
@@ -3904,16 +3819,14 @@ static int sctp_setsockopt_deactivate_key(struct sock 
*sk, char __user *optval,
  * Note. In this implementation, socket operation overrides default parameter
  * being set by sysctl as well as FreeBSD implementation
  */
-static int sctp_setsockopt_auto_asconf(struct sock *sk, char __user *optval,
-                                       unsigned int optlen)
+#define val (*optval)
+static int sctp_setsockopt_auto_asconf(struct sock *sk, int val,
+                                      unsigned int optlen)
 {
-       int val;
        struct sctp_sock *sp = sctp_sk(sk);
 
        if (optlen < sizeof(int))
                return -EINVAL;
-       if (get_user(val, (int __user *)optval))
-               return -EFAULT;
        if (!sctp_is_ep_boundall(sk) && val)
                return -EINVAL;
        if ((val && sp->do_auto_asconf) || (!val && !sp->do_auto_asconf))
@@ -3931,6 +3844,7 @@ static int sctp_setsockopt_auto_asconf(struct sock *sk, 
char __user *optval,
        spin_unlock_bh(&sock_net(sk)->sctp.addr_wq_lock);
        return 0;
 }
+#undef val
 
 /*
  * SCTP_PEER_ADDR_THLDS
@@ -3939,11 +3853,11 @@ static int sctp_setsockopt_auto_asconf(struct sock *sk, 
char __user *optval,
  * transports in an association.  See Section 6.1 of:
  * http://www.ietf.org/id/draft-nishida-tsvwg-sctp-failover-05.txt
  */
+#define val (*val)
 static int sctp_setsockopt_paddr_thresholds(struct sock *sk,
-                                           char __user *optval,
+                                           struct sctp_paddrthlds_v2 val,
                                            unsigned int optlen, bool v2)
 {
-       struct sctp_paddrthlds_v2 val;
        struct sctp_transport *trans;
        struct sctp_association *asoc;
        int len;
@@ -3951,8 +3865,6 @@ static int sctp_setsockopt_paddr_thresholds(struct sock 
*sk,
        len = v2 ? sizeof(val) : sizeof(struct sctp_paddrthlds);
        if (optlen < len)
                return -EINVAL;
-       if (copy_from_user(&val, optval, len))
-               return -EFAULT;
 
        if (v2 && val.spt_pathpfthld > val.spt_pathcpthld)
                return -EINVAL;
@@ -4004,52 +3916,39 @@ static int sctp_setsockopt_paddr_thresholds(struct sock 
*sk,
 
        return 0;
 }
+#undef val
 
-static int sctp_setsockopt_recvrcvinfo(struct sock *sk,
-                                      char __user *optval,
+static int sctp_setsockopt_recvrcvinfo(struct sock *sk, int *optval,
                                       unsigned int optlen)
 {
-       int val;
-
        if (optlen < sizeof(int))
                return -EINVAL;
-       if (get_user(val, (int __user *) optval))
-               return -EFAULT;
 
-       sctp_sk(sk)->recvrcvinfo = (val == 0) ? 0 : 1;
+       sctp_sk(sk)->recvrcvinfo = (*optval == 0) ? 0 : 1;
 
        return 0;
 }
 
-static int sctp_setsockopt_recvnxtinfo(struct sock *sk,
-                                      char __user *optval,
+static int sctp_setsockopt_recvnxtinfo(struct sock *sk, int *optval,
                                       unsigned int optlen)
 {
-       int val;
-
        if (optlen < sizeof(int))
                return -EINVAL;
-       if (get_user(val, (int __user *) optval))
-               return -EFAULT;
 
-       sctp_sk(sk)->recvnxtinfo = (val == 0) ? 0 : 1;
+       sctp_sk(sk)->recvnxtinfo = (*optval == 0) ? 0 : 1;
 
        return 0;
 }
 
 static int sctp_setsockopt_pr_supported(struct sock *sk,
-                                       char __user *optval,
+                                       struct sctp_assoc_value params,
                                        unsigned int optlen)
 {
-       struct sctp_assoc_value params;
        struct sctp_association *asoc;
 
        if (optlen != sizeof(params))
                return -EINVAL;
 
-       if (copy_from_user(&params, optval, optlen))
-               return -EFAULT;
-
        asoc = sctp_id2assoc(sk, params.assoc_id);
        if (!asoc && params.assoc_id != SCTP_FUTURE_ASSOC &&
            sctp_style(sk, UDP))
@@ -4061,22 +3960,16 @@ static int sctp_setsockopt_pr_supported(struct sock *sk,
 }
 
 static int sctp_setsockopt_default_prinfo(struct sock *sk,
-                                         char __user *optval,
+                                         struct sctp_default_prinfo info,
                                          unsigned int optlen)
 {
        struct sctp_sock *sp = sctp_sk(sk);
-       struct sctp_default_prinfo info;
        struct sctp_association *asoc;
        int retval = -EINVAL;
 
        if (optlen != sizeof(info))
                goto out;
 
-       if (copy_from_user(&info, optval, sizeof(info))) {
-               retval = -EFAULT;
-               goto out;
-       }
-
        if (info.pr_policy & ~SCTP_PR_SCTP_MASK)
                goto out;
 
@@ -4116,23 +4009,18 @@ static int sctp_setsockopt_default_prinfo(struct sock 
*sk,
 out:
        return retval;
 }
+#undef info
 
 static int sctp_setsockopt_reconfig_supported(struct sock *sk,
-                                             char __user *optval,
+                                             struct sctp_assoc_value params,
                                              unsigned int optlen)
 {
-       struct sctp_assoc_value params;
        struct sctp_association *asoc;
        int retval = -EINVAL;
 
        if (optlen != sizeof(params))
                goto out;
 
-       if (copy_from_user(&params, optval, optlen)) {
-               retval = -EFAULT;
-               goto out;
-       }
-
        asoc = sctp_id2assoc(sk, params.assoc_id);
        if (!asoc && params.assoc_id != SCTP_FUTURE_ASSOC &&
            sctp_style(sk, UDP))
@@ -4147,22 +4035,16 @@ static int sctp_setsockopt_reconfig_supported(struct 
sock *sk,
 }
 
 static int sctp_setsockopt_enable_strreset(struct sock *sk,
-                                          char __user *optval,
+                                          struct sctp_assoc_value params,
                                           unsigned int optlen)
 {
        struct sctp_endpoint *ep = sctp_sk(sk)->ep;
-       struct sctp_assoc_value params;
        struct sctp_association *asoc;
        int retval = -EINVAL;
 
        if (optlen != sizeof(params))
                goto out;
 
-       if (copy_from_user(&params, optval, optlen)) {
-               retval = -EFAULT;
-               goto out;
-       }
-
        if (params.assoc_value & (~SCTP_ENABLE_STRRESET_MASK))
                goto out;
 
@@ -4194,11 +4076,11 @@ static int sctp_setsockopt_enable_strreset(struct sock 
*sk,
        return retval;
 }
 
+#undef params
 static int sctp_setsockopt_reset_streams(struct sock *sk,
-                                        char __user *optval,
+                                        struct sctp_reset_streams *params,
                                         unsigned int optlen)
 {
-       struct sctp_reset_streams *params;
        struct sctp_association *asoc;
        int retval = -EINVAL;
 
@@ -4208,10 +4090,6 @@ static int sctp_setsockopt_reset_streams(struct sock *sk,
        optlen = min_t(unsigned int, optlen, USHRT_MAX +
                                             sizeof(__u16) * sizeof(*params));
 
-       params = memdup_user(optval, optlen);
-       if (IS_ERR(params))
-               return PTR_ERR(params);
-
        if (params->srs_number_streams * sizeof(__u16) >
            optlen - sizeof(*params))
                goto out;
@@ -4223,26 +4101,21 @@ static int sctp_setsockopt_reset_streams(struct sock 
*sk,
        retval = sctp_send_reset_streams(asoc, params);
 
 out:
-       kfree(params);
        return retval;
 }
+#define params (*params)
 
-static int sctp_setsockopt_reset_assoc(struct sock *sk,
-                                      char __user *optval,
+
+#define associd (*associd)
+static int sctp_setsockopt_reset_assoc(struct sock *sk, sctp_assoc_t associd,
                                       unsigned int optlen)
 {
        struct sctp_association *asoc;
-       sctp_assoc_t associd;
        int retval = -EINVAL;
 
        if (optlen != sizeof(associd))
                goto out;
 
-       if (copy_from_user(&associd, optval, optlen)) {
-               retval = -EFAULT;
-               goto out;
-       }
-
        asoc = sctp_id2assoc(sk, associd);
        if (!asoc)
                goto out;
@@ -4252,23 +4125,18 @@ static int sctp_setsockopt_reset_assoc(struct sock *sk,
 out:
        return retval;
 }
+#undef associd
 
 static int sctp_setsockopt_add_streams(struct sock *sk,
-                                      char __user *optval,
+                                      struct sctp_add_streams params,
                                       unsigned int optlen)
 {
        struct sctp_association *asoc;
-       struct sctp_add_streams params;
        int retval = -EINVAL;
 
        if (optlen != sizeof(params))
                goto out;
 
-       if (copy_from_user(&params, optval, optlen)) {
-               retval = -EFAULT;
-               goto out;
-       }
-
        asoc = sctp_id2assoc(sk, params.sas_assoc_id);
        if (!asoc)
                goto out;
@@ -4280,21 +4148,16 @@ static int sctp_setsockopt_add_streams(struct sock *sk,
 }
 
 static int sctp_setsockopt_scheduler(struct sock *sk,
-                                    char __user *optval,
+                                    struct sctp_assoc_value params,
                                     unsigned int optlen)
 {
        struct sctp_sock *sp = sctp_sk(sk);
        struct sctp_association *asoc;
-       struct sctp_assoc_value params;
        int retval = 0;
 
        if (optlen < sizeof(params))
                return -EINVAL;
 
-       optlen = sizeof(params);
-       if (copy_from_user(&params, optval, optlen))
-               return -EFAULT;
-
        if (params.assoc_value > SCTP_SS_MAX)
                return -EINVAL;
 
@@ -4328,22 +4191,15 @@ static int sctp_setsockopt_scheduler(struct sock *sk,
 }
 
 static int sctp_setsockopt_scheduler_value(struct sock *sk,
-                                          char __user *optval,
+                                          struct sctp_stream_value params,
                                           unsigned int optlen)
 {
-       struct sctp_stream_value params;
        struct sctp_association *asoc;
        int retval = -EINVAL;
 
        if (optlen < sizeof(params))
                goto out;
 
-       optlen = sizeof(params);
-       if (copy_from_user(&params, optval, optlen)) {
-               retval = -EFAULT;
-               goto out;
-       }
-
        asoc = sctp_id2assoc(sk, params.assoc_id);
        if (!asoc && params.assoc_id != SCTP_CURRENT_ASSOC &&
            sctp_style(sk, UDP))
@@ -4369,23 +4225,16 @@ static int sctp_setsockopt_scheduler_value(struct sock 
*sk,
 }
 
 static int sctp_setsockopt_interleaving_supported(struct sock *sk,
-                                                 char __user *optval,
+                                                 struct sctp_assoc_value 
params,
                                                  unsigned int optlen)
 {
        struct sctp_sock *sp = sctp_sk(sk);
-       struct sctp_assoc_value params;
        struct sctp_association *asoc;
        int retval = -EINVAL;
 
        if (optlen < sizeof(params))
                goto out;
 
-       optlen = sizeof(params);
-       if (copy_from_user(&params, optval, optlen)) {
-               retval = -EFAULT;
-               goto out;
-       }
-
        asoc = sctp_id2assoc(sk, params.assoc_id);
        if (!asoc && params.assoc_id != SCTP_FUTURE_ASSOC &&
            sctp_style(sk, UDP))
@@ -4404,11 +4253,9 @@ static int sctp_setsockopt_interleaving_supported(struct 
sock *sk,
        return retval;
 }
 
-static int sctp_setsockopt_reuse_port(struct sock *sk, char __user *optval,
+static int sctp_setsockopt_reuse_port(struct sock *sk, int *optval,
                                      unsigned int optlen)
 {
-       int val;
-
        if (!sctp_style(sk, TCP))
                return -EOPNOTSUPP;
 
@@ -4418,10 +4265,7 @@ static int sctp_setsockopt_reuse_port(struct sock *sk, 
char __user *optval,
        if (optlen < sizeof(int))
                return -EINVAL;
 
-       if (get_user(val, (int __user *)optval))
-               return -EFAULT;
-
-       sctp_sk(sk)->reuse = !!val;
+       sctp_sk(sk)->reuse = !!*optval;
 
        return 0;
 }
@@ -4447,21 +4291,17 @@ static int sctp_assoc_ulpevent_type_set(struct 
sctp_event *param,
        return 0;
 }
 
-static int sctp_setsockopt_event(struct sock *sk, char __user *optval,
+#define param (*param)
+static int sctp_setsockopt_event(struct sock *sk, struct sctp_event param,
                                 unsigned int optlen)
 {
        struct sctp_sock *sp = sctp_sk(sk);
        struct sctp_association *asoc;
-       struct sctp_event param;
        int retval = 0;
 
        if (optlen < sizeof(param))
                return -EINVAL;
 
-       optlen = sizeof(param);
-       if (copy_from_user(&param, optval, optlen))
-               return -EFAULT;
-
        if (param.se_type < SCTP_SN_TYPE_BASE ||
            param.se_type > SCTP_SN_TYPE_MAX)
                return -EINVAL;
@@ -4494,12 +4334,12 @@ static int sctp_setsockopt_event(struct sock *sk, char 
__user *optval,
 
        return retval;
 }
+#undef param
 
 static int sctp_setsockopt_asconf_supported(struct sock *sk,
-                                           char __user *optval,
+                                           struct sctp_assoc_value params,
                                            unsigned int optlen)
 {
-       struct sctp_assoc_value params;
        struct sctp_association *asoc;
        struct sctp_endpoint *ep;
        int retval = -EINVAL;
@@ -4507,11 +4347,6 @@ static int sctp_setsockopt_asconf_supported(struct sock 
*sk,
        if (optlen != sizeof(params))
                goto out;
 
-       if (copy_from_user(&params, optval, optlen)) {
-               retval = -EFAULT;
-               goto out;
-       }
-
        asoc = sctp_id2assoc(sk, params.assoc_id);
        if (!asoc && params.assoc_id != SCTP_FUTURE_ASSOC &&
            sctp_style(sk, UDP))
@@ -4532,10 +4367,9 @@ static int sctp_setsockopt_asconf_supported(struct sock 
*sk,
 }
 
 static int sctp_setsockopt_auth_supported(struct sock *sk,
-                                         char __user *optval,
+                                         struct sctp_assoc_value params,
                                          unsigned int optlen)
 {
-       struct sctp_assoc_value params;
        struct sctp_association *asoc;
        struct sctp_endpoint *ep;
        int retval = -EINVAL;
@@ -4543,11 +4377,6 @@ static int sctp_setsockopt_auth_supported(struct sock 
*sk,
        if (optlen != sizeof(params))
                goto out;
 
-       if (copy_from_user(&params, optval, optlen)) {
-               retval = -EFAULT;
-               goto out;
-       }
-
        asoc = sctp_id2assoc(sk, params.assoc_id);
        if (!asoc && params.assoc_id != SCTP_FUTURE_ASSOC &&
            sctp_style(sk, UDP))
@@ -4572,21 +4401,15 @@ static int sctp_setsockopt_auth_supported(struct sock 
*sk,
 }
 
 static int sctp_setsockopt_ecn_supported(struct sock *sk,
-                                        char __user *optval,
+                                        struct sctp_assoc_value params,
                                         unsigned int optlen)
 {
-       struct sctp_assoc_value params;
        struct sctp_association *asoc;
        int retval = -EINVAL;
 
        if (optlen != sizeof(params))
                goto out;
 
-       if (copy_from_user(&params, optval, optlen)) {
-               retval = -EFAULT;
-               goto out;
-       }
-
        asoc = sctp_id2assoc(sk, params.assoc_id);
        if (!asoc && params.assoc_id != SCTP_FUTURE_ASSOC &&
            sctp_style(sk, UDP))
@@ -4600,21 +4423,15 @@ static int sctp_setsockopt_ecn_supported(struct sock 
*sk,
 }
 
 static int sctp_setsockopt_pf_expose(struct sock *sk,
-                                    char __user *optval,
+                                    struct sctp_assoc_value params,
                                     unsigned int optlen)
 {
-       struct sctp_assoc_value params;
        struct sctp_association *asoc;
        int retval = -EINVAL;
 
        if (optlen != sizeof(params))
                goto out;
 
-       if (copy_from_user(&params, optval, optlen)) {
-               retval = -EFAULT;
-               goto out;
-       }
-
        if (params.assoc_value > SCTP_PF_EXPOSE_MAX)
                goto out;
 
@@ -4632,72 +4449,36 @@ static int sctp_setsockopt_pf_expose(struct sock *sk,
 out:
        return retval;
 }
+#undef params
 
-/* API 6.2 setsockopt(), getsockopt()
- *
- * Applications use setsockopt() and getsockopt() to set or retrieve
- * socket options.  Socket options are used to change the default
- * behavior of sockets calls.  They are described in Section 7.
- *
- * The syntax is:
- *
- *   ret = getsockopt(int sd, int level, int optname, void __user *optval,
- *                    int __user *optlen);
- *   ret = setsockopt(int sd, int level, int optname, const void __user 
*optval,
- *                    int optlen);
- *
- *   sd      - the socket descript.
- *   level   - set to IPPROTO_SCTP for all SCTP options.
- *   optname - the option name.
- *   optval  - the buffer to store the value of the option.
- *   optlen  - the size of the buffer.
- */
-static int sctp_setsockopt(struct sock *sk, int level, int optname,
-                          char __user *optval, unsigned int optlen)
+static int kernel_sctp_setsockopt(struct sock *sk, int optname, void *optval,
+                          int optlen)
 {
        int retval = 0;
 
-       pr_debug("%s: sk:%p, optname:%d\n", __func__, sk, optname);
-
-       /* I can hardly begin to describe how wrong this is.  This is
-        * so broken as to be worse than useless.  The API draft
-        * REALLY is NOT helpful here...  I am not convinced that the
-        * semantics of setsockopt() with a level OTHER THAN SOL_SCTP
-        * are at all well-founded.
-        */
-       if (level != SOL_SCTP) {
-               struct sctp_af *af = sctp_sk(sk)->pf->af;
-               retval = af->setsockopt(sk, level, optname, optval, optlen);
-               goto out_nounlock;
-       }
-
        lock_sock(sk);
 
        switch (optname) {
        case SCTP_SOCKOPT_BINDX_ADD:
                /* 'optlen' is the size of the addresses buffer. */
-               retval = sctp_setsockopt_bindx(sk, (struct sockaddr __user 
*)optval,
-                                              optlen, SCTP_BINDX_ADD_ADDR);
+               retval = sctp_setsockopt_bindx(sk, optval, optlen,
+                                              SCTP_BINDX_ADD_ADDR);
                break;
 
        case SCTP_SOCKOPT_BINDX_REM:
                /* 'optlen' is the size of the addresses buffer. */
-               retval = sctp_setsockopt_bindx(sk, (struct sockaddr __user 
*)optval,
-                                              optlen, SCTP_BINDX_REM_ADDR);
+               retval = sctp_setsockopt_bindx(sk, optval, optlen,
+                                              SCTP_BINDX_REM_ADDR);
                break;
 
        case SCTP_SOCKOPT_CONNECTX_OLD:
                /* 'optlen' is the size of the addresses buffer. */
-               retval = sctp_setsockopt_connectx_old(sk,
-                                           (struct sockaddr __user *)optval,
-                                           optlen);
+               retval = sctp_setsockopt_connectx_old(sk, optval, optlen);
                break;
 
        case SCTP_SOCKOPT_CONNECTX:
                /* 'optlen' is the size of the addresses buffer. */
-               retval = sctp_setsockopt_connectx(sk,
-                                           (struct sockaddr __user *)optval,
-                                           optlen);
+               retval = sctp_setsockopt_connectx(sk, optval, optlen);
                break;
 
        case SCTP_DISABLE_FRAGMENTS:
@@ -4857,7 +4638,65 @@ static int sctp_setsockopt(struct sock *sk, int level, 
int optname,
 
        release_sock(sk);
 
-out_nounlock:
+       return retval;
+}
+
+/* API 6.2 setsockopt(), getsockopt()
+ *
+ * Applications use setsockopt() and getsockopt() to set or retrieve
+ * socket options.  Socket options are used to change the default
+ * behavior of sockets calls.  They are described in Section 7.
+ *
+ * The syntax is:
+ *
+ *   ret = getsockopt(int sd, int level, int optname, void __user *optval,
+ *                    int __user *optlen);
+ *   ret = setsockopt(int sd, int level, int optname, const void __user 
*optval,
+ *                    int optlen);
+ *
+ *   sd      - the socket descript.
+ *   level   - set to IPPROTO_SCTP for all SCTP options.
+ *   optname - the option name.
+ *   optval  - the buffer to store the value of the option.
+ *   optlen  - the size of the buffer.
+ */
+static int sctp_setsockopt(struct sock *sk, int level, int optname,
+                          char __user *u_optval, unsigned int optlen)
+{
+       u64 param_buf[8];
+       int retval = 0;
+       void *optval;
+
+       pr_debug("%s: sk:%p, optname:%d\n", __func__, sk, optname);
+
+       /* I can hardly begin to describe how wrong this is.  This is
+        * so broken as to be worse than useless.  The API draft
+        * REALLY is NOT helpful here...  I am not convinced that the
+        * semantics of setsockopt() with a level OTHER THAN SOL_SCTP
+        * are at all well-founded.
+        */
+       if (level != SOL_SCTP) {
+               struct sctp_af *af = sctp_sk(sk)->pf->af;
+               return af->setsockopt(sk, level, optname, u_optval, optlen);
+       }
+
+       if (optlen < sizeof (param_buf)) {
+               if (copy_from_user(&param_buf, u_optval, optlen))
+                       return -EFAULT;
+               optval = param_buf;
+       } else {
+               /* Sanity bound the length */
+               if (optlen > 0x40000)
+                       optlen = 0x40000;
+               optval = memdup_user(u_optval, optlen);
+               if (IS_ERR(optval))
+                       return PTR_ERR(optval);
+       }
+
+       retval = kernel_sctp_setsockopt(sk, optname, optval, optlen);
+       if (optval != param_buf)
+               kfree(optval);
+
        return retval;
 }
 
-- 
1.8.1.2

-
Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, 
UK
Registration No: 1397386 (Wales)

Reply via email to