Skip to content

Commit e71e2ac

Browse files
pcctorvalds
authored andcommitted
userfaultfd: do not untag user pointers
Patch series "userfaultfd: do not untag user pointers", v5. If a user program uses userfaultfd on ranges of heap memory, it may end up passing a tagged pointer to the kernel in the range.start field of the UFFDIO_REGISTER ioctl. This can happen when using an MTE-capable allocator, or on Android if using the Tagged Pointers feature for MTE readiness [1]. When a fault subsequently occurs, the tag is stripped from the fault address returned to the application in the fault.address field of struct uffd_msg. However, from the application's perspective, the tagged address *is* the memory address, so if the application is unaware of memory tags, it may get confused by receiving an address that is, from its point of view, outside of the bounds of the allocation. We observed this behavior in the kselftest for userfaultfd [2] but other applications could have the same problem. Address this by not untagging pointers passed to the userfaultfd ioctls. Instead, let the system call fail. Also change the kselftest to use mmap so that it doesn't encounter this problem. [1] https://source.android.com/devices/tech/debug/tagged-pointers [2] tools/testing/selftests/vm/userfaultfd.c This patch (of 2): Do not untag pointers passed to the userfaultfd ioctls. Instead, let the system call fail. This will provide an early indication of problems with tag-unaware userspace code instead of letting the code get confused later, and is consistent with how we decided to handle brk/mmap/mremap in commit dcde237 ("mm: Avoid creating virtual address aliases in brk()/mmap()/mremap()"), as well as being consistent with the existing tagged address ABI documentation relating to how ioctl arguments are handled. The code change is a revert of commit 7d03257 ("userfaultfd: untag user pointers") plus some fixups to some additional calls to validate_range that have appeared since then. [1] https://source.android.com/devices/tech/debug/tagged-pointers [2] tools/testing/selftests/vm/userfaultfd.c Link: https://lkml.kernel.org/r/[email protected] Link: https://lkml.kernel.org/r/[email protected] Link: https://linux-review.googlesource.com/id/I761aa9f0344454c482b83fcfcce547db0a25501b Fixes: 63f0c60 ("arm64: Introduce prctl() options to control the tagged user addresses ABI") Signed-off-by: Peter Collingbourne <[email protected]> Reviewed-by: Andrey Konovalov <[email protected]> Reviewed-by: Catalin Marinas <[email protected]> Cc: Alistair Delva <[email protected]> Cc: Andrea Arcangeli <[email protected]> Cc: Dave Martin <[email protected]> Cc: Evgenii Stepanov <[email protected]> Cc: Lokesh Gidra <[email protected]> Cc: Mitch Phillips <[email protected]> Cc: Vincenzo Frascino <[email protected]> Cc: Will Deacon <[email protected]> Cc: William McVicker <[email protected]> Cc: <[email protected]> [5.4] Signed-off-by: Andrew Morton <[email protected]> Signed-off-by: Linus Torvalds <[email protected]>
1 parent 704f4cb commit e71e2ac

File tree

2 files changed

+30
-22
lines changed

2 files changed

+30
-22
lines changed

Documentation/arm64/tagged-address-abi.rst

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -45,14 +45,24 @@ how the user addresses are used by the kernel:
4545

4646
1. User addresses not accessed by the kernel but used for address space
4747
management (e.g. ``mprotect()``, ``madvise()``). The use of valid
48-
tagged pointers in this context is allowed with the exception of
49-
``brk()``, ``mmap()`` and the ``new_address`` argument to
50-
``mremap()`` as these have the potential to alias with existing
51-
user addresses.
52-
53-
NOTE: This behaviour changed in v5.6 and so some earlier kernels may
54-
incorrectly accept valid tagged pointers for the ``brk()``,
55-
``mmap()`` and ``mremap()`` system calls.
48+
tagged pointers in this context is allowed with these exceptions:
49+
50+
- ``brk()``, ``mmap()`` and the ``new_address`` argument to
51+
``mremap()`` as these have the potential to alias with existing
52+
user addresses.
53+
54+
NOTE: This behaviour changed in v5.6 and so some earlier kernels may
55+
incorrectly accept valid tagged pointers for the ``brk()``,
56+
``mmap()`` and ``mremap()`` system calls.
57+
58+
- The ``range.start``, ``start`` and ``dst`` arguments to the
59+
``UFFDIO_*`` ``ioctl()``s used on a file descriptor obtained from
60+
``userfaultfd()``, as fault addresses subsequently obtained by reading
61+
the file descriptor will be untagged, which may otherwise confuse
62+
tag-unaware programs.
63+
64+
NOTE: This behaviour changed in v5.14 and so some earlier kernels may
65+
incorrectly accept valid tagged pointers for this system call.
5666

5767
2. User addresses accessed by the kernel (e.g. ``write()``). This ABI
5868
relaxation is disabled by default and the application thread needs to

fs/userfaultfd.c

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1236,23 +1236,21 @@ static __always_inline void wake_userfault(struct userfaultfd_ctx *ctx,
12361236
}
12371237

12381238
static __always_inline int validate_range(struct mm_struct *mm,
1239-
__u64 *start, __u64 len)
1239+
__u64 start, __u64 len)
12401240
{
12411241
__u64 task_size = mm->task_size;
12421242

1243-
*start = untagged_addr(*start);
1244-
1245-
if (*start & ~PAGE_MASK)
1243+
if (start & ~PAGE_MASK)
12461244
return -EINVAL;
12471245
if (len & ~PAGE_MASK)
12481246
return -EINVAL;
12491247
if (!len)
12501248
return -EINVAL;
1251-
if (*start < mmap_min_addr)
1249+
if (start < mmap_min_addr)
12521250
return -EINVAL;
1253-
if (*start >= task_size)
1251+
if (start >= task_size)
12541252
return -EINVAL;
1255-
if (len > task_size - *start)
1253+
if (len > task_size - start)
12561254
return -EINVAL;
12571255
return 0;
12581256
}
@@ -1316,7 +1314,7 @@ static int userfaultfd_register(struct userfaultfd_ctx *ctx,
13161314
vm_flags |= VM_UFFD_MINOR;
13171315
}
13181316

1319-
ret = validate_range(mm, &uffdio_register.range.start,
1317+
ret = validate_range(mm, uffdio_register.range.start,
13201318
uffdio_register.range.len);
13211319
if (ret)
13221320
goto out;
@@ -1522,7 +1520,7 @@ static int userfaultfd_unregister(struct userfaultfd_ctx *ctx,
15221520
if (copy_from_user(&uffdio_unregister, buf, sizeof(uffdio_unregister)))
15231521
goto out;
15241522

1525-
ret = validate_range(mm, &uffdio_unregister.start,
1523+
ret = validate_range(mm, uffdio_unregister.start,
15261524
uffdio_unregister.len);
15271525
if (ret)
15281526
goto out;
@@ -1671,7 +1669,7 @@ static int userfaultfd_wake(struct userfaultfd_ctx *ctx,
16711669
if (copy_from_user(&uffdio_wake, buf, sizeof(uffdio_wake)))
16721670
goto out;
16731671

1674-
ret = validate_range(ctx->mm, &uffdio_wake.start, uffdio_wake.len);
1672+
ret = validate_range(ctx->mm, uffdio_wake.start, uffdio_wake.len);
16751673
if (ret)
16761674
goto out;
16771675

@@ -1711,7 +1709,7 @@ static int userfaultfd_copy(struct userfaultfd_ctx *ctx,
17111709
sizeof(uffdio_copy)-sizeof(__s64)))
17121710
goto out;
17131711

1714-
ret = validate_range(ctx->mm, &uffdio_copy.dst, uffdio_copy.len);
1712+
ret = validate_range(ctx->mm, uffdio_copy.dst, uffdio_copy.len);
17151713
if (ret)
17161714
goto out;
17171715
/*
@@ -1768,7 +1766,7 @@ static int userfaultfd_zeropage(struct userfaultfd_ctx *ctx,
17681766
sizeof(uffdio_zeropage)-sizeof(__s64)))
17691767
goto out;
17701768

1771-
ret = validate_range(ctx->mm, &uffdio_zeropage.range.start,
1769+
ret = validate_range(ctx->mm, uffdio_zeropage.range.start,
17721770
uffdio_zeropage.range.len);
17731771
if (ret)
17741772
goto out;
@@ -1818,7 +1816,7 @@ static int userfaultfd_writeprotect(struct userfaultfd_ctx *ctx,
18181816
sizeof(struct uffdio_writeprotect)))
18191817
return -EFAULT;
18201818

1821-
ret = validate_range(ctx->mm, &uffdio_wp.range.start,
1819+
ret = validate_range(ctx->mm, uffdio_wp.range.start,
18221820
uffdio_wp.range.len);
18231821
if (ret)
18241822
return ret;
@@ -1866,7 +1864,7 @@ static int userfaultfd_continue(struct userfaultfd_ctx *ctx, unsigned long arg)
18661864
sizeof(uffdio_continue) - (sizeof(__s64))))
18671865
goto out;
18681866

1869-
ret = validate_range(ctx->mm, &uffdio_continue.range.start,
1867+
ret = validate_range(ctx->mm, uffdio_continue.range.start,
18701868
uffdio_continue.range.len);
18711869
if (ret)
18721870
goto out;

0 commit comments

Comments
 (0)