| From e96560ee2428c25be04ffdbd2c23de176a5bc343 Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Thu, 15 Apr 2021 13:07:39 +0100 |
| Subject: io_uring: fix overflows checks in provide buffers |
| |
| From: Pavel Begunkov <asml.silence@gmail.com> |
| |
| [ Upstream commit 38134ada0ceea3e848fe993263c0ff6207fd46e7 ] |
| |
| Colin reported before possible overflow and sign extension problems in |
| io_provide_buffers_prep(). As Linus pointed out previous attempt did nothing |
| useful, see d81269fecb8ce ("io_uring: fix provide_buffers sign extension"). |
| |
| Do that with help of check_<op>_overflow helpers. And fix struct |
| io_provide_buf::len type, as it doesn't make much sense to keep it |
| signed. |
| |
| Reported-by: Colin Ian King <colin.king@canonical.com> |
| Fixes: efe68c1ca8f49 ("io_uring: validate the full range of provided buffers for access") |
| Signed-off-by: Pavel Begunkov <asml.silence@gmail.com> |
| Link: https://lore.kernel.org/r/46538827e70fce5f6cdb50897cff4cacc490f380.1618488258.git.asml.silence@gmail.com |
| Signed-off-by: Jens Axboe <axboe@kernel.dk> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| fs/io_uring.c | 10 ++++++++-- |
| 1 file changed, 8 insertions(+), 2 deletions(-) |
| |
| diff --git a/fs/io_uring.c b/fs/io_uring.c |
| index 157ceda04650..c42c2e9570e5 100644 |
| --- a/fs/io_uring.c |
| +++ b/fs/io_uring.c |
| @@ -535,7 +535,7 @@ struct io_splice { |
| struct io_provide_buf { |
| struct file *file; |
| __u64 addr; |
| - __s32 len; |
| + __u32 len; |
| __u32 bgid; |
| __u16 nbufs; |
| __u16 bid; |
| @@ -4214,7 +4214,7 @@ static int io_remove_buffers(struct io_kiocb *req, bool force_nonblock, |
| static int io_provide_buffers_prep(struct io_kiocb *req, |
| const struct io_uring_sqe *sqe) |
| { |
| - unsigned long size; |
| + unsigned long size, tmp_check; |
| struct io_provide_buf *p = &req->pbuf; |
| u64 tmp; |
| |
| @@ -4228,6 +4228,12 @@ static int io_provide_buffers_prep(struct io_kiocb *req, |
| p->addr = READ_ONCE(sqe->addr); |
| p->len = READ_ONCE(sqe->len); |
| |
| + if (check_mul_overflow((unsigned long)p->len, (unsigned long)p->nbufs, |
| + &size)) |
| + return -EOVERFLOW; |
| + if (check_add_overflow((unsigned long)p->addr, size, &tmp_check)) |
| + return -EOVERFLOW; |
| + |
| size = (unsigned long)p->len * p->nbufs; |
| if (!access_ok(u64_to_user_ptr(p->addr), size)) |
| return -EFAULT; |
| -- |
| 2.30.2 |
| |