diff options
Diffstat (limited to 'net/socket.c')
-rw-r--r-- | net/socket.c | 130 |
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: |