1822 lines · 45.1 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0#include <linux/kernel.h>#include <linux/errno.h>#include <linux/file.h>#include <linux/slab.h>#include <linux/net.h>#include <linux/compat.h>#include <net/compat.h>#include <linux/io_uring.h> #include <uapi/linux/io_uring.h> #include "io_uring.h"#include "kbuf.h"#include "alloc_cache.h"#include "net.h"#include "notif.h"#include "rsrc.h" #if defined(CONFIG_NET)struct io_shutdown {	struct file			*file;	int				how;}; struct io_accept {	struct file			*file;	struct sockaddr __user		*addr;	int __user			*addr_len;	int				flags;	int				iou_flags;	u32				file_slot;	unsigned long			nofile;}; struct io_socket {	struct file			*file;	int				domain;	int				type;	int				protocol;	int				flags;	u32				file_slot;	unsigned long			nofile;}; struct io_connect {	struct file			*file;	struct sockaddr __user		*addr;	int				addr_len;	bool				in_progress;	bool				seen_econnaborted;}; struct io_bind {	struct file			*file;	int				addr_len;}; struct io_listen {	struct file			*file;	int				backlog;}; struct io_sr_msg {	struct file			*file;	union {		struct compat_msghdr __user	*umsg_compat;		struct user_msghdr __user	*umsg;		void __user			*buf;	};	int				len;	unsigned			done_io;	unsigned			msg_flags;	unsigned			nr_multishot_loops;	u16				flags;	/* initialised and used only by !msg send variants */	u16				buf_group;	u16				buf_index;	void __user			*msg_control;	/* used only for send zerocopy */	struct io_kiocb 		*notif;}; /* * Number of times we'll try and do receives if there's more data. If we * exceed this limit, then add us to the back of the queue and retry from * there. This helps fairness between flooding clients. */#define MULTISHOT_MAX_RETRY	32 int io_shutdown_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe){	struct io_shutdown *shutdown = io_kiocb_to_cmd(req, struct io_shutdown); 	if (unlikely(sqe->off || sqe->addr || sqe->rw_flags ||		     sqe->buf_index || sqe->splice_fd_in))		return -EINVAL; 	shutdown->how = READ_ONCE(sqe->len);	req->flags |= REQ_F_FORCE_ASYNC;	return 0;} int io_shutdown(struct io_kiocb *req, unsigned int issue_flags){	struct io_shutdown *shutdown = io_kiocb_to_cmd(req, struct io_shutdown);	struct socket *sock;	int ret; 	WARN_ON_ONCE(issue_flags & IO_URING_F_NONBLOCK); 	sock = sock_from_file(req->file);	if (unlikely(!sock))		return -ENOTSOCK; 	ret = __sys_shutdown_sock(sock, shutdown->how);	io_req_set_res(req, ret, 0);	return IOU_OK;} static bool io_net_retry(struct socket *sock, int flags){	if (!(flags & MSG_WAITALL))		return false;	return sock->type == SOCK_STREAM || sock->type == SOCK_SEQPACKET;} static void io_netmsg_iovec_free(struct io_async_msghdr *kmsg){	if (kmsg->free_iov) {		kfree(kmsg->free_iov);		kmsg->free_iov_nr = 0;		kmsg->free_iov = NULL;	}} static void io_netmsg_recycle(struct io_kiocb *req, unsigned int issue_flags){	struct io_async_msghdr *hdr = req->async_data;	struct iovec *iov; 	/* can't recycle, ensure we free the iovec if we have one */	if (unlikely(issue_flags & IO_URING_F_UNLOCKED)) {		io_netmsg_iovec_free(hdr);		return;	} 	/* Let normal cleanup path reap it if we fail adding to the cache */	iov = hdr->free_iov;	if (io_alloc_cache_put(&req->ctx->netmsg_cache, hdr)) {		if (iov)			kasan_mempool_poison_object(iov);		req->async_data = NULL;		req->flags &= ~REQ_F_ASYNC_DATA;	}} static struct io_async_msghdr *io_msg_alloc_async(struct io_kiocb *req){	struct io_ring_ctx *ctx = req->ctx;	struct io_async_msghdr *hdr; 	hdr = io_alloc_cache_get(&ctx->netmsg_cache);	if (hdr) {		if (hdr->free_iov) {			kasan_mempool_unpoison_object(hdr->free_iov,				hdr->free_iov_nr * sizeof(struct iovec));			req->flags |= REQ_F_NEED_CLEANUP;		}		req->flags |= REQ_F_ASYNC_DATA;		req->async_data = hdr;		return hdr;	} 	if (!io_alloc_async_data(req)) {		hdr = req->async_data;		hdr->free_iov_nr = 0;		hdr->free_iov = NULL;		return hdr;	}	return NULL;} /* assign new iovec to kmsg, if we need to */static int io_net_vec_assign(struct io_kiocb *req, struct io_async_msghdr *kmsg,			     struct iovec *iov){	if (iov) {		req->flags |= REQ_F_NEED_CLEANUP;		kmsg->free_iov_nr = kmsg->msg.msg_iter.nr_segs;		if (kmsg->free_iov)			kfree(kmsg->free_iov);		kmsg->free_iov = iov;	}	return 0;} static inline void io_mshot_prep_retry(struct io_kiocb *req,				       struct io_async_msghdr *kmsg){	struct io_sr_msg *sr = io_kiocb_to_cmd(req, struct io_sr_msg); 	req->flags &= ~REQ_F_BL_EMPTY;	sr->done_io = 0;	sr->len = 0; /* get from the provided buffer */	req->buf_index = sr->buf_group;} #ifdef CONFIG_COMPATstatic int io_compat_msg_copy_hdr(struct io_kiocb *req,				  struct io_async_msghdr *iomsg,				  struct compat_msghdr *msg, int ddir){	struct io_sr_msg *sr = io_kiocb_to_cmd(req, struct io_sr_msg);	struct compat_iovec __user *uiov;	struct iovec *iov;	int ret, nr_segs; 	if (iomsg->free_iov) {		nr_segs = iomsg->free_iov_nr;		iov = iomsg->free_iov;	} else {		iov = &iomsg->fast_iov;		nr_segs = 1;	} 	if (copy_from_user(msg, sr->umsg_compat, sizeof(*msg)))		return -EFAULT; 	uiov = compat_ptr(msg->msg_iov);	if (req->flags & REQ_F_BUFFER_SELECT) {		compat_ssize_t clen; 		if (msg->msg_iovlen == 0) {			sr->len = iov->iov_len = 0;			iov->iov_base = NULL;		} else if (msg->msg_iovlen > 1) {			return -EINVAL;		} else {			if (!access_ok(uiov, sizeof(*uiov)))				return -EFAULT;			if (__get_user(clen, &uiov->iov_len))				return -EFAULT;			if (clen < 0)				return -EINVAL;			sr->len = clen;		} 		return 0;	} 	ret = __import_iovec(ddir, (struct iovec __user *)uiov, msg->msg_iovlen,				nr_segs, &iov, &iomsg->msg.msg_iter, true);	if (unlikely(ret < 0))		return ret; 	return io_net_vec_assign(req, iomsg, iov);}#endif static int io_msg_copy_hdr(struct io_kiocb *req, struct io_async_msghdr *iomsg,			   struct user_msghdr *msg, int ddir){	struct io_sr_msg *sr = io_kiocb_to_cmd(req, struct io_sr_msg);	struct user_msghdr __user *umsg = sr->umsg;	struct iovec *iov;	int ret, nr_segs; 	if (iomsg->free_iov) {		nr_segs = iomsg->free_iov_nr;		iov = iomsg->free_iov;	} else {		iov = &iomsg->fast_iov;		nr_segs = 1;	} 	if (!user_access_begin(umsg, sizeof(*umsg)))		return -EFAULT; 	ret = -EFAULT;	unsafe_get_user(msg->msg_name, &umsg->msg_name, ua_end);	unsafe_get_user(msg->msg_namelen, &umsg->msg_namelen, ua_end);	unsafe_get_user(msg->msg_iov, &umsg->msg_iov, ua_end);	unsafe_get_user(msg->msg_iovlen, &umsg->msg_iovlen, ua_end);	unsafe_get_user(msg->msg_control, &umsg->msg_control, ua_end);	unsafe_get_user(msg->msg_controllen, &umsg->msg_controllen, ua_end);	msg->msg_flags = 0; 	if (req->flags & REQ_F_BUFFER_SELECT) {		if (msg->msg_iovlen == 0) {			sr->len = iov->iov_len = 0;			iov->iov_base = NULL;		} else if (msg->msg_iovlen > 1) {			ret = -EINVAL;			goto ua_end;		} else {			/* we only need the length for provided buffers */			if (!access_ok(&msg->msg_iov[0].iov_len, sizeof(__kernel_size_t)))				goto ua_end;			unsafe_get_user(iov->iov_len, &msg->msg_iov[0].iov_len,					ua_end);			sr->len = iov->iov_len;		}		ret = 0;ua_end:		user_access_end();		return ret;	} 	user_access_end();	ret = __import_iovec(ddir, msg->msg_iov, msg->msg_iovlen, nr_segs,				&iov, &iomsg->msg.msg_iter, false);	if (unlikely(ret < 0))		return ret; 	return io_net_vec_assign(req, iomsg, iov);} static int io_sendmsg_copy_hdr(struct io_kiocb *req,			       struct io_async_msghdr *iomsg){	struct io_sr_msg *sr = io_kiocb_to_cmd(req, struct io_sr_msg);	struct user_msghdr msg;	int ret; 	iomsg->msg.msg_name = &iomsg->addr;	iomsg->msg.msg_iter.nr_segs = 0; #ifdef CONFIG_COMPAT	if (unlikely(req->ctx->compat)) {		struct compat_msghdr cmsg; 		ret = io_compat_msg_copy_hdr(req, iomsg, &cmsg, ITER_SOURCE);		if (unlikely(ret))			return ret; 		return __get_compat_msghdr(&iomsg->msg, &cmsg, NULL);	}#endif 	ret = io_msg_copy_hdr(req, iomsg, &msg, ITER_SOURCE);	if (unlikely(ret))		return ret; 	ret = __copy_msghdr(&iomsg->msg, &msg, NULL); 	/* save msg_control as sys_sendmsg() overwrites it */	sr->msg_control = iomsg->msg.msg_control_user;	return ret;} void io_sendmsg_recvmsg_cleanup(struct io_kiocb *req){	struct io_async_msghdr *io = req->async_data; 	io_netmsg_iovec_free(io);} static int io_send_setup(struct io_kiocb *req, const struct io_uring_sqe *sqe){	struct io_sr_msg *sr = io_kiocb_to_cmd(req, struct io_sr_msg);	struct io_async_msghdr *kmsg = req->async_data;	void __user *addr;	u16 addr_len;	int ret; 	sr->buf = u64_to_user_ptr(READ_ONCE(sqe->addr)); 	if (READ_ONCE(sqe->__pad3[0]))		return -EINVAL; 	kmsg->msg.msg_name = NULL;	kmsg->msg.msg_namelen = 0;	kmsg->msg.msg_control = NULL;	kmsg->msg.msg_controllen = 0;	kmsg->msg.msg_ubuf = NULL; 	addr = u64_to_user_ptr(READ_ONCE(sqe->addr2));	addr_len = READ_ONCE(sqe->addr_len);	if (addr) {		ret = move_addr_to_kernel(addr, addr_len, &kmsg->addr);		if (unlikely(ret < 0))			return ret;		kmsg->msg.msg_name = &kmsg->addr;		kmsg->msg.msg_namelen = addr_len;	}	if (!io_do_buffer_select(req)) {		ret = import_ubuf(ITER_SOURCE, sr->buf, sr->len,				  &kmsg->msg.msg_iter);		if (unlikely(ret < 0))			return ret;	}	return 0;} static int io_sendmsg_setup(struct io_kiocb *req, const struct io_uring_sqe *sqe){	struct io_sr_msg *sr = io_kiocb_to_cmd(req, struct io_sr_msg);	struct io_async_msghdr *kmsg = req->async_data;	int ret; 	sr->umsg = u64_to_user_ptr(READ_ONCE(sqe->addr)); 	ret = io_sendmsg_copy_hdr(req, kmsg);	if (!ret)		req->flags |= REQ_F_NEED_CLEANUP;	return ret;} #define SENDMSG_FLAGS (IORING_RECVSEND_POLL_FIRST | IORING_RECVSEND_BUNDLE) int io_sendmsg_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe){	struct io_sr_msg *sr = io_kiocb_to_cmd(req, struct io_sr_msg); 	sr->done_io = 0; 	if (req->opcode != IORING_OP_SEND) {		if (sqe->addr2 || sqe->file_index)			return -EINVAL;	} 	sr->len = READ_ONCE(sqe->len);	sr->flags = READ_ONCE(sqe->ioprio);	if (sr->flags & ~SENDMSG_FLAGS)		return -EINVAL;	sr->msg_flags = READ_ONCE(sqe->msg_flags) | MSG_NOSIGNAL;	if (sr->msg_flags & MSG_DONTWAIT)		req->flags |= REQ_F_NOWAIT;	if (sr->flags & IORING_RECVSEND_BUNDLE) {		if (req->opcode == IORING_OP_SENDMSG)			return -EINVAL;		if (!(req->flags & REQ_F_BUFFER_SELECT))			return -EINVAL;		sr->msg_flags |= MSG_WAITALL;		sr->buf_group = req->buf_index;		req->buf_list = NULL;	} #ifdef CONFIG_COMPAT	if (req->ctx->compat)		sr->msg_flags |= MSG_CMSG_COMPAT;#endif	if (unlikely(!io_msg_alloc_async(req)))		return -ENOMEM;	if (req->opcode != IORING_OP_SENDMSG)		return io_send_setup(req, sqe);	return io_sendmsg_setup(req, sqe);} static void io_req_msg_cleanup(struct io_kiocb *req,			       unsigned int issue_flags){	req->flags &= ~REQ_F_NEED_CLEANUP;	io_netmsg_recycle(req, issue_flags);} /* * For bundle completions, we need to figure out how many segments we consumed. * A bundle could be using a single ITER_UBUF if that's all we mapped, or it * could be using an ITER_IOVEC. If the latter, then if we consumed all of * the segments, then it's a trivial questiont o answer. If we have residual * data in the iter, then loop the segments to figure out how much we * transferred. */static int io_bundle_nbufs(struct io_async_msghdr *kmsg, int ret){	struct iovec *iov;	int nbufs; 	/* no data is always zero segments, and a ubuf is always 1 segment */	if (ret <= 0)		return 0;	if (iter_is_ubuf(&kmsg->msg.msg_iter))		return 1; 	iov = kmsg->free_iov;	if (!iov)		iov = &kmsg->fast_iov; 	/* if all data was transferred, it's basic pointer math */	if (!iov_iter_count(&kmsg->msg.msg_iter))		return iter_iov(&kmsg->msg.msg_iter) - iov; 	/* short transfer, count segments */	nbufs = 0;	do {		int this_len = min_t(int, iov[nbufs].iov_len, ret); 		nbufs++;		ret -= this_len;	} while (ret); 	return nbufs;} static inline bool io_send_finish(struct io_kiocb *req, int *ret,				  struct io_async_msghdr *kmsg,				  unsigned issue_flags){

Lines 1–500 of 1822.