Bluetooth: Fix l2cap_sock_setsockopt() with optname BT_RCVMTU
[pandora-kernel.git] / net / socket.c
index bf7adaa..bdcddce 100644 (file)
@@ -215,12 +215,13 @@ static int move_addr_to_user(struct sockaddr *kaddr, int klen,
        int err;
        int len;
 
+       BUG_ON(klen > sizeof(struct sockaddr_storage));
        err = get_user(len, ulen);
        if (err)
                return err;
        if (len > klen)
                len = klen;
-       if (len < 0 || len > sizeof(struct sockaddr_storage))
+       if (len < 0)
                return -EINVAL;
        if (len) {
                if (audit_sockaddr(klen, kaddr))
@@ -1752,8 +1753,10 @@ SYSCALL_DEFINE6(recvfrom, int, fd, void __user *, ubuf, size_t, size,
        msg.msg_iov = &iov;
        iov.iov_len = size;
        iov.iov_base = ubuf;
-       msg.msg_name = (struct sockaddr *)&address;
-       msg.msg_namelen = sizeof(address);
+       /* Save some cycles and don't copy the address if not needed */
+       msg.msg_name = addr ? (struct sockaddr *)&address : NULL;
+       /* We assume all kernel code knows the size of sockaddr_storage */
+       msg.msg_namelen = 0;
        if (sock->file->f_flags & O_NONBLOCK)
                flags |= MSG_DONTWAIT;
        err = sock_recvmsg(sock, &msg, size, flags);
@@ -1881,8 +1884,15 @@ static int copy_msghdr_from_user(struct msghdr *kmsg,
 {
        if (copy_from_user(kmsg, umsg, sizeof(struct msghdr)))
                return -EFAULT;
-       if (kmsg->msg_namelen > sizeof(struct sockaddr_storage))
+
+       if (kmsg->msg_name == NULL)
+               kmsg->msg_namelen = 0;
+
+       if (kmsg->msg_namelen < 0)
                return -EINVAL;
+
+       if (kmsg->msg_namelen > sizeof(struct sockaddr_storage))
+               kmsg->msg_namelen = sizeof(struct sockaddr_storage);
        return 0;
 }
 
@@ -1901,14 +1911,12 @@ static int ___sys_sendmsg(struct socket *sock, struct msghdr __user *msg,
        int err, ctl_len, iov_size, total_len;
 
        err = -EFAULT;
-       if (MSG_CMSG_COMPAT & flags) {
-               if (get_compat_msghdr(msg_sys, msg_compat))
-                       return -EFAULT;
-       } else {
+       if (MSG_CMSG_COMPAT & flags)
+               err = get_compat_msghdr(msg_sys, msg_compat);
+       else
                err = copy_msghdr_from_user(msg_sys, msg);
-               if (err)
-                       return err;
-       }
+       if (err)
+               return err;
 
        /* do not move before msg_sys is valid */
        err = -EMSGSIZE;
@@ -2120,14 +2128,12 @@ static int ___sys_recvmsg(struct socket *sock, struct msghdr __user *msg,
        struct sockaddr __user *uaddr;
        int __user *uaddr_len;
 
-       if (MSG_CMSG_COMPAT & flags) {
-               if (get_compat_msghdr(msg_sys, msg_compat))
-                       return -EFAULT;
-       } else {
+       if (MSG_CMSG_COMPAT & flags)
+               err = get_compat_msghdr(msg_sys, msg_compat);
+       else
                err = copy_msghdr_from_user(msg_sys, msg);
-               if (err)
-                       return err;
-       }
+       if (err)
+               return err;
 
        err = -EMSGSIZE;
        if (msg_sys->msg_iovlen > UIO_MAXIOV)
@@ -2142,18 +2148,16 @@ static int ___sys_recvmsg(struct socket *sock, struct msghdr __user *msg,
                        goto out;
        }
 
-       /*
-        *      Save the user-mode address (verify_iovec will change the
-        *      kernel msghdr to use the kernel address space)
+       /* Save the user-mode address (verify_iovec will change the
+        * kernel msghdr to use the kernel address space)
         */
-
        uaddr = (__force void __user *)msg_sys->msg_name;
        uaddr_len = COMPAT_NAMELEN(msg);
-       if (MSG_CMSG_COMPAT & flags) {
+       if (MSG_CMSG_COMPAT & flags)
                err = verify_compat_iovec(msg_sys, iov,
                                          (struct sockaddr *)&addr,
                                          VERIFY_WRITE);
-       else
+       else
                err = verify_iovec(msg_sys, iov,
                                   (struct sockaddr *)&addr,
                                   VERIFY_WRITE);
@@ -2164,6 +2168,9 @@ static int ___sys_recvmsg(struct socket *sock, struct msghdr __user *msg,
        cmsg_ptr = (unsigned long)msg_sys->msg_control;
        msg_sys->msg_flags = flags & (MSG_CMSG_CLOEXEC|MSG_CMSG_COMPAT);
 
+       /* We assume all kernel code knows the size of sockaddr_storage */
+       msg_sys->msg_namelen = 0;
+
        if (sock->file->f_flags & O_NONBLOCK)
                flags |= MSG_DONTWAIT;
        err = (nosec ? sock_recvmsg_nosec : sock_recvmsg)(sock, msg_sys,
@@ -2310,31 +2317,31 @@ int __sys_recvmmsg(int fd, struct mmsghdr __user *mmsg, unsigned int vlen,
                        break;
        }
 
-out_put:
-       fput_light(sock->file, fput_needed);
-
        if (err == 0)
-               return datagrams;
+               goto out_put;
 
-       if (datagrams != 0) {
+       if (datagrams == 0) {
+               datagrams = err;
+               goto out_put;
+       }
+
+       /*
+        * We may return less entries than requested (vlen) if the
+        * sock is non block and there aren't enough datagrams...
+        */
+       if (err != -EAGAIN) {
                /*
-                * We may return less entries than requested (vlen) if the
-                * sock is non block and there aren't enough datagrams...
+                * ... or  if recvmsg returns an error after we
+                * received some datagrams, where we record the
+                * error to return on the next call or if the
+                * app asks about it using getsockopt(SO_ERROR).
                 */
-               if (err != -EAGAIN) {
-                       /*
-                        * ... or  if recvmsg returns an error after we
-                        * received some datagrams, where we record the
-                        * error to return on the next call or if the
-                        * app asks about it using getsockopt(SO_ERROR).
-                        */
-                       sock->sk->sk_err = -err;
-               }
-
-               return datagrams;
+               sock->sk->sk_err = -err;
        }
+out_put:
+       fput_light(sock->file, fput_needed);
 
-       return err;
+       return datagrams;
 }
 
 SYSCALL_DEFINE5(recvmmsg, int, fd, struct mmsghdr __user *, mmsg,