* invalid addresses -EFAULT is returned. On a success 0 is returned.
*/
-int move_addr_to_kernel(void __user *uaddr, int ulen, struct sockaddr *kaddr)
+int move_addr_to_kernel(void __user *uaddr, int ulen, struct sockaddr_storage *kaddr)
{
if (ulen < 0 || ulen > sizeof(struct sockaddr_storage))
return -EINVAL;
* specified. Zero is returned for a success.
*/
-static int move_addr_to_user(struct sockaddr *kaddr, int klen,
+static int move_addr_to_user(struct sockaddr_storage *kaddr, int klen,
void __user *uaddr, int __user *ulen)
{
int err;
return &ei->vfs_inode;
}
-
-
-static void wq_free_rcu(struct rcu_head *head)
-{
- struct socket_wq *wq = container_of(head, struct socket_wq, rcu);
-
- kfree(wq);
-}
-
static void sock_destroy_inode(struct inode *inode)
{
struct socket_alloc *ei;
ei = container_of(inode, struct socket_alloc, vfs_inode);
wq = rcu_dereference_protected(ei->socket.wq, 1);
- call_rcu(&wq->rcu, wq_free_rcu);
+ kfree_rcu(wq, rcu);
kmem_cache_free(sock_inode_cachep, ei);
}
struct inode *inode;
struct socket *sock;
- inode = new_inode(sock_mnt->mnt_sb);
+ inode = new_inode_pseudo(sock_mnt->mnt_sb);
if (!inode)
return NULL;
*tx_flags |= SKBTX_HW_TSTAMP;
if (sock_flag(sk, SOCK_TIMESTAMPING_TX_SOFTWARE))
*tx_flags |= SKBTX_SW_TSTAMP;
+ if (sock_flag(sk, SOCK_WIFI_STATUS))
+ *tx_flags |= SKBTX_WIFI_STATUS;
return 0;
}
EXPORT_SYMBOL(sock_tx_timestamp);
-static inline int __sock_sendmsg(struct kiocb *iocb, struct socket *sock,
- struct msghdr *msg, size_t size)
+static inline int __sock_sendmsg_nosec(struct kiocb *iocb, struct socket *sock,
+ struct msghdr *msg, size_t size)
{
struct sock_iocb *si = kiocb_to_siocb(iocb);
- int err;
sock_update_classid(sock->sk);
+ sock_update_netprioidx(sock->sk);
+
si->sock = sock;
si->scm = NULL;
si->msg = msg;
si->size = size;
- err = security_socket_sendmsg(sock, msg, size);
- if (err)
- return err;
-
return sock->ops->sendmsg(iocb, sock, msg, size);
}
+static inline int __sock_sendmsg(struct kiocb *iocb, struct socket *sock,
+ struct msghdr *msg, size_t size)
+{
+ int err = security_socket_sendmsg(sock, msg, size);
+
+ return err ?: __sock_sendmsg_nosec(iocb, sock, msg, size);
+}
+
int sock_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
{
struct kiocb iocb;
}
EXPORT_SYMBOL(sock_sendmsg);
+static int sock_sendmsg_nosec(struct socket *sock, struct msghdr *msg, size_t size)
+{
+ struct kiocb iocb;
+ struct sock_iocb siocb;
+ int ret;
+
+ init_sync_kiocb(&iocb, NULL);
+ iocb.private = &siocb;
+ ret = __sock_sendmsg_nosec(&iocb, sock, msg, size);
+ if (-EIOCBQUEUED == ret)
+ ret = wait_on_sync_kiocb(&iocb);
+ return ret;
+}
+
int kernel_sendmsg(struct socket *sock, struct msghdr *msg,
struct kvec *vec, size_t num, size_t size)
{
}
EXPORT_SYMBOL_GPL(__sock_recv_timestamp);
+void __sock_recv_wifi_status(struct msghdr *msg, struct sock *sk,
+ struct sk_buff *skb)
+{
+ int ack;
+
+ if (!sock_flag(sk, SOCK_WIFI_STATUS))
+ return;
+ if (!skb->wifi_acked_valid)
+ return;
+
+ ack = skb->wifi_acked;
+
+ put_cmsg(msg, SOL_SOCKET, SCM_WIFI_STATUS, sizeof(ack), &ack);
+}
+EXPORT_SYMBOL_GPL(__sock_recv_wifi_status);
+
static inline void sock_recv_drops(struct msghdr *msg, struct sock *sk,
struct sk_buff *skb)
{
sock = file->private_data;
- flags = !(file->f_flags & O_NONBLOCK) ? 0 : MSG_DONTWAIT;
- if (more)
- flags |= MSG_MORE;
+ flags = (file->f_flags & O_NONBLOCK) ? MSG_DONTWAIT : 0;
+ /* more is a combination of MSG_MORE and MSG_SENDPAGE_NOTLAST */
+ flags |= more;
return kernel_sendpage(sock, page, offset, size, flags);
}
sock = sockfd_lookup_light(fd, &err, &fput_needed);
if (sock) {
- err = move_addr_to_kernel(umyaddr, addrlen, (struct sockaddr *)&address);
+ err = move_addr_to_kernel(umyaddr, addrlen, &address);
if (err >= 0) {
err = security_socket_bind(sock,
(struct sockaddr *)&address,
err = -ECONNABORTED;
goto out_fd;
}
- err = move_addr_to_user((struct sockaddr *)&address,
+ err = move_addr_to_user(&address,
len, upeer_sockaddr, upeer_addrlen);
if (err < 0)
goto out_fd;
sock = sockfd_lookup_light(fd, &err, &fput_needed);
if (!sock)
goto out;
- err = move_addr_to_kernel(uservaddr, addrlen, (struct sockaddr *)&address);
+ err = move_addr_to_kernel(uservaddr, addrlen, &address);
if (err < 0)
goto out_put;
err = sock->ops->getname(sock, (struct sockaddr *)&address, &len, 0);
if (err)
goto out_put;
- err = move_addr_to_user((struct sockaddr *)&address, len, usockaddr, usockaddr_len);
+ err = move_addr_to_user(&address, len, usockaddr, usockaddr_len);
out_put:
fput_light(sock->file, fput_needed);
sock->ops->getname(sock, (struct sockaddr *)&address, &len,
1);
if (!err)
- err = move_addr_to_user((struct sockaddr *)&address, len, usockaddr,
+ err = move_addr_to_user(&address, len, usockaddr,
usockaddr_len);
fput_light(sock->file, fput_needed);
}
msg.msg_controllen = 0;
msg.msg_namelen = 0;
if (addr) {
- err = move_addr_to_kernel(addr, addr_len, (struct sockaddr *)&address);
+ err = move_addr_to_kernel(addr, addr_len, &address);
if (err < 0)
goto out_put;
msg.msg_name = (struct sockaddr *)&address;
err = sock_recvmsg(sock, &msg, size, flags);
if (err >= 0 && addr != NULL) {
- err2 = move_addr_to_user((struct sockaddr *)&address,
+ err2 = move_addr_to_user(&address,
msg.msg_namelen, addr, addr_len);
if (err2 < 0)
err = err2;
#define COMPAT_NAMELEN(msg) COMPAT_MSG(msg, msg_namelen)
#define COMPAT_FLAGS(msg) COMPAT_MSG(msg, msg_flags)
-/*
- * BSD sendmsg interface
- */
+struct used_address {
+ struct sockaddr_storage name;
+ unsigned int name_len;
+};
-SYSCALL_DEFINE3(sendmsg, int, fd, struct msghdr __user *, msg, unsigned, flags)
+static int __sys_sendmsg(struct socket *sock, struct msghdr __user *msg,
+ struct msghdr *msg_sys, unsigned flags,
+ struct used_address *used_address)
{
struct compat_msghdr __user *msg_compat =
(struct compat_msghdr __user *)msg;
- struct socket *sock;
struct sockaddr_storage address;
struct iovec iovstack[UIO_FASTIOV], *iov = iovstack;
unsigned char ctl[sizeof(struct cmsghdr) + 20]
__attribute__ ((aligned(sizeof(__kernel_size_t))));
/* 20 is size of ipv6_pktinfo */
unsigned char *ctl_buf = ctl;
- struct msghdr msg_sys;
int err, ctl_len, iov_size, total_len;
- int fput_needed;
err = -EFAULT;
if (MSG_CMSG_COMPAT & flags) {
- if (get_compat_msghdr(&msg_sys, msg_compat))
+ if (get_compat_msghdr(msg_sys, msg_compat))
return -EFAULT;
- } else if (copy_from_user(&msg_sys, msg, sizeof(struct msghdr)))
+ } else if (copy_from_user(msg_sys, msg, sizeof(struct msghdr)))
return -EFAULT;
- sock = sockfd_lookup_light(fd, &err, &fput_needed);
- if (!sock)
- goto out;
-
/* do not move before msg_sys is valid */
err = -EMSGSIZE;
- if (msg_sys.msg_iovlen > UIO_MAXIOV)
- goto out_put;
+ if (msg_sys->msg_iovlen > UIO_MAXIOV)
+ goto out;
/* Check whether to allocate the iovec area */
err = -ENOMEM;
- iov_size = msg_sys.msg_iovlen * sizeof(struct iovec);
- if (msg_sys.msg_iovlen > UIO_FASTIOV) {
+ iov_size = msg_sys->msg_iovlen * sizeof(struct iovec);
+ if (msg_sys->msg_iovlen > UIO_FASTIOV) {
iov = sock_kmalloc(sock->sk, iov_size, GFP_KERNEL);
if (!iov)
- goto out_put;
+ goto out;
}
/* This will also move the address data into kernel space */
if (MSG_CMSG_COMPAT & flags) {
- err = verify_compat_iovec(&msg_sys, iov,
- (struct sockaddr *)&address,
- VERIFY_READ);
+ err = verify_compat_iovec(msg_sys, iov, &address, VERIFY_READ);
} else
- err = verify_iovec(&msg_sys, iov,
- (struct sockaddr *)&address,
- VERIFY_READ);
+ err = verify_iovec(msg_sys, iov, &address, VERIFY_READ);
if (err < 0)
goto out_freeiov;
total_len = err;
err = -ENOBUFS;
- if (msg_sys.msg_controllen > INT_MAX)
+ if (msg_sys->msg_controllen > INT_MAX)
goto out_freeiov;
- ctl_len = msg_sys.msg_controllen;
+ ctl_len = msg_sys->msg_controllen;
if ((MSG_CMSG_COMPAT & flags) && ctl_len) {
err =
- cmsghdr_from_user_compat_to_kern(&msg_sys, sock->sk, ctl,
+ cmsghdr_from_user_compat_to_kern(msg_sys, sock->sk, ctl,
sizeof(ctl));
if (err)
goto out_freeiov;
- ctl_buf = msg_sys.msg_control;
- ctl_len = msg_sys.msg_controllen;
+ ctl_buf = msg_sys->msg_control;
+ ctl_len = msg_sys->msg_controllen;
} else if (ctl_len) {
if (ctl_len > sizeof(ctl)) {
ctl_buf = sock_kmalloc(sock->sk, ctl_len, GFP_KERNEL);
}
err = -EFAULT;
/*
- * Careful! Before this, msg_sys.msg_control contains a user pointer.
+ * Careful! Before this, msg_sys->msg_control contains a user pointer.
* Afterwards, it will be a kernel pointer. Thus the compiler-assisted
* checking falls down on this.
*/
if (copy_from_user(ctl_buf,
- (void __user __force *)msg_sys.msg_control,
+ (void __user __force *)msg_sys->msg_control,
ctl_len))
goto out_freectl;
- msg_sys.msg_control = ctl_buf;
+ msg_sys->msg_control = ctl_buf;
}
- msg_sys.msg_flags = flags;
+ msg_sys->msg_flags = flags;
if (sock->file->f_flags & O_NONBLOCK)
- msg_sys.msg_flags |= MSG_DONTWAIT;
- err = sock_sendmsg(sock, &msg_sys, total_len);
+ msg_sys->msg_flags |= MSG_DONTWAIT;
+ /*
+ * If this is sendmmsg() and current destination address is same as
+ * previously succeeded address, omit asking LSM's decision.
+ * used_address->name_len is initialized to UINT_MAX so that the first
+ * destination address never matches.
+ */
+ if (used_address && msg_sys->msg_name &&
+ used_address->name_len == msg_sys->msg_namelen &&
+ !memcmp(&used_address->name, msg_sys->msg_name,
+ used_address->name_len)) {
+ err = sock_sendmsg_nosec(sock, msg_sys, total_len);
+ goto out_freectl;
+ }
+ err = sock_sendmsg(sock, msg_sys, total_len);
+ /*
+ * If this is sendmmsg() and sending to current destination address was
+ * successful, remember it.
+ */
+ if (used_address && err >= 0) {
+ used_address->name_len = msg_sys->msg_namelen;
+ if (msg_sys->msg_name)
+ memcpy(&used_address->name, msg_sys->msg_name,
+ used_address->name_len);
+ }
out_freectl:
if (ctl_buf != ctl)
out_freeiov:
if (iov != iovstack)
sock_kfree_s(sock->sk, iov, iov_size);
-out_put:
+out:
+ return err;
+}
+
+/*
+ * BSD sendmsg interface
+ */
+
+SYSCALL_DEFINE3(sendmsg, int, fd, struct msghdr __user *, msg, unsigned, flags)
+{
+ int fput_needed, err;
+ struct msghdr msg_sys;
+ struct socket *sock = sockfd_lookup_light(fd, &err, &fput_needed);
+
+ if (!sock)
+ goto out;
+
+ err = __sys_sendmsg(sock, msg, &msg_sys, flags, NULL);
+
fput_light(sock->file, fput_needed);
out:
return err;
}
+/*
+ * Linux sendmmsg interface
+ */
+
+int __sys_sendmmsg(int fd, struct mmsghdr __user *mmsg, unsigned int vlen,
+ unsigned int flags)
+{
+ int fput_needed, err, datagrams;
+ struct socket *sock;
+ struct mmsghdr __user *entry;
+ struct compat_mmsghdr __user *compat_entry;
+ struct msghdr msg_sys;
+ struct used_address used_address;
+
+ if (vlen > UIO_MAXIOV)
+ vlen = UIO_MAXIOV;
+
+ datagrams = 0;
+
+ sock = sockfd_lookup_light(fd, &err, &fput_needed);
+ if (!sock)
+ return err;
+
+ used_address.name_len = UINT_MAX;
+ entry = mmsg;
+ compat_entry = (struct compat_mmsghdr __user *)mmsg;
+ err = 0;
+
+ while (datagrams < vlen) {
+ if (MSG_CMSG_COMPAT & flags) {
+ err = __sys_sendmsg(sock, (struct msghdr __user *)compat_entry,
+ &msg_sys, flags, &used_address);
+ if (err < 0)
+ break;
+ err = __put_user(err, &compat_entry->msg_len);
+ ++compat_entry;
+ } else {
+ err = __sys_sendmsg(sock, (struct msghdr __user *)entry,
+ &msg_sys, flags, &used_address);
+ if (err < 0)
+ break;
+ err = put_user(err, &entry->msg_len);
+ ++entry;
+ }
+
+ if (err)
+ break;
+ ++datagrams;
+ }
+
+ fput_light(sock->file, fput_needed);
+
+ /* We only return an error if no datagrams were able to be sent */
+ if (datagrams != 0)
+ return datagrams;
+
+ return err;
+}
+
+SYSCALL_DEFINE4(sendmmsg, int, fd, struct mmsghdr __user *, mmsg,
+ unsigned int, vlen, unsigned int, flags)
+{
+ return __sys_sendmmsg(fd, mmsg, vlen, flags);
+}
+
static int __sys_recvmsg(struct socket *sock, struct msghdr __user *msg,
struct msghdr *msg_sys, unsigned flags, int nosec)
{
uaddr = (__force void __user *)msg_sys->msg_name;
uaddr_len = COMPAT_NAMELEN(msg);
if (MSG_CMSG_COMPAT & flags) {
- err = verify_compat_iovec(msg_sys, iov,
- (struct sockaddr *)&addr,
- VERIFY_WRITE);
+ err = verify_compat_iovec(msg_sys, iov, &addr, VERIFY_WRITE);
} else
- err = verify_iovec(msg_sys, iov,
- (struct sockaddr *)&addr,
- VERIFY_WRITE);
+ err = verify_iovec(msg_sys, iov, &addr, VERIFY_WRITE);
if (err < 0)
goto out_freeiov;
total_len = err;
len = err;
if (uaddr != NULL) {
- err = move_addr_to_user((struct sockaddr *)&addr,
+ err = move_addr_to_user(&addr,
msg_sys->msg_namelen, uaddr,
uaddr_len);
if (err < 0)
*/
if (MSG_CMSG_COMPAT & flags) {
err = __sys_recvmsg(sock, (struct msghdr __user *)compat_entry,
- &msg_sys, flags, datagrams);
+ &msg_sys, flags & ~MSG_WAITFORONE,
+ datagrams);
if (err < 0)
break;
err = __put_user(err, &compat_entry->msg_len);
++compat_entry;
} else {
err = __sys_recvmsg(sock, (struct msghdr __user *)entry,
- &msg_sys, flags, datagrams);
+ &msg_sys, flags & ~MSG_WAITFORONE,
+ datagrams);
if (err < 0)
break;
err = put_user(err, &entry->msg_len);
#ifdef __ARCH_WANT_SYS_SOCKETCALL
/* Argument list sizes for sys_socketcall */
#define AL(x) ((x) * sizeof(unsigned long))
-static const unsigned char nargs[20] = {
+static const unsigned char nargs[21] = {
AL(0), AL(3), AL(3), AL(3), AL(2), AL(3),
AL(3), AL(3), AL(4), AL(4), AL(4), AL(6),
AL(6), AL(2), AL(5), AL(5), AL(3), AL(3),
- AL(4), AL(5)
+ AL(4), AL(5), AL(4)
};
#undef AL
int err;
unsigned int len;
- if (call < 1 || call > SYS_RECVMMSG)
+ if (call < 1 || call > SYS_SENDMMSG)
return -EINVAL;
len = nargs[call];
case SYS_SENDMSG:
err = sys_sendmsg(a0, (struct msghdr __user *)a1, a[2]);
break;
+ case SYS_SENDMMSG:
+ err = sys_sendmmsg(a0, (struct mmsghdr __user *)a1, a[2], a[3]);
+ break;
case SYS_RECVMSG:
err = sys_recvmsg(a0, (struct msghdr __user *)a1, a[2]);
break;
BUG_ON(family < 0 || family >= NPROTO);
spin_lock(&net_family_lock);
- rcu_assign_pointer(net_families[family], NULL);
+ RCU_INIT_POINTER(net_families[family], NULL);
spin_unlock(&net_family_lock);
synchronize_rcu();
#ifdef CONFIG_COMPAT
static int do_siocgstamp(struct net *net, struct socket *sock,
- unsigned int cmd, struct compat_timeval __user *up)
+ unsigned int cmd, void __user *up)
{
mm_segment_t old_fs = get_fs();
struct timeval ktv;
set_fs(KERNEL_DS);
err = sock_do_ioctl(net, sock, cmd, (unsigned long)&ktv);
set_fs(old_fs);
- if (!err) {
- err = put_user(ktv.tv_sec, &up->tv_sec);
- err |= __put_user(ktv.tv_usec, &up->tv_usec);
- }
+ if (!err)
+ err = compat_put_timeval(up, &ktv);
+
return err;
}
static int do_siocgstampns(struct net *net, struct socket *sock,
- unsigned int cmd, struct compat_timespec __user *up)
+ unsigned int cmd, void __user *up)
{
mm_segment_t old_fs = get_fs();
struct timespec kts;
set_fs(KERNEL_DS);
err = sock_do_ioctl(net, sock, cmd, (unsigned long)&kts);
set_fs(old_fs);
- if (!err) {
- err = put_user(kts.tv_sec, &up->tv_sec);
- err |= __put_user(kts.tv_nsec, &up->tv_nsec);
- }
+ if (!err)
+ err = compat_put_timespec(up, &kts);
+
return err;
}
case ETHTOOL_GRXRINGS:
case ETHTOOL_GRXCLSRLCNT:
case ETHTOOL_GRXCLSRULE:
+ case ETHTOOL_SRXCLSRLINS:
convert_out = true;
/* fall through */
case ETHTOOL_SRXCLSRLDEL:
- case ETHTOOL_SRXCLSRLINS:
buf_size += sizeof(struct ethtool_rxnfc);
convert_in = true;
break;
return dev_ioctl(net, cmd, uifr);
default:
- return -EINVAL;
+ return -ENOIOCTLCMD;
}
}
return sock_do_ioctl(net, sock, cmd, arg);
}
- /* Prevent warning from compat_sys_ioctl, these always
- * result in -EINVAL in the native case anyway. */
- switch (cmd) {
- case SIOCRTMSG:
- case SIOCGIFCOUNT:
- case SIOCSRARP:
- case SIOCGRARP:
- case SIOCDRARP:
- case SIOCSIFLINK:
- case SIOCGIFSLAVE:
- case SIOCSIFSLAVE:
- return -EINVAL;
- }
-
return -ENOIOCTLCMD;
}