xref: /linux/io_uring/query.c (revision c265ae75f900cea4e415230a77b5d152377627dd)
1 // SPDX-License-Identifier: GPL-2.0
2 
3 #include "linux/io_uring/query.h"
4 
5 #include "query.h"
6 #include "io_uring.h"
7 
8 #define IO_MAX_QUERY_SIZE		(sizeof(struct io_uring_query_opcode))
9 
10 static ssize_t io_query_ops(void *data)
11 {
12 	struct io_uring_query_opcode *e = data;
13 
14 	BUILD_BUG_ON(sizeof(*e) > IO_MAX_QUERY_SIZE);
15 
16 	e->nr_request_opcodes = IORING_OP_LAST;
17 	e->nr_register_opcodes = IORING_REGISTER_LAST;
18 	e->feature_flags = IORING_FEAT_FLAGS;
19 	e->ring_setup_flags = IORING_SETUP_FLAGS;
20 	e->enter_flags = IORING_ENTER_FLAGS;
21 	e->sqe_flags = SQE_VALID_FLAGS;
22 	return sizeof(*e);
23 }
24 
25 static int io_handle_query_entry(struct io_ring_ctx *ctx,
26 				 void *data, void __user *uhdr,
27 				 u64 *next_entry)
28 {
29 	struct io_uring_query_hdr hdr;
30 	size_t usize, res_size = 0;
31 	ssize_t ret = -EINVAL;
32 	void __user *udata;
33 
34 	if (copy_from_user(&hdr, uhdr, sizeof(hdr)))
35 		return -EFAULT;
36 	usize = hdr.size;
37 	hdr.size = min(hdr.size, IO_MAX_QUERY_SIZE);
38 	udata = u64_to_user_ptr(hdr.query_data);
39 
40 	if (hdr.query_op >= __IO_URING_QUERY_MAX) {
41 		ret = -EOPNOTSUPP;
42 		goto out;
43 	}
44 	if (!mem_is_zero(hdr.__resv, sizeof(hdr.__resv)) || hdr.result || !hdr.size)
45 		goto out;
46 	if (copy_from_user(data, udata, hdr.size))
47 		return -EFAULT;
48 
49 	switch (hdr.query_op) {
50 	case IO_URING_QUERY_OPCODES:
51 		ret = io_query_ops(data);
52 		break;
53 	}
54 
55 	if (ret >= 0) {
56 		if (WARN_ON_ONCE(ret > IO_MAX_QUERY_SIZE))
57 			return -EFAULT;
58 		res_size = ret;
59 		ret = 0;
60 	}
61 out:
62 	hdr.result = ret;
63 	hdr.size = min_t(size_t, usize, res_size);
64 
65 	if (copy_struct_to_user(udata, usize, data, hdr.size, NULL))
66 		return -EFAULT;
67 	if (copy_to_user(uhdr, &hdr, sizeof(hdr)))
68 		return -EFAULT;
69 	*next_entry = hdr.next_entry;
70 	return 0;
71 }
72 
73 int io_query(struct io_ring_ctx *ctx, void __user *arg, unsigned nr_args)
74 {
75 	char entry_buffer[IO_MAX_QUERY_SIZE];
76 	void __user *uhdr = arg;
77 	int ret;
78 
79 	memset(entry_buffer, 0, sizeof(entry_buffer));
80 
81 	if (nr_args)
82 		return -EINVAL;
83 
84 	while (uhdr) {
85 		u64 next_hdr;
86 
87 		ret = io_handle_query_entry(ctx, entry_buffer, uhdr, &next_hdr);
88 		if (ret)
89 			return ret;
90 		uhdr = u64_to_user_ptr(next_hdr);
91 	}
92 	return 0;
93 }
94