162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci#include <linux/kernel.h>
362306a36Sopenharmony_ci#include <linux/errno.h>
462306a36Sopenharmony_ci#include <linux/fs.h>
562306a36Sopenharmony_ci#include <linux/file.h>
662306a36Sopenharmony_ci#include <linux/mm.h>
762306a36Sopenharmony_ci#include <linux/slab.h>
862306a36Sopenharmony_ci#include <linux/namei.h>
962306a36Sopenharmony_ci#include <linux/nospec.h>
1062306a36Sopenharmony_ci#include <linux/io_uring.h>
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <uapi/linux/io_uring.h>
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include "io_uring.h"
1562306a36Sopenharmony_ci#include "tctx.h"
1662306a36Sopenharmony_ci#include "poll.h"
1762306a36Sopenharmony_ci#include "timeout.h"
1862306a36Sopenharmony_ci#include "cancel.h"
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_cistruct io_cancel {
2162306a36Sopenharmony_ci	struct file			*file;
2262306a36Sopenharmony_ci	u64				addr;
2362306a36Sopenharmony_ci	u32				flags;
2462306a36Sopenharmony_ci	s32				fd;
2562306a36Sopenharmony_ci	u8				opcode;
2662306a36Sopenharmony_ci};
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#define CANCEL_FLAGS	(IORING_ASYNC_CANCEL_ALL | IORING_ASYNC_CANCEL_FD | \
2962306a36Sopenharmony_ci			 IORING_ASYNC_CANCEL_ANY | IORING_ASYNC_CANCEL_FD_FIXED | \
3062306a36Sopenharmony_ci			 IORING_ASYNC_CANCEL_USERDATA | IORING_ASYNC_CANCEL_OP)
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci/*
3362306a36Sopenharmony_ci * Returns true if the request matches the criteria outlined by 'cd'.
3462306a36Sopenharmony_ci */
3562306a36Sopenharmony_cibool io_cancel_req_match(struct io_kiocb *req, struct io_cancel_data *cd)
3662306a36Sopenharmony_ci{
3762306a36Sopenharmony_ci	bool match_user_data = cd->flags & IORING_ASYNC_CANCEL_USERDATA;
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	if (req->ctx != cd->ctx)
4062306a36Sopenharmony_ci		return false;
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	if (!(cd->flags & (IORING_ASYNC_CANCEL_FD | IORING_ASYNC_CANCEL_OP)))
4362306a36Sopenharmony_ci		match_user_data = true;
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	if (cd->flags & IORING_ASYNC_CANCEL_ANY)
4662306a36Sopenharmony_ci		goto check_seq;
4762306a36Sopenharmony_ci	if (cd->flags & IORING_ASYNC_CANCEL_FD) {
4862306a36Sopenharmony_ci		if (req->file != cd->file)
4962306a36Sopenharmony_ci			return false;
5062306a36Sopenharmony_ci	}
5162306a36Sopenharmony_ci	if (cd->flags & IORING_ASYNC_CANCEL_OP) {
5262306a36Sopenharmony_ci		if (req->opcode != cd->opcode)
5362306a36Sopenharmony_ci			return false;
5462306a36Sopenharmony_ci	}
5562306a36Sopenharmony_ci	if (match_user_data && req->cqe.user_data != cd->data)
5662306a36Sopenharmony_ci		return false;
5762306a36Sopenharmony_ci	if (cd->flags & IORING_ASYNC_CANCEL_ALL) {
5862306a36Sopenharmony_cicheck_seq:
5962306a36Sopenharmony_ci		if (cd->seq == req->work.cancel_seq)
6062306a36Sopenharmony_ci			return false;
6162306a36Sopenharmony_ci		req->work.cancel_seq = cd->seq;
6262306a36Sopenharmony_ci	}
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	return true;
6562306a36Sopenharmony_ci}
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_cistatic bool io_cancel_cb(struct io_wq_work *work, void *data)
6862306a36Sopenharmony_ci{
6962306a36Sopenharmony_ci	struct io_kiocb *req = container_of(work, struct io_kiocb, work);
7062306a36Sopenharmony_ci	struct io_cancel_data *cd = data;
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	return io_cancel_req_match(req, cd);
7362306a36Sopenharmony_ci}
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_cistatic int io_async_cancel_one(struct io_uring_task *tctx,
7662306a36Sopenharmony_ci			       struct io_cancel_data *cd)
7762306a36Sopenharmony_ci{
7862306a36Sopenharmony_ci	enum io_wq_cancel cancel_ret;
7962306a36Sopenharmony_ci	int ret = 0;
8062306a36Sopenharmony_ci	bool all;
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	if (!tctx || !tctx->io_wq)
8362306a36Sopenharmony_ci		return -ENOENT;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	all = cd->flags & (IORING_ASYNC_CANCEL_ALL|IORING_ASYNC_CANCEL_ANY);
8662306a36Sopenharmony_ci	cancel_ret = io_wq_cancel_cb(tctx->io_wq, io_cancel_cb, cd, all);
8762306a36Sopenharmony_ci	switch (cancel_ret) {
8862306a36Sopenharmony_ci	case IO_WQ_CANCEL_OK:
8962306a36Sopenharmony_ci		ret = 0;
9062306a36Sopenharmony_ci		break;
9162306a36Sopenharmony_ci	case IO_WQ_CANCEL_RUNNING:
9262306a36Sopenharmony_ci		ret = -EALREADY;
9362306a36Sopenharmony_ci		break;
9462306a36Sopenharmony_ci	case IO_WQ_CANCEL_NOTFOUND:
9562306a36Sopenharmony_ci		ret = -ENOENT;
9662306a36Sopenharmony_ci		break;
9762306a36Sopenharmony_ci	}
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	return ret;
10062306a36Sopenharmony_ci}
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ciint io_try_cancel(struct io_uring_task *tctx, struct io_cancel_data *cd,
10362306a36Sopenharmony_ci		  unsigned issue_flags)
10462306a36Sopenharmony_ci{
10562306a36Sopenharmony_ci	struct io_ring_ctx *ctx = cd->ctx;
10662306a36Sopenharmony_ci	int ret;
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	WARN_ON_ONCE(!io_wq_current_is_worker() && tctx != current->io_uring);
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	ret = io_async_cancel_one(tctx, cd);
11162306a36Sopenharmony_ci	/*
11262306a36Sopenharmony_ci	 * Fall-through even for -EALREADY, as we may have poll armed
11362306a36Sopenharmony_ci	 * that need unarming.
11462306a36Sopenharmony_ci	 */
11562306a36Sopenharmony_ci	if (!ret)
11662306a36Sopenharmony_ci		return 0;
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	ret = io_poll_cancel(ctx, cd, issue_flags);
11962306a36Sopenharmony_ci	if (ret != -ENOENT)
12062306a36Sopenharmony_ci		return ret;
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	spin_lock(&ctx->completion_lock);
12362306a36Sopenharmony_ci	if (!(cd->flags & IORING_ASYNC_CANCEL_FD))
12462306a36Sopenharmony_ci		ret = io_timeout_cancel(ctx, cd);
12562306a36Sopenharmony_ci	spin_unlock(&ctx->completion_lock);
12662306a36Sopenharmony_ci	return ret;
12762306a36Sopenharmony_ci}
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ciint io_async_cancel_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
13062306a36Sopenharmony_ci{
13162306a36Sopenharmony_ci	struct io_cancel *cancel = io_kiocb_to_cmd(req, struct io_cancel);
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	if (unlikely(req->flags & REQ_F_BUFFER_SELECT))
13462306a36Sopenharmony_ci		return -EINVAL;
13562306a36Sopenharmony_ci	if (sqe->off || sqe->splice_fd_in)
13662306a36Sopenharmony_ci		return -EINVAL;
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	cancel->addr = READ_ONCE(sqe->addr);
13962306a36Sopenharmony_ci	cancel->flags = READ_ONCE(sqe->cancel_flags);
14062306a36Sopenharmony_ci	if (cancel->flags & ~CANCEL_FLAGS)
14162306a36Sopenharmony_ci		return -EINVAL;
14262306a36Sopenharmony_ci	if (cancel->flags & IORING_ASYNC_CANCEL_FD) {
14362306a36Sopenharmony_ci		if (cancel->flags & IORING_ASYNC_CANCEL_ANY)
14462306a36Sopenharmony_ci			return -EINVAL;
14562306a36Sopenharmony_ci		cancel->fd = READ_ONCE(sqe->fd);
14662306a36Sopenharmony_ci	}
14762306a36Sopenharmony_ci	if (cancel->flags & IORING_ASYNC_CANCEL_OP) {
14862306a36Sopenharmony_ci		if (cancel->flags & IORING_ASYNC_CANCEL_ANY)
14962306a36Sopenharmony_ci			return -EINVAL;
15062306a36Sopenharmony_ci		cancel->opcode = READ_ONCE(sqe->len);
15162306a36Sopenharmony_ci	}
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	return 0;
15462306a36Sopenharmony_ci}
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_cistatic int __io_async_cancel(struct io_cancel_data *cd,
15762306a36Sopenharmony_ci			     struct io_uring_task *tctx,
15862306a36Sopenharmony_ci			     unsigned int issue_flags)
15962306a36Sopenharmony_ci{
16062306a36Sopenharmony_ci	bool all = cd->flags & (IORING_ASYNC_CANCEL_ALL|IORING_ASYNC_CANCEL_ANY);
16162306a36Sopenharmony_ci	struct io_ring_ctx *ctx = cd->ctx;
16262306a36Sopenharmony_ci	struct io_tctx_node *node;
16362306a36Sopenharmony_ci	int ret, nr = 0;
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	do {
16662306a36Sopenharmony_ci		ret = io_try_cancel(tctx, cd, issue_flags);
16762306a36Sopenharmony_ci		if (ret == -ENOENT)
16862306a36Sopenharmony_ci			break;
16962306a36Sopenharmony_ci		if (!all)
17062306a36Sopenharmony_ci			return ret;
17162306a36Sopenharmony_ci		nr++;
17262306a36Sopenharmony_ci	} while (1);
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	/* slow path, try all io-wq's */
17562306a36Sopenharmony_ci	io_ring_submit_lock(ctx, issue_flags);
17662306a36Sopenharmony_ci	ret = -ENOENT;
17762306a36Sopenharmony_ci	list_for_each_entry(node, &ctx->tctx_list, ctx_node) {
17862306a36Sopenharmony_ci		struct io_uring_task *tctx = node->task->io_uring;
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci		ret = io_async_cancel_one(tctx, cd);
18162306a36Sopenharmony_ci		if (ret != -ENOENT) {
18262306a36Sopenharmony_ci			if (!all)
18362306a36Sopenharmony_ci				break;
18462306a36Sopenharmony_ci			nr++;
18562306a36Sopenharmony_ci		}
18662306a36Sopenharmony_ci	}
18762306a36Sopenharmony_ci	io_ring_submit_unlock(ctx, issue_flags);
18862306a36Sopenharmony_ci	return all ? nr : ret;
18962306a36Sopenharmony_ci}
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ciint io_async_cancel(struct io_kiocb *req, unsigned int issue_flags)
19262306a36Sopenharmony_ci{
19362306a36Sopenharmony_ci	struct io_cancel *cancel = io_kiocb_to_cmd(req, struct io_cancel);
19462306a36Sopenharmony_ci	struct io_cancel_data cd = {
19562306a36Sopenharmony_ci		.ctx	= req->ctx,
19662306a36Sopenharmony_ci		.data	= cancel->addr,
19762306a36Sopenharmony_ci		.flags	= cancel->flags,
19862306a36Sopenharmony_ci		.opcode	= cancel->opcode,
19962306a36Sopenharmony_ci		.seq	= atomic_inc_return(&req->ctx->cancel_seq),
20062306a36Sopenharmony_ci	};
20162306a36Sopenharmony_ci	struct io_uring_task *tctx = req->task->io_uring;
20262306a36Sopenharmony_ci	int ret;
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	if (cd.flags & IORING_ASYNC_CANCEL_FD) {
20562306a36Sopenharmony_ci		if (req->flags & REQ_F_FIXED_FILE ||
20662306a36Sopenharmony_ci		    cd.flags & IORING_ASYNC_CANCEL_FD_FIXED) {
20762306a36Sopenharmony_ci			req->flags |= REQ_F_FIXED_FILE;
20862306a36Sopenharmony_ci			req->file = io_file_get_fixed(req, cancel->fd,
20962306a36Sopenharmony_ci							issue_flags);
21062306a36Sopenharmony_ci		} else {
21162306a36Sopenharmony_ci			req->file = io_file_get_normal(req, cancel->fd);
21262306a36Sopenharmony_ci		}
21362306a36Sopenharmony_ci		if (!req->file) {
21462306a36Sopenharmony_ci			ret = -EBADF;
21562306a36Sopenharmony_ci			goto done;
21662306a36Sopenharmony_ci		}
21762306a36Sopenharmony_ci		cd.file = req->file;
21862306a36Sopenharmony_ci	}
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	ret = __io_async_cancel(&cd, tctx, issue_flags);
22162306a36Sopenharmony_cidone:
22262306a36Sopenharmony_ci	if (ret < 0)
22362306a36Sopenharmony_ci		req_set_fail(req);
22462306a36Sopenharmony_ci	io_req_set_res(req, ret, 0);
22562306a36Sopenharmony_ci	return IOU_OK;
22662306a36Sopenharmony_ci}
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_civoid init_hash_table(struct io_hash_table *table, unsigned size)
22962306a36Sopenharmony_ci{
23062306a36Sopenharmony_ci	unsigned int i;
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	for (i = 0; i < size; i++) {
23362306a36Sopenharmony_ci		spin_lock_init(&table->hbs[i].lock);
23462306a36Sopenharmony_ci		INIT_HLIST_HEAD(&table->hbs[i].list);
23562306a36Sopenharmony_ci	}
23662306a36Sopenharmony_ci}
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_cistatic int __io_sync_cancel(struct io_uring_task *tctx,
23962306a36Sopenharmony_ci			    struct io_cancel_data *cd, int fd)
24062306a36Sopenharmony_ci{
24162306a36Sopenharmony_ci	struct io_ring_ctx *ctx = cd->ctx;
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	/* fixed must be grabbed every time since we drop the uring_lock */
24462306a36Sopenharmony_ci	if ((cd->flags & IORING_ASYNC_CANCEL_FD) &&
24562306a36Sopenharmony_ci	    (cd->flags & IORING_ASYNC_CANCEL_FD_FIXED)) {
24662306a36Sopenharmony_ci		if (unlikely(fd >= ctx->nr_user_files))
24762306a36Sopenharmony_ci			return -EBADF;
24862306a36Sopenharmony_ci		fd = array_index_nospec(fd, ctx->nr_user_files);
24962306a36Sopenharmony_ci		cd->file = io_file_from_index(&ctx->file_table, fd);
25062306a36Sopenharmony_ci		if (!cd->file)
25162306a36Sopenharmony_ci			return -EBADF;
25262306a36Sopenharmony_ci	}
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	return __io_async_cancel(cd, tctx, 0);
25562306a36Sopenharmony_ci}
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ciint io_sync_cancel(struct io_ring_ctx *ctx, void __user *arg)
25862306a36Sopenharmony_ci	__must_hold(&ctx->uring_lock)
25962306a36Sopenharmony_ci{
26062306a36Sopenharmony_ci	struct io_cancel_data cd = {
26162306a36Sopenharmony_ci		.ctx	= ctx,
26262306a36Sopenharmony_ci		.seq	= atomic_inc_return(&ctx->cancel_seq),
26362306a36Sopenharmony_ci	};
26462306a36Sopenharmony_ci	ktime_t timeout = KTIME_MAX;
26562306a36Sopenharmony_ci	struct io_uring_sync_cancel_reg sc;
26662306a36Sopenharmony_ci	struct file *file = NULL;
26762306a36Sopenharmony_ci	DEFINE_WAIT(wait);
26862306a36Sopenharmony_ci	int ret, i;
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	if (copy_from_user(&sc, arg, sizeof(sc)))
27162306a36Sopenharmony_ci		return -EFAULT;
27262306a36Sopenharmony_ci	if (sc.flags & ~CANCEL_FLAGS)
27362306a36Sopenharmony_ci		return -EINVAL;
27462306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(sc.pad); i++)
27562306a36Sopenharmony_ci		if (sc.pad[i])
27662306a36Sopenharmony_ci			return -EINVAL;
27762306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(sc.pad2); i++)
27862306a36Sopenharmony_ci		if (sc.pad2[i])
27962306a36Sopenharmony_ci			return -EINVAL;
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	cd.data = sc.addr;
28262306a36Sopenharmony_ci	cd.flags = sc.flags;
28362306a36Sopenharmony_ci	cd.opcode = sc.opcode;
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	/* we can grab a normal file descriptor upfront */
28662306a36Sopenharmony_ci	if ((cd.flags & IORING_ASYNC_CANCEL_FD) &&
28762306a36Sopenharmony_ci	   !(cd.flags & IORING_ASYNC_CANCEL_FD_FIXED)) {
28862306a36Sopenharmony_ci		file = fget(sc.fd);
28962306a36Sopenharmony_ci		if (!file)
29062306a36Sopenharmony_ci			return -EBADF;
29162306a36Sopenharmony_ci		cd.file = file;
29262306a36Sopenharmony_ci	}
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	ret = __io_sync_cancel(current->io_uring, &cd, sc.fd);
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	/* found something, done! */
29762306a36Sopenharmony_ci	if (ret != -EALREADY)
29862306a36Sopenharmony_ci		goto out;
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	if (sc.timeout.tv_sec != -1UL || sc.timeout.tv_nsec != -1UL) {
30162306a36Sopenharmony_ci		struct timespec64 ts = {
30262306a36Sopenharmony_ci			.tv_sec		= sc.timeout.tv_sec,
30362306a36Sopenharmony_ci			.tv_nsec	= sc.timeout.tv_nsec
30462306a36Sopenharmony_ci		};
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci		timeout = ktime_add_ns(timespec64_to_ktime(ts), ktime_get_ns());
30762306a36Sopenharmony_ci	}
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	/*
31062306a36Sopenharmony_ci	 * Keep looking until we get -ENOENT. we'll get woken everytime
31162306a36Sopenharmony_ci	 * every time a request completes and will retry the cancelation.
31262306a36Sopenharmony_ci	 */
31362306a36Sopenharmony_ci	do {
31462306a36Sopenharmony_ci		cd.seq = atomic_inc_return(&ctx->cancel_seq);
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci		prepare_to_wait(&ctx->cq_wait, &wait, TASK_INTERRUPTIBLE);
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci		ret = __io_sync_cancel(current->io_uring, &cd, sc.fd);
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci		mutex_unlock(&ctx->uring_lock);
32162306a36Sopenharmony_ci		if (ret != -EALREADY)
32262306a36Sopenharmony_ci			break;
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci		ret = io_run_task_work_sig(ctx);
32562306a36Sopenharmony_ci		if (ret < 0)
32662306a36Sopenharmony_ci			break;
32762306a36Sopenharmony_ci		ret = schedule_hrtimeout(&timeout, HRTIMER_MODE_ABS);
32862306a36Sopenharmony_ci		if (!ret) {
32962306a36Sopenharmony_ci			ret = -ETIME;
33062306a36Sopenharmony_ci			break;
33162306a36Sopenharmony_ci		}
33262306a36Sopenharmony_ci		mutex_lock(&ctx->uring_lock);
33362306a36Sopenharmony_ci	} while (1);
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	finish_wait(&ctx->cq_wait, &wait);
33662306a36Sopenharmony_ci	mutex_lock(&ctx->uring_lock);
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	if (ret == -ENOENT || ret > 0)
33962306a36Sopenharmony_ci		ret = 0;
34062306a36Sopenharmony_ciout:
34162306a36Sopenharmony_ci	if (file)
34262306a36Sopenharmony_ci		fput(file);
34362306a36Sopenharmony_ci	return ret;
34462306a36Sopenharmony_ci}
345