* [PATCH v3 0/4] timeout immediate arg
@ 2026-03-02 13:10 Pavel Begunkov
2026-03-02 13:10 ` [PATCH v3 1/4] io_uring/timeout: check unused sqe fields Pavel Begunkov
` (4 more replies)
0 siblings, 5 replies; 6+ messages in thread
From: Pavel Begunkov @ 2026-03-02 13:10 UTC (permalink / raw)
To: io-uring; +Cc: asml.silence, axboe
Allow the user to pass the timeout value inside the SQE instead of
pointing to a timespec, people asked for it as it makes user space
simpler. More details description is in Patch 4.
v3: Enable the feature for the abs timeout mode
Convert internal request handling to ktime
Validate unused SQE fields for timeout reqs
v2: ditto for timeout updates
Pavel Begunkov (4):
io_uring/timeout: check unused sqe fields
io_uring/timeout: add helper for parsing user time
io_uring/timeout: migrate reqs from ts64 to ktime
io_uring/timeout: immediate timeout arg
include/uapi/linux/io_uring.h | 5 +++
io_uring/timeout.c | 70 +++++++++++++++++++++++------------
io_uring/timeout.h | 2 +-
3 files changed, 53 insertions(+), 24 deletions(-)
--
2.53.0
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH v3 1/4] io_uring/timeout: check unused sqe fields
2026-03-02 13:10 [PATCH v3 0/4] timeout immediate arg Pavel Begunkov
@ 2026-03-02 13:10 ` Pavel Begunkov
2026-03-02 13:10 ` [PATCH v3 2/4] io_uring/timeout: add helper for parsing user time Pavel Begunkov
` (3 subsequent siblings)
4 siblings, 0 replies; 6+ messages in thread
From: Pavel Begunkov @ 2026-03-02 13:10 UTC (permalink / raw)
To: io-uring; +Cc: asml.silence, axboe
Zero check unused SQE fields addr3 and pad2 for timeout and timeout
update requests. They're not needed now, but could be used sometime
in the future.
Cc: stable@vger.kernel.org
Signed-off-by: Pavel Begunkov <asml.silence@gmail.com>
---
io_uring/timeout.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/io_uring/timeout.c b/io_uring/timeout.c
index cb61d4862fc6..e3815e3465dd 100644
--- a/io_uring/timeout.c
+++ b/io_uring/timeout.c
@@ -449,6 +449,8 @@ int io_timeout_remove_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
if (unlikely(req->flags & (REQ_F_FIXED_FILE | REQ_F_BUFFER_SELECT)))
return -EINVAL;
+ if (sqe->addr3 || sqe->__pad2[0])
+ return -EINVAL;
if (sqe->buf_index || sqe->len || sqe->splice_fd_in)
return -EINVAL;
@@ -521,6 +523,8 @@ static int __io_timeout_prep(struct io_kiocb *req,
unsigned flags;
u32 off = READ_ONCE(sqe->off);
+ if (sqe->addr3 || sqe->__pad2[0])
+ return -EINVAL;
if (sqe->buf_index || sqe->len != 1 || sqe->splice_fd_in)
return -EINVAL;
if (off && is_timeout_link)
--
2.53.0
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH v3 2/4] io_uring/timeout: add helper for parsing user time
2026-03-02 13:10 [PATCH v3 0/4] timeout immediate arg Pavel Begunkov
2026-03-02 13:10 ` [PATCH v3 1/4] io_uring/timeout: check unused sqe fields Pavel Begunkov
@ 2026-03-02 13:10 ` Pavel Begunkov
2026-03-02 13:10 ` [PATCH v3 3/4] io_uring/timeout: migrate reqs from ts64 to ktime Pavel Begunkov
` (2 subsequent siblings)
4 siblings, 0 replies; 6+ messages in thread
From: Pavel Begunkov @ 2026-03-02 13:10 UTC (permalink / raw)
To: io-uring; +Cc: asml.silence, axboe
There is some duplication for timespec checks that can be deduplicated
with a new function, and it'll be extended in next patches.
Signed-off-by: Pavel Begunkov <asml.silence@gmail.com>
---
io_uring/timeout.c | 29 ++++++++++++++++++++---------
1 file changed, 20 insertions(+), 9 deletions(-)
diff --git a/io_uring/timeout.c b/io_uring/timeout.c
index e3815e3465dd..f6520599e3e8 100644
--- a/io_uring/timeout.c
+++ b/io_uring/timeout.c
@@ -35,6 +35,18 @@ struct io_timeout_rem {
bool ltimeout;
};
+static int io_parse_user_time(struct timespec64 *ts_out, u64 arg)
+{
+ struct timespec64 ts;
+
+ if (get_timespec64(&ts, u64_to_user_ptr(arg)))
+ return -EFAULT;
+ if (ts.tv_sec < 0 || ts.tv_nsec < 0)
+ return -EINVAL;
+ *ts_out = ts;
+ return 0;
+}
+
static struct io_kiocb *__io_disarm_linked_timeout(struct io_kiocb *req,
struct io_kiocb *link);
@@ -446,6 +458,7 @@ static int io_timeout_update(struct io_ring_ctx *ctx, __u64 user_data,
int io_timeout_remove_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
{
struct io_timeout_rem *tr = io_kiocb_to_cmd(req, struct io_timeout_rem);
+ int ret;
if (unlikely(req->flags & (REQ_F_FIXED_FILE | REQ_F_BUFFER_SELECT)))
return -EINVAL;
@@ -464,10 +477,9 @@ int io_timeout_remove_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
tr->ltimeout = true;
if (tr->flags & ~(IORING_TIMEOUT_UPDATE_MASK|IORING_TIMEOUT_ABS))
return -EINVAL;
- if (get_timespec64(&tr->ts, u64_to_user_ptr(READ_ONCE(sqe->addr2))))
- return -EFAULT;
- if (tr->ts.tv_sec < 0 || tr->ts.tv_nsec < 0)
- return -EINVAL;
+ ret = io_parse_user_time(&tr->ts, READ_ONCE(sqe->addr2));
+ if (ret)
+ return ret;
} else if (tr->flags) {
/* timeout removal doesn't support flags */
return -EINVAL;
@@ -522,6 +534,7 @@ static int __io_timeout_prep(struct io_kiocb *req,
struct io_timeout_data *data;
unsigned flags;
u32 off = READ_ONCE(sqe->off);
+ int ret;
if (sqe->addr3 || sqe->__pad2[0])
return -EINVAL;
@@ -561,11 +574,9 @@ static int __io_timeout_prep(struct io_kiocb *req,
data->req = req;
data->flags = flags;
- if (get_timespec64(&data->ts, u64_to_user_ptr(READ_ONCE(sqe->addr))))
- return -EFAULT;
-
- if (data->ts.tv_sec < 0 || data->ts.tv_nsec < 0)
- return -EINVAL;
+ ret = io_parse_user_time(&data->ts, READ_ONCE(sqe->addr));
+ if (ret)
+ return ret;
data->mode = io_translate_timeout_mode(flags);
--
2.53.0
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH v3 3/4] io_uring/timeout: migrate reqs from ts64 to ktime
2026-03-02 13:10 [PATCH v3 0/4] timeout immediate arg Pavel Begunkov
2026-03-02 13:10 ` [PATCH v3 1/4] io_uring/timeout: check unused sqe fields Pavel Begunkov
2026-03-02 13:10 ` [PATCH v3 2/4] io_uring/timeout: add helper for parsing user time Pavel Begunkov
@ 2026-03-02 13:10 ` Pavel Begunkov
2026-03-02 13:10 ` [PATCH v3 4/4] io_uring/timeout: immediate timeout arg Pavel Begunkov
2026-03-02 16:28 ` [PATCH v3 0/4] timeout immediate arg Jens Axboe
4 siblings, 0 replies; 6+ messages in thread
From: Pavel Begunkov @ 2026-03-02 13:10 UTC (permalink / raw)
To: io-uring; +Cc: asml.silence, axboe
It'll be more convenient for next patches to keep ktime in requests
instead of timespec64. Convert everything to ktime right after user
argument parsing at request prep time.
Signed-off-by: Pavel Begunkov <asml.silence@gmail.com>
---
io_uring/timeout.c | 31 +++++++++++++++----------------
io_uring/timeout.h | 2 +-
2 files changed, 16 insertions(+), 17 deletions(-)
diff --git a/io_uring/timeout.c b/io_uring/timeout.c
index f6520599e3e8..4b67746ea3ca 100644
--- a/io_uring/timeout.c
+++ b/io_uring/timeout.c
@@ -30,12 +30,12 @@ struct io_timeout_rem {
u64 addr;
/* timeout update */
- struct timespec64 ts;
+ ktime_t time;
u32 flags;
bool ltimeout;
};
-static int io_parse_user_time(struct timespec64 *ts_out, u64 arg)
+static int io_parse_user_time(ktime_t *time, u64 arg)
{
struct timespec64 ts;
@@ -43,7 +43,7 @@ static int io_parse_user_time(struct timespec64 *ts_out, u64 arg)
return -EFAULT;
if (ts.tv_sec < 0 || ts.tv_nsec < 0)
return -EINVAL;
- *ts_out = ts;
+ *time = timespec64_to_ktime(ts);
return 0;
}
@@ -92,7 +92,7 @@ static void io_timeout_complete(struct io_tw_req tw_req, io_tw_token_t tw)
/* re-arm timer */
raw_spin_lock_irq(&ctx->timeout_lock);
list_add(&timeout->list, ctx->timeout_list.prev);
- hrtimer_start(&data->timer, timespec64_to_ktime(data->ts), data->mode);
+ hrtimer_start(&data->timer, data->time, data->mode);
raw_spin_unlock_irq(&ctx->timeout_lock);
return;
}
@@ -407,7 +407,7 @@ static clockid_t io_timeout_get_clock(struct io_timeout_data *data)
}
static int io_linked_timeout_update(struct io_ring_ctx *ctx, __u64 user_data,
- struct timespec64 *ts, enum hrtimer_mode mode)
+ ktime_t ts, enum hrtimer_mode mode)
__must_hold(&ctx->timeout_lock)
{
struct io_timeout_data *io;
@@ -429,12 +429,12 @@ static int io_linked_timeout_update(struct io_ring_ctx *ctx, __u64 user_data,
if (hrtimer_try_to_cancel(&io->timer) == -1)
return -EALREADY;
hrtimer_setup(&io->timer, io_link_timeout_fn, io_timeout_get_clock(io), mode);
- hrtimer_start(&io->timer, timespec64_to_ktime(*ts), mode);
+ hrtimer_start(&io->timer, ts, mode);
return 0;
}
static int io_timeout_update(struct io_ring_ctx *ctx, __u64 user_data,
- struct timespec64 *ts, enum hrtimer_mode mode)
+ ktime_t time, enum hrtimer_mode mode)
__must_hold(&ctx->timeout_lock)
{
struct io_cancel_data cd = { .ctx = ctx, .data = user_data, };
@@ -447,11 +447,11 @@ static int io_timeout_update(struct io_ring_ctx *ctx, __u64 user_data,
timeout->off = 0; /* noseq */
data = req->async_data;
- data->ts = *ts;
+ data->time = time;
list_add_tail(&timeout->list, &ctx->timeout_list);
hrtimer_setup(&data->timer, io_timeout_fn, io_timeout_get_clock(data), mode);
- hrtimer_start(&data->timer, timespec64_to_ktime(data->ts), mode);
+ hrtimer_start(&data->timer, data->time, mode);
return 0;
}
@@ -477,7 +477,7 @@ int io_timeout_remove_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
tr->ltimeout = true;
if (tr->flags & ~(IORING_TIMEOUT_UPDATE_MASK|IORING_TIMEOUT_ABS))
return -EINVAL;
- ret = io_parse_user_time(&tr->ts, READ_ONCE(sqe->addr2));
+ ret = io_parse_user_time(&tr->time, READ_ONCE(sqe->addr2));
if (ret)
return ret;
} else if (tr->flags) {
@@ -514,9 +514,9 @@ int io_timeout_remove(struct io_kiocb *req, unsigned int issue_flags)
raw_spin_lock_irq(&ctx->timeout_lock);
if (tr->ltimeout)
- ret = io_linked_timeout_update(ctx, tr->addr, &tr->ts, mode);
+ ret = io_linked_timeout_update(ctx, tr->addr, tr->time, mode);
else
- ret = io_timeout_update(ctx, tr->addr, &tr->ts, mode);
+ ret = io_timeout_update(ctx, tr->addr, tr->time, mode);
raw_spin_unlock_irq(&ctx->timeout_lock);
}
@@ -574,7 +574,7 @@ static int __io_timeout_prep(struct io_kiocb *req,
data->req = req;
data->flags = flags;
- ret = io_parse_user_time(&data->ts, READ_ONCE(sqe->addr));
+ ret = io_parse_user_time(&data->time, READ_ONCE(sqe->addr));
if (ret)
return ret;
@@ -652,7 +652,7 @@ int io_timeout(struct io_kiocb *req, unsigned int issue_flags)
}
add:
list_add(&timeout->list, entry);
- hrtimer_start(&data->timer, timespec64_to_ktime(data->ts), data->mode);
+ hrtimer_start(&data->timer, data->time, data->mode);
raw_spin_unlock_irq(&ctx->timeout_lock);
return IOU_ISSUE_SKIP_COMPLETE;
}
@@ -670,8 +670,7 @@ void io_queue_linked_timeout(struct io_kiocb *req)
if (timeout->head) {
struct io_timeout_data *data = req->async_data;
- hrtimer_start(&data->timer, timespec64_to_ktime(data->ts),
- data->mode);
+ hrtimer_start(&data->timer, data->time, data->mode);
list_add_tail(&timeout->list, &ctx->ltimeout_list);
}
raw_spin_unlock_irq(&ctx->timeout_lock);
diff --git a/io_uring/timeout.h b/io_uring/timeout.h
index 2b7c9ad72992..1620f94dd45a 100644
--- a/io_uring/timeout.h
+++ b/io_uring/timeout.h
@@ -3,7 +3,7 @@
struct io_timeout_data {
struct io_kiocb *req;
struct hrtimer timer;
- struct timespec64 ts;
+ ktime_t time;
enum hrtimer_mode mode;
u32 flags;
};
--
2.53.0
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH v3 4/4] io_uring/timeout: immediate timeout arg
2026-03-02 13:10 [PATCH v3 0/4] timeout immediate arg Pavel Begunkov
` (2 preceding siblings ...)
2026-03-02 13:10 ` [PATCH v3 3/4] io_uring/timeout: migrate reqs from ts64 to ktime Pavel Begunkov
@ 2026-03-02 13:10 ` Pavel Begunkov
2026-03-02 16:28 ` [PATCH v3 0/4] timeout immediate arg Jens Axboe
4 siblings, 0 replies; 6+ messages in thread
From: Pavel Begunkov @ 2026-03-02 13:10 UTC (permalink / raw)
To: io-uring; +Cc: asml.silence, axboe
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>
---
include/uapi/linux/io_uring.h | 5 +++++
io_uring/timeout.c | 20 +++++++++++++++-----
2 files changed, 20 insertions(+), 5 deletions(-)
diff --git a/include/uapi/linux/io_uring.h b/include/uapi/linux/io_uring.h
index 17475c2045fb..17ac1b785440 100644
--- a/include/uapi/linux/io_uring.h
+++ b/include/uapi/linux/io_uring.h
@@ -343,6 +343,10 @@ enum io_uring_op {
/*
* sqe->timeout_flags
+ *
+ * IORING_TIMEOUT_IMMEDIATE_ARG: If set, sqe->addr stores the timeout
+ * value in nanoseconds instead of
+ * pointing to a timespec.
*/
#define IORING_TIMEOUT_ABS (1U << 0)
#define IORING_TIMEOUT_UPDATE (1U << 1)
@@ -351,6 +355,7 @@ enum io_uring_op {
#define IORING_LINK_TIMEOUT_UPDATE (1U << 4)
#define IORING_TIMEOUT_ETIME_SUCCESS (1U << 5)
#define IORING_TIMEOUT_MULTISHOT (1U << 6)
+#define IORING_TIMEOUT_IMMEDIATE_ARG (1U << 7)
#define IORING_TIMEOUT_CLOCK_MASK (IORING_TIMEOUT_BOOTTIME | IORING_TIMEOUT_REALTIME)
#define IORING_TIMEOUT_UPDATE_MASK (IORING_TIMEOUT_UPDATE | IORING_LINK_TIMEOUT_UPDATE)
/*
diff --git a/io_uring/timeout.c b/io_uring/timeout.c
index 4b67746ea3ca..8eddf8add7a2 100644
--- a/io_uring/timeout.c
+++ b/io_uring/timeout.c
@@ -35,10 +35,17 @@ struct io_timeout_rem {
bool ltimeout;
};
-static int io_parse_user_time(ktime_t *time, u64 arg)
+static int io_parse_user_time(ktime_t *time, u64 arg, unsigned flags)
{
struct timespec64 ts;
+ if (flags & IORING_TIMEOUT_IMMEDIATE_ARG) {
+ *time = ns_to_ktime(arg);
+ if (*time < 0)
+ return -EINVAL;
+ return 0;
+ }
+
if (get_timespec64(&ts, u64_to_user_ptr(arg)))
return -EFAULT;
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)
return -EINVAL;
if (tr->flags & IORING_LINK_TIMEOUT_UPDATE)
tr->ltimeout = true;
- if (tr->flags & ~(IORING_TIMEOUT_UPDATE_MASK|IORING_TIMEOUT_ABS))
+ if (tr->flags & ~(IORING_TIMEOUT_UPDATE_MASK |
+ IORING_TIMEOUT_ABS |
+ IORING_TIMEOUT_IMMEDIATE_ARG))
return -EINVAL;
- ret = io_parse_user_time(&tr->time, READ_ONCE(sqe->addr2));
+ ret = io_parse_user_time(&tr->time, READ_ONCE(sqe->addr2), tr->flags);
if (ret)
return ret;
} else if (tr->flags) {
@@ -545,7 +554,8 @@ static int __io_timeout_prep(struct io_kiocb *req,
flags = READ_ONCE(sqe->timeout_flags);
if (flags & ~(IORING_TIMEOUT_ABS | IORING_TIMEOUT_CLOCK_MASK |
IORING_TIMEOUT_ETIME_SUCCESS |
- IORING_TIMEOUT_MULTISHOT))
+ IORING_TIMEOUT_MULTISHOT |
+ IORING_TIMEOUT_IMMEDIATE_ARG))
return -EINVAL;
/* more than one clock specified is invalid, obviously */
if (hweight32(flags & IORING_TIMEOUT_CLOCK_MASK) > 1)
@@ -574,7 +584,7 @@ static int __io_timeout_prep(struct io_kiocb *req,
data->req = req;
data->flags = flags;
- ret = io_parse_user_time(&data->time, READ_ONCE(sqe->addr));
+ ret = io_parse_user_time(&data->time, READ_ONCE(sqe->addr), flags);
if (ret)
return ret;
--
2.53.0
^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH v3 0/4] timeout immediate arg
2026-03-02 13:10 [PATCH v3 0/4] timeout immediate arg Pavel Begunkov
` (3 preceding siblings ...)
2026-03-02 13:10 ` [PATCH v3 4/4] io_uring/timeout: immediate timeout arg Pavel Begunkov
@ 2026-03-02 16:28 ` Jens Axboe
4 siblings, 0 replies; 6+ messages in thread
From: Jens Axboe @ 2026-03-02 16:28 UTC (permalink / raw)
To: io-uring, Pavel Begunkov
On Mon, 02 Mar 2026 13:10:33 +0000, Pavel Begunkov wrote:
> Allow the user to pass the timeout value inside the SQE instead of
> pointing to a timespec, people asked for it as it makes user space
> simpler. More details description is in Patch 4.
>
> v3: Enable the feature for the abs timeout mode
> Convert internal request handling to ktime
> Validate unused SQE fields for timeout reqs
> v2: ditto for timeout updates
>
> [...]
Applied, thanks!
[1/4] io_uring/timeout: check unused sqe fields
commit: db74fcdb2659790cca5a2be7317c703f18289cc8
[2/4] io_uring/timeout: add helper for parsing user time
commit: e23b896d6b3aa9167206f60abba2d17a188ef010
[3/4] io_uring/timeout: migrate reqs from ts64 to ktime
commit: e4f28b04894d2d2ced34b1fbc89238881c3a9fdd
[4/4] io_uring/timeout: immediate timeout arg
commit: ee1d7dc3399054247e6146d8ce35bcd5ab8f2bd1
Best regards,
--
Jens Axboe
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2026-03-02 16:28 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-03-02 13:10 [PATCH v3 0/4] timeout immediate arg Pavel Begunkov
2026-03-02 13:10 ` [PATCH v3 1/4] io_uring/timeout: check unused sqe fields Pavel Begunkov
2026-03-02 13:10 ` [PATCH v3 2/4] io_uring/timeout: add helper for parsing user time Pavel Begunkov
2026-03-02 13:10 ` [PATCH v3 3/4] io_uring/timeout: migrate reqs from ts64 to ktime Pavel Begunkov
2026-03-02 13:10 ` [PATCH v3 4/4] io_uring/timeout: immediate timeout arg Pavel Begunkov
2026-03-02 16:28 ` [PATCH v3 0/4] timeout immediate arg Jens Axboe
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox