Skip to content

Commit d8345a2

Browse files
isilenceaxboe
authored andcommitted
io_uring/timeout: immediate timeout arg
One the things the user has always keep in mind is that any user pointers they put into an SQE is not going to be read by the kernel until submission happens, and the user has to ensure the pointee stays alive until then. For example, snippet below will lead to UAF of the on stack variable ts. Instead of passing the timeout value as a pointer allow to store it immediately in the SQE. The user has to set a new flag called IORING_TIMEOUT_IMMEDIATE_ARG, in which case sqe->addr for timeout or sqe->addr2 for timeout update requests will be interpreted as a time value in nanosecods. void prep_timeout(struct io_uring_sqe *sqe) { struct __kernel_timespec ts = {...}; prep_timeout(sqe, &ts); } void submit() { sqe = get_sqe(); prep_timeout(sqe); io_uring_submit(); } Signed-off-by: Pavel Begunkov <asml.silence@gmail.com> Signed-off-by: Jens Axboe <axboe@kernel.dk>
1 parent 0e78aa1 commit d8345a2

2 files changed

Lines changed: 20 additions & 5 deletions

File tree

include/uapi/linux/io_uring.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,10 @@ enum io_uring_op {
343343

344344
/*
345345
* sqe->timeout_flags
346+
*
347+
* IORING_TIMEOUT_IMMEDIATE_ARG: If set, sqe->addr stores the timeout
348+
* value in nanoseconds instead of
349+
* pointing to a timespec.
346350
*/
347351
#define IORING_TIMEOUT_ABS (1U << 0)
348352
#define IORING_TIMEOUT_UPDATE (1U << 1)
@@ -351,6 +355,7 @@ enum io_uring_op {
351355
#define IORING_LINK_TIMEOUT_UPDATE (1U << 4)
352356
#define IORING_TIMEOUT_ETIME_SUCCESS (1U << 5)
353357
#define IORING_TIMEOUT_MULTISHOT (1U << 6)
358+
#define IORING_TIMEOUT_IMMEDIATE_ARG (1U << 7)
354359
#define IORING_TIMEOUT_CLOCK_MASK (IORING_TIMEOUT_BOOTTIME | IORING_TIMEOUT_REALTIME)
355360
#define IORING_TIMEOUT_UPDATE_MASK (IORING_TIMEOUT_UPDATE | IORING_LINK_TIMEOUT_UPDATE)
356361
/*

io_uring/timeout.c

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,17 @@ struct io_timeout_rem {
3535
bool ltimeout;
3636
};
3737

38-
static int io_parse_user_time(ktime_t *time, u64 arg)
38+
static int io_parse_user_time(ktime_t *time, u64 arg, unsigned flags)
3939
{
4040
struct timespec64 ts;
4141

42+
if (flags & IORING_TIMEOUT_IMMEDIATE_ARG) {
43+
*time = ns_to_ktime(arg);
44+
if (*time < 0)
45+
return -EINVAL;
46+
return 0;
47+
}
48+
4249
if (get_timespec64(&ts, u64_to_user_ptr(arg)))
4350
return -EFAULT;
4451
if (ts.tv_sec < 0 || ts.tv_nsec < 0)
@@ -475,9 +482,11 @@ int io_timeout_remove_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
475482
return -EINVAL;
476483
if (tr->flags & IORING_LINK_TIMEOUT_UPDATE)
477484
tr->ltimeout = true;
478-
if (tr->flags & ~(IORING_TIMEOUT_UPDATE_MASK|IORING_TIMEOUT_ABS))
485+
if (tr->flags & ~(IORING_TIMEOUT_UPDATE_MASK |
486+
IORING_TIMEOUT_ABS |
487+
IORING_TIMEOUT_IMMEDIATE_ARG))
479488
return -EINVAL;
480-
ret = io_parse_user_time(&tr->time, READ_ONCE(sqe->addr2));
489+
ret = io_parse_user_time(&tr->time, READ_ONCE(sqe->addr2), tr->flags);
481490
if (ret)
482491
return ret;
483492
} else if (tr->flags) {
@@ -545,7 +554,8 @@ static int __io_timeout_prep(struct io_kiocb *req,
545554
flags = READ_ONCE(sqe->timeout_flags);
546555
if (flags & ~(IORING_TIMEOUT_ABS | IORING_TIMEOUT_CLOCK_MASK |
547556
IORING_TIMEOUT_ETIME_SUCCESS |
548-
IORING_TIMEOUT_MULTISHOT))
557+
IORING_TIMEOUT_MULTISHOT |
558+
IORING_TIMEOUT_IMMEDIATE_ARG))
549559
return -EINVAL;
550560
/* more than one clock specified is invalid, obviously */
551561
if (hweight32(flags & IORING_TIMEOUT_CLOCK_MASK) > 1)
@@ -574,7 +584,7 @@ static int __io_timeout_prep(struct io_kiocb *req,
574584
data->req = req;
575585
data->flags = flags;
576586

577-
ret = io_parse_user_time(&data->time, READ_ONCE(sqe->addr));
587+
ret = io_parse_user_time(&data->time, READ_ONCE(sqe->addr), flags);
578588
if (ret)
579589
return ret;
580590

0 commit comments

Comments
 (0)