summaryrefslogtreecommitdiff
path: root/net/socket.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/socket.c')
-rw-r--r--net/socket.c130
1 files changed, 76 insertions, 54 deletions
diff --git a/net/socket.c b/net/socket.c
index 879933aaed4c..c226aceee65b 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -221,13 +221,12 @@ static int move_addr_to_user(struct sockaddr_storage *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)
+ if (len < 0 || len > sizeof(struct sockaddr_storage))
return -EINVAL;
if (len) {
if (audit_sockaddr(klen, kaddr))
@@ -1445,61 +1444,48 @@ SYSCALL_DEFINE4(socketpair, int, family, int, type, int, protocol,
err = fd1;
goto out_release_both;
}
-
fd2 = get_unused_fd_flags(flags);
if (unlikely(fd2 < 0)) {
err = fd2;
- goto out_put_unused_1;
+ put_unused_fd(fd1);
+ goto out_release_both;
}
newfile1 = sock_alloc_file(sock1, flags, NULL);
if (unlikely(IS_ERR(newfile1))) {
err = PTR_ERR(newfile1);
- goto out_put_unused_both;
+ put_unused_fd(fd1);
+ put_unused_fd(fd2);
+ goto out_release_both;
}
newfile2 = sock_alloc_file(sock2, flags, NULL);
if (IS_ERR(newfile2)) {
err = PTR_ERR(newfile2);
- goto out_fput_1;
+ fput(newfile1);
+ put_unused_fd(fd1);
+ put_unused_fd(fd2);
+ sock_release(sock2);
+ goto out;
}
- err = put_user(fd1, &usockvec[0]);
- if (err)
- goto out_fput_both;
-
- err = put_user(fd2, &usockvec[1]);
- if (err)
- goto out_fput_both;
-
audit_fd_pair(fd1, fd2);
-
fd_install(fd1, newfile1);
fd_install(fd2, newfile2);
/* fd1 and fd2 may be already another descriptors.
* Not kernel problem.
*/
- return 0;
-
-out_fput_both:
- fput(newfile2);
- fput(newfile1);
- put_unused_fd(fd2);
- put_unused_fd(fd1);
- goto out;
+ err = put_user(fd1, &usockvec[0]);
+ if (!err)
+ err = put_user(fd2, &usockvec[1]);
+ if (!err)
+ return 0;
-out_fput_1:
- fput(newfile1);
- put_unused_fd(fd2);
- put_unused_fd(fd1);
- sock_release(sock2);
- goto out;
+ sys_close(fd2);
+ sys_close(fd1);
+ return err;
-out_put_unused_both:
- put_unused_fd(fd2);
-out_put_unused_1:
- put_unused_fd(fd1);
out_release_both:
sock_release(sock2);
out_release_1:
@@ -1854,10 +1840,8 @@ SYSCALL_DEFINE6(recvfrom, int, fd, void __user *, ubuf, size_t, size,
msg.msg_iov = &iov;
iov.iov_len = size;
iov.iov_base = ubuf;
- /* 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;
+ msg.msg_name = (struct sockaddr *)&address;
+ msg.msg_namelen = sizeof(address);
if (sock->file->f_flags & O_NONBLOCK)
flags |= MSG_DONTWAIT;
err = sock_recvmsg(sock, &msg, size, flags);
@@ -1986,7 +1970,7 @@ 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))
- kmsg->msg_namelen = sizeof(struct sockaddr_storage);
+ return -EINVAL;
return 0;
}
@@ -2237,14 +2221,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, &addr, VERIFY_WRITE);
- else
+ } else
err = verify_iovec(msg_sys, iov, &addr, VERIFY_WRITE);
if (err < 0)
goto out_freeiov;
@@ -2253,9 +2239,6 @@ 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,
@@ -2981,8 +2964,11 @@ static int bond_ioctl(struct net *net, unsigned int cmd,
struct compat_ifreq __user *ifr32)
{
struct ifreq kifr;
+ struct ifreq __user *uifr;
mm_segment_t old_fs;
int err;
+ u32 data;
+ void __user *datap;
switch (cmd) {
case SIOCBONDENSLAVE:
@@ -2999,13 +2985,26 @@ static int bond_ioctl(struct net *net, unsigned int cmd,
set_fs(old_fs);
return err;
+ case SIOCBONDSLAVEINFOQUERY:
+ case SIOCBONDINFOQUERY:
+ uifr = compat_alloc_user_space(sizeof(*uifr));
+ if (copy_in_user(&uifr->ifr_name, &ifr32->ifr_name, IFNAMSIZ))
+ return -EFAULT;
+
+ if (get_user(data, &ifr32->ifr_ifru.ifru_data))
+ return -EFAULT;
+
+ datap = compat_ptr(data);
+ if (put_user(datap, &uifr->ifr_ifru.ifru_data))
+ return -EFAULT;
+
+ return dev_ioctl(net, cmd, uifr);
default:
return -ENOIOCTLCMD;
}
}
-/* Handle ioctls that use ifreq::ifr_data and just need struct ifreq converted */
-static int compat_ifr_data_ioctl(struct net *net, unsigned int cmd,
+static int siocdevprivate_ioctl(struct net *net, unsigned int cmd,
struct compat_ifreq __user *u_ifreq32)
{
struct ifreq __user *u_ifreq64;
@@ -3016,16 +3015,19 @@ static int compat_ifr_data_ioctl(struct net *net, unsigned int cmd,
if (copy_from_user(&tmp_buf[0], &(u_ifreq32->ifr_ifrn.ifrn_name[0]),
IFNAMSIZ))
return -EFAULT;
- if (get_user(data32, &u_ifreq32->ifr_ifru.ifru_data))
+ if (__get_user(data32, &u_ifreq32->ifr_ifru.ifru_data))
return -EFAULT;
data64 = compat_ptr(data32);
u_ifreq64 = compat_alloc_user_space(sizeof(*u_ifreq64));
+ /* Don't check these user accesses, just let that get trapped
+ * in the ioctl handler instead.
+ */
if (copy_to_user(&u_ifreq64->ifr_ifrn.ifrn_name[0], &tmp_buf[0],
IFNAMSIZ))
return -EFAULT;
- if (put_user(data64, &u_ifreq64->ifr_ifru.ifru_data))
+ if (__put_user(data64, &u_ifreq64->ifr_ifru.ifru_data))
return -EFAULT;
return dev_ioctl(net, cmd, u_ifreq64);
@@ -3105,6 +3107,27 @@ static int compat_sioc_ifmap(struct net *net, unsigned int cmd,
return err;
}
+static int compat_siocshwtstamp(struct net *net, struct compat_ifreq __user *uifr32)
+{
+ void __user *uptr;
+ compat_uptr_t uptr32;
+ struct ifreq __user *uifr;
+
+ uifr = compat_alloc_user_space(sizeof(*uifr));
+ if (copy_in_user(uifr, uifr32, sizeof(struct compat_ifreq)))
+ return -EFAULT;
+
+ if (get_user(uptr32, &uifr32->ifr_data))
+ return -EFAULT;
+
+ uptr = compat_ptr(uptr32);
+
+ if (put_user(uptr, &uifr->ifr_data))
+ return -EFAULT;
+
+ return dev_ioctl(net, SIOCSHWTSTAMP, uifr);
+}
+
struct rtentry32 {
u32 rt_pad1;
struct sockaddr rt_dst; /* target address */
@@ -3216,7 +3239,7 @@ static int compat_sock_ioctl_trans(struct file *file, struct socket *sock,
struct net *net = sock_net(sk);
if (cmd >= SIOCDEVPRIVATE && cmd <= (SIOCDEVPRIVATE + 15))
- return compat_ifr_data_ioctl(net, cmd, argp);
+ return siocdevprivate_ioctl(net, cmd, argp);
switch (cmd) {
case SIOCSIFBR:
@@ -3236,6 +3259,8 @@ static int compat_sock_ioctl_trans(struct file *file, struct socket *sock,
case SIOCBONDENSLAVE:
case SIOCBONDRELEASE:
case SIOCBONDSETHWADDR:
+ case SIOCBONDSLAVEINFOQUERY:
+ case SIOCBONDINFOQUERY:
case SIOCBONDCHANGEACTIVE:
return bond_ioctl(net, cmd, argp);
case SIOCADDRT:
@@ -3245,11 +3270,8 @@ static int compat_sock_ioctl_trans(struct file *file, struct socket *sock,
return do_siocgstamp(net, sock, cmd, argp);
case SIOCGSTAMPNS:
return do_siocgstampns(net, sock, cmd, argp);
- case SIOCBONDSLAVEINFOQUERY:
- case SIOCBONDINFOQUERY:
case SIOCSHWTSTAMP:
- case SIOCGHWTSTAMP:
- return compat_ifr_data_ioctl(net, cmd, argp);
+ return compat_siocshwtstamp(net, argp);
case FIOSETOWN:
case SIOCSPGRP: