mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-09-18 22:19:30 +02:00
io_uring/net: don't overconsume buffers when using MSG_TRUNC
When a recv/recvmsg is issued with MSG_TRUNC and the incoming packet is
larger than the provided buffer, the net layer returns the full length
of the packet rather than the number of bytes actually copied into the
buffer. As a result, io_uring advances more of the provided buffer ring
than was actually filled. Use the actual filled region size to consume
the buffer, but still return the full size to preserve MSG_TRUNC
semantics.
Take care with multishot, because that seems to already truncate the
consumption based on the available payload size.
This was reported in https://github.com/axboe/liburing/issues/1619.
Fixes: ae98dbf43d ("io_uring/kbuf: add support for incremental buffer consumption")
Cc: stable@vger.kernel.org
Link: https://patch.msgid.link/20260728191454.1850326-1-krisman@suse.de
Signed-off-by: Gabriel Krisman Bertazi <krisman@suse.de>
Link: https://patch.msgid.link/20260902230041.1320658-3-krisman@suse.de
[axboe: fold in size_t unsigned fix]
Signed-off-by: Jens Axboe <axboe@kernel.dk>
This commit is contained in:
committed by
Jens Axboe
parent
dcbd1c0548
commit
6028b54388
+33
-8
@@ -853,7 +853,7 @@ int io_recvmsg_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
|
||||
static inline bool io_recv_finish(struct io_kiocb *req,
|
||||
struct io_async_msghdr *kmsg,
|
||||
struct io_br_sel *sel, bool mshot_finished,
|
||||
unsigned issue_flags)
|
||||
unsigned issue_flags, int consumed)
|
||||
{
|
||||
struct io_sr_msg *sr = io_kiocb_to_cmd(req, struct io_sr_msg);
|
||||
unsigned int cflags = 0;
|
||||
@@ -877,7 +877,7 @@ static inline bool io_recv_finish(struct io_kiocb *req,
|
||||
if (sr->flags & IORING_RECVSEND_BUNDLE) {
|
||||
size_t this_ret = sel->val - sr->done_io;
|
||||
|
||||
cflags |= io_put_kbufs(req, this_ret, sel->buf_list, io_bundle_nbufs(kmsg, this_ret));
|
||||
cflags |= io_put_kbufs(req, consumed, sel->buf_list, io_bundle_nbufs(kmsg, consumed));
|
||||
if (sr->flags & IORING_RECV_RETRY)
|
||||
cflags = req->cqe.flags | (cflags & CQE_F_MASK);
|
||||
if (sr->mshot_len && sel->val >= sr->mshot_len)
|
||||
@@ -899,7 +899,7 @@ static inline bool io_recv_finish(struct io_kiocb *req,
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
cflags |= io_put_kbuf(req, sel->val, sel->buf_list);
|
||||
cflags |= io_put_kbuf(req, consumed, sel->buf_list);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1027,6 +1027,8 @@ int io_recvmsg(struct io_kiocb *req, unsigned int issue_flags)
|
||||
int ret, min_ret = 0;
|
||||
bool force_nonblock = issue_flags & IO_URING_F_NONBLOCK;
|
||||
bool mshot_finished = true;
|
||||
int consumed = 0;
|
||||
size_t len;
|
||||
|
||||
sock = sock_from_file(req->file);
|
||||
if (unlikely(!sock))
|
||||
@@ -1042,9 +1044,8 @@ int io_recvmsg(struct io_kiocb *req, unsigned int issue_flags)
|
||||
|
||||
retry_multishot:
|
||||
sel.buf_list = NULL;
|
||||
len = sr->len;
|
||||
if (io_do_buffer_select(req)) {
|
||||
size_t len = sr->len;
|
||||
|
||||
sel = io_buffer_select(req, &len, sr->buf_group, issue_flags);
|
||||
if (!sel.addr)
|
||||
return -ENOBUFS;
|
||||
@@ -1065,6 +1066,7 @@ retry_multishot:
|
||||
if (req->flags & REQ_F_APOLL_MULTISHOT) {
|
||||
ret = io_recvmsg_multishot(sock, sr, kmsg, flags,
|
||||
&mshot_finished);
|
||||
consumed = ret;
|
||||
} else {
|
||||
/* disable partial retry for recvmsg with cmsg attached */
|
||||
if (flags & MSG_WAITALL && !kmsg->msg.msg_controllen)
|
||||
@@ -1072,6 +1074,15 @@ retry_multishot:
|
||||
|
||||
ret = __sys_recvmsg_sock(sock, &kmsg->msg, sr->umsg,
|
||||
kmsg->uaddr, flags);
|
||||
/*
|
||||
* With MSG_TRUNC, the net layer will return the full size of
|
||||
* the packet, even if we only filled part of it in the buffers.
|
||||
* Adjust the returned size to consume only the real part of the
|
||||
* buffer.
|
||||
*/
|
||||
consumed = ret;
|
||||
if (ret > 0)
|
||||
consumed = min_t(size_t, ret, len);
|
||||
}
|
||||
|
||||
if (ret < min_ret) {
|
||||
@@ -1098,7 +1109,7 @@ retry_multishot:
|
||||
io_kbuf_recycle(req, sel.buf_list, issue_flags);
|
||||
|
||||
sel.val = ret;
|
||||
if (!io_recv_finish(req, kmsg, &sel, mshot_finished, issue_flags))
|
||||
if (!io_recv_finish(req, kmsg, &sel, mshot_finished, issue_flags, consumed))
|
||||
goto retry_multishot;
|
||||
|
||||
return sel.val;
|
||||
@@ -1185,9 +1196,10 @@ int io_recv(struct io_kiocb *req, unsigned int issue_flags)
|
||||
struct io_br_sel sel;
|
||||
struct socket *sock;
|
||||
unsigned flags;
|
||||
int ret, min_ret = 0;
|
||||
int ret, min_ret = 0, consumed = 0;
|
||||
bool force_nonblock = issue_flags & IO_URING_F_NONBLOCK;
|
||||
bool mshot_finished;
|
||||
size_t len = 0;
|
||||
|
||||
sock = sock_from_file(req->file);
|
||||
if (unlikely(!sock))
|
||||
@@ -1215,6 +1227,7 @@ int io_recv(struct io_kiocb *req, unsigned int issue_flags)
|
||||
|
||||
retry_multishot:
|
||||
sel.buf_list = NULL;
|
||||
len = sr->len;
|
||||
if (io_do_buffer_select(req)) {
|
||||
sel.val = sr->len;
|
||||
ret = io_recv_buf_select(req, kmsg, &sel, issue_flags);
|
||||
@@ -1222,6 +1235,7 @@ retry_multishot:
|
||||
kmsg->msg.msg_inq = -1;
|
||||
goto out_free;
|
||||
}
|
||||
len = ret;
|
||||
sr->buf = NULL;
|
||||
}
|
||||
|
||||
@@ -1252,6 +1266,17 @@ out_free:
|
||||
}
|
||||
|
||||
mshot_finished = ret <= 0;
|
||||
|
||||
/*
|
||||
* With MSG_TRUNC, the net layer will return the full size of
|
||||
* the packet, even if we only filled part of it in the buffers.
|
||||
* Adjust the returned size to consume only the real part of the
|
||||
* buffer.
|
||||
*/
|
||||
consumed = ret;
|
||||
if (ret > 0)
|
||||
consumed = min_t(size_t, ret, len);
|
||||
|
||||
if (ret > 0)
|
||||
ret += sr->done_io;
|
||||
else if (sr->done_io)
|
||||
@@ -1260,7 +1285,7 @@ out_free:
|
||||
io_kbuf_recycle(req, sel.buf_list, issue_flags);
|
||||
|
||||
sel.val = ret;
|
||||
if (!io_recv_finish(req, kmsg, &sel, mshot_finished, issue_flags))
|
||||
if (!io_recv_finish(req, kmsg, &sel, mshot_finished, issue_flags, consumed))
|
||||
goto retry_multishot;
|
||||
|
||||
return sel.val;
|
||||
|
||||
Reference in New Issue
Block a user