Commit 2ef5d724 authored by Axel Rasmussen's avatar Axel Rasmussen Committed by Andrew Morton

mm: userfaultfd: check for start + len overflow in validate_range

Most userfaultfd ioctls take a `start + len` range as an argument.  We
have the validate_range helper to check that such ranges are valid. 
However, some (but not all!) ioctls *also* check that `start + len`
doesn't wrap around (overflow).

Just check for this in validate_range.  This saves some repetitive code,
and adds the check to some ioctls which weren't bothering to check for it
before.

[axelrasmussen@google.com: call validate_range() on the src range too]
  Link: https://lkml.kernel.org/r/20230714182932.2608735-1-axelrasmussen@google.com
[axelrasmussen@google.com: fix src/dst validation]
  Link: https://lkml.kernel.org/r/20230810192128.1855570-1-axelrasmussen@google.com
Link: https://lkml.kernel.org/r/20230707215540.2324998-3-axelrasmussen@google.comSigned-off-by: default avatarAxel Rasmussen <axelrasmussen@google.com>
Reviewed-by: default avatarPeter Xu <peterx@redhat.com>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: Brian Geffon <bgeffon@google.com>
Cc: Christian Brauner <brauner@kernel.org>
Cc: David Hildenbrand <david@redhat.com>
Cc: Gaosheng Cui <cuigaosheng1@huawei.com>
Cc: Huang, Ying <ying.huang@intel.com>
Cc: Hugh Dickins <hughd@google.com>
Cc: James Houghton <jthoughton@google.com>
Cc: Jan Alexander Steffens (heftig) <heftig@archlinux.org>
Cc: Jiaqi Yan <jiaqiyan@google.com>
Cc: Jonathan Corbet <corbet@lwn.net>
Cc: Kefeng Wang <wangkefeng.wang@huawei.com>
Cc: Liam R. Howlett <Liam.Howlett@oracle.com>
Cc: Miaohe Lin <linmiaohe@huawei.com>
Cc: Mike Kravetz <mike.kravetz@oracle.com>
Cc: Mike Rapoport (IBM) <rppt@kernel.org>
Cc: Muchun Song <muchun.song@linux.dev>
Cc: Nadav Amit <namit@vmware.com>
Cc: Naoya Horiguchi <naoya.horiguchi@nec.com>
Cc: Ryan Roberts <ryan.roberts@arm.com>
Cc: Shuah Khan <shuah@kernel.org>
Cc: Suleiman Souhlal <suleiman@google.com>
Cc: Suren Baghdasaryan <surenb@google.com>
Cc: T.J. Alumbaugh <talumbau@google.com>
Cc: Yu Zhao <yuzhao@google.com>
Cc: ZhangPeng <zhangpeng362@huawei.com>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
parent f92cedfa
...@@ -1289,13 +1289,11 @@ static __always_inline void wake_userfault(struct userfaultfd_ctx *ctx, ...@@ -1289,13 +1289,11 @@ static __always_inline void wake_userfault(struct userfaultfd_ctx *ctx,
__wake_userfault(ctx, range); __wake_userfault(ctx, range);
} }
static __always_inline int validate_range(struct mm_struct *mm, static __always_inline int validate_unaligned_range(
__u64 start, __u64 len) struct mm_struct *mm, __u64 start, __u64 len)
{ {
__u64 task_size = mm->task_size; __u64 task_size = mm->task_size;
if (start & ~PAGE_MASK)
return -EINVAL;
if (len & ~PAGE_MASK) if (len & ~PAGE_MASK)
return -EINVAL; return -EINVAL;
if (!len) if (!len)
...@@ -1306,9 +1304,20 @@ static __always_inline int validate_range(struct mm_struct *mm, ...@@ -1306,9 +1304,20 @@ static __always_inline int validate_range(struct mm_struct *mm,
return -EINVAL; return -EINVAL;
if (len > task_size - start) if (len > task_size - start)
return -EINVAL; return -EINVAL;
if (start + len <= start)
return -EINVAL;
return 0; return 0;
} }
static __always_inline int validate_range(struct mm_struct *mm,
__u64 start, __u64 len)
{
if (start & ~PAGE_MASK)
return -EINVAL;
return validate_unaligned_range(mm, start, len);
}
static int userfaultfd_register(struct userfaultfd_ctx *ctx, static int userfaultfd_register(struct userfaultfd_ctx *ctx,
unsigned long arg) unsigned long arg)
{ {
...@@ -1757,17 +1766,15 @@ static int userfaultfd_copy(struct userfaultfd_ctx *ctx, ...@@ -1757,17 +1766,15 @@ static int userfaultfd_copy(struct userfaultfd_ctx *ctx,
sizeof(uffdio_copy)-sizeof(__s64))) sizeof(uffdio_copy)-sizeof(__s64)))
goto out; goto out;
ret = validate_unaligned_range(ctx->mm, uffdio_copy.src,
uffdio_copy.len);
if (ret)
goto out;
ret = validate_range(ctx->mm, uffdio_copy.dst, uffdio_copy.len); ret = validate_range(ctx->mm, uffdio_copy.dst, uffdio_copy.len);
if (ret) if (ret)
goto out; goto out;
/*
* double check for wraparound just in case. copy_from_user()
* will later check uffdio_copy.src + uffdio_copy.len to fit
* in the userland range.
*/
ret = -EINVAL; ret = -EINVAL;
if (uffdio_copy.src + uffdio_copy.len <= uffdio_copy.src)
goto out;
if (uffdio_copy.mode & ~(UFFDIO_COPY_MODE_DONTWAKE|UFFDIO_COPY_MODE_WP)) if (uffdio_copy.mode & ~(UFFDIO_COPY_MODE_DONTWAKE|UFFDIO_COPY_MODE_WP))
goto out; goto out;
if (uffdio_copy.mode & UFFDIO_COPY_MODE_WP) if (uffdio_copy.mode & UFFDIO_COPY_MODE_WP)
...@@ -1927,11 +1934,6 @@ static int userfaultfd_continue(struct userfaultfd_ctx *ctx, unsigned long arg) ...@@ -1927,11 +1934,6 @@ static int userfaultfd_continue(struct userfaultfd_ctx *ctx, unsigned long arg)
goto out; goto out;
ret = -EINVAL; ret = -EINVAL;
/* double check for wraparound just in case. */
if (uffdio_continue.range.start + uffdio_continue.range.len <=
uffdio_continue.range.start) {
goto out;
}
if (uffdio_continue.mode & ~(UFFDIO_CONTINUE_MODE_DONTWAKE | if (uffdio_continue.mode & ~(UFFDIO_CONTINUE_MODE_DONTWAKE |
UFFDIO_CONTINUE_MODE_WP)) UFFDIO_CONTINUE_MODE_WP))
goto out; goto out;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment