Skip to content

Commit 1c47c0d

Browse files
committed
io_uring/rsrc: ensure compat iovecs are copied correctly
For buffer registration (or updates), a userspace iovec is copied in and updated. If the application is within a compat syscall, then the iovec type is compat_iovec rather than iovec. However, the type used in __io_sqe_buffers_update() and io_sqe_buffers_register() is always struct iovec, and hence the source is incremented by the size of a non-compat iovec in the loop. This misses every other iovec in the source, and will run into garbage half way through the copies and return -EFAULT to the application. Maintain the source address separately and assign to our user vec pointer, so that copies always happen from the right source address. While in there, correct a bad placement of __user which triggered the following sparse warning prior to this fix: io_uring/rsrc.c:981:33: warning: cast removes address space '__user' of expression io_uring/rsrc.c:981:30: warning: incorrect type in assignment (different address spaces) io_uring/rsrc.c:981:30: expected struct iovec const [noderef] __user *uvec io_uring/rsrc.c:981:30: got struct iovec *[noderef] __user Fixes: f4eaf8e ("io_uring/rsrc: Drop io_copy_iov in favor of iovec API") Reviewed-by: Gabriel Krisman Bertazi <[email protected]> Signed-off-by: Jens Axboe <[email protected]>
1 parent e0ee967 commit 1c47c0d

File tree

1 file changed

+15
-4
lines changed

1 file changed

+15
-4
lines changed

io_uring/rsrc.c

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -394,10 +394,11 @@ static int __io_sqe_buffers_update(struct io_ring_ctx *ctx,
394394
struct io_uring_rsrc_update2 *up,
395395
unsigned int nr_args)
396396
{
397-
struct iovec __user *uvec = u64_to_user_ptr(up->data);
398397
u64 __user *tags = u64_to_user_ptr(up->tags);
399398
struct iovec fast_iov, *iov;
400399
struct page *last_hpage = NULL;
400+
struct iovec __user *uvec;
401+
u64 user_data = up->data;
401402
__u32 done;
402403
int i, err;
403404

@@ -410,7 +411,8 @@ static int __io_sqe_buffers_update(struct io_ring_ctx *ctx,
410411
struct io_mapped_ubuf *imu;
411412
u64 tag = 0;
412413

413-
iov = iovec_from_user(&uvec[done], 1, 1, &fast_iov, ctx->compat);
414+
uvec = u64_to_user_ptr(user_data);
415+
iov = iovec_from_user(uvec, 1, 1, &fast_iov, ctx->compat);
414416
if (IS_ERR(iov)) {
415417
err = PTR_ERR(iov);
416418
break;
@@ -443,6 +445,10 @@ static int __io_sqe_buffers_update(struct io_ring_ctx *ctx,
443445

444446
ctx->user_bufs[i] = imu;
445447
*io_get_tag_slot(ctx->buf_data, i) = tag;
448+
if (ctx->compat)
449+
user_data += sizeof(struct compat_iovec);
450+
else
451+
user_data += sizeof(struct iovec);
446452
}
447453
return done ? done : err;
448454
}
@@ -949,7 +955,7 @@ int io_sqe_buffers_register(struct io_ring_ctx *ctx, void __user *arg,
949955
struct page *last_hpage = NULL;
950956
struct io_rsrc_data *data;
951957
struct iovec fast_iov, *iov = &fast_iov;
952-
const struct iovec __user *uvec = (struct iovec * __user) arg;
958+
const struct iovec __user *uvec;
953959
int i, ret;
954960

955961
BUILD_BUG_ON(IORING_MAX_REG_BUFFERS >= (1u << 16));
@@ -972,14 +978,19 @@ int io_sqe_buffers_register(struct io_ring_ctx *ctx, void __user *arg,
972978

973979
for (i = 0; i < nr_args; i++, ctx->nr_user_bufs++) {
974980
if (arg) {
975-
iov = iovec_from_user(&uvec[i], 1, 1, &fast_iov, ctx->compat);
981+
uvec = (struct iovec __user *) arg;
982+
iov = iovec_from_user(uvec, 1, 1, &fast_iov, ctx->compat);
976983
if (IS_ERR(iov)) {
977984
ret = PTR_ERR(iov);
978985
break;
979986
}
980987
ret = io_buffer_validate(iov);
981988
if (ret)
982989
break;
990+
if (ctx->compat)
991+
arg += sizeof(struct compat_iovec);
992+
else
993+
arg += sizeof(struct iovec);
983994
}
984995

985996
if (!iov->iov_base && *io_get_tag_slot(data, i)) {

0 commit comments

Comments
 (0)