Use the recently introduced PCuABI reservation interfaces to add different parameter constraints for mremap syscall. The capability returned by mremap syscall is either same as the input capability or newly created if an old reservation exists or not.
Signed-off-by: Amit Daniel Kachhap amitdaniel.kachhap@arm.com --- mm/mremap.c | 56 +++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 42 insertions(+), 14 deletions(-)
diff --git a/mm/mremap.c b/mm/mremap.c index 1e0dd63c35a6..8a6a90d3c40e 100644 --- a/mm/mremap.c +++ b/mm/mremap.c @@ -25,6 +25,7 @@ #include <linux/uaccess.h> #include <linux/userfaultfd_k.h> #include <linux/mempolicy.h> +#include <linux/cap_addr_mgmt.h>
#include <asm/cacheflush.h> #include <asm/tlb.h> @@ -865,17 +866,20 @@ static struct vm_area_struct *vma_to_resize(unsigned long addr, return vma; }
-static unsigned long mremap_to(unsigned long addr, unsigned long old_len, - unsigned long new_addr, unsigned long new_len, bool *locked, +static user_uintptr_t mremap_to(user_uintptr_t user_ptr, unsigned long old_len, + user_uintptr_t new_user_ptr, unsigned long new_len, bool *locked, unsigned long flags, struct vm_userfaultfd_ctx *uf, struct list_head *uf_unmap_early, struct list_head *uf_unmap) { struct mm_struct *mm = current->mm; struct vm_area_struct *vma, *prev; - unsigned long ret = -EINVAL; + user_uintptr_t ret = -EINVAL; unsigned long map_flags = 0; struct reserv_struct reserv_info, *reserv_ptr = NULL; + ptraddr_t addr = (ptraddr_t)user_ptr; + ptraddr_t new_addr = (ptraddr_t)new_user_ptr; + unsigned long old_perm = 0;
if (offset_in_page(new_addr)) goto out; @@ -957,9 +961,18 @@ static unsigned long mremap_to(unsigned long addr, unsigned long old_len, if (!(flags & MREMAP_FIXED)) new_addr = ret;
+ old_perm = reserv_vma_reserv_perms(vma); ret = move_vma(vma, addr, old_len, new_len, new_addr, locked, flags, uf, uf_unmap, reserv_ptr);
+ if (!IS_ERR_VALUE(ret)) { + if (reserv_is_supported(mm)) { + if (!(flags & MREMAP_FIXED)) + ret = make_user_ptr_owning(new_addr, new_len, old_perm); + else + ret = new_user_ptr; + } + } out: return ret; } @@ -985,19 +998,21 @@ static int vma_expandable(struct vm_area_struct *vma, unsigned long delta) * MREMAP_FIXED option added 5-Dec-1999 by Benjamin LaHaise * This option implies MREMAP_MAYMOVE. */ -SYSCALL_DEFINE5(__retptr__(mremap), user_uintptr_t, addr, unsigned long, old_len, +SYSCALL_DEFINE5(__retptr__(mremap), user_uintptr_t, user_ptr, unsigned long, old_len, unsigned long, new_len, unsigned long, flags, - user_uintptr_t, new_addr) + user_uintptr_t, new_user_ptr) { struct mm_struct *mm = current->mm; struct vm_area_struct *vma; user_uintptr_t ret = -EINVAL; bool locked = false; struct vm_userfaultfd_ctx uf = NULL_VM_UFFD_CTX; + ptraddr_t addr = (ptraddr_t)user_ptr; + ptraddr_t new_addr = (ptraddr_t)new_user_ptr; + unsigned long old_perm = 0; LIST_HEAD(uf_unmap_early); LIST_HEAD(uf_unmap);
- /* @TODO [PCuABI] - capability validation */ /* * There is a deliberate asymmetry here: we strip the pointer tag * from the old address but leave the new address alone. This is @@ -1010,6 +1025,7 @@ SYSCALL_DEFINE5(__retptr__(mremap), user_uintptr_t, addr, unsigned long, old_len * information. */ addr = untagged_addr(addr); + user_ptr = (user_uintptr_t)user_ptr_set_addr((void __user *)user_ptr, addr);
if (flags & ~(MREMAP_FIXED | MREMAP_MAYMOVE | MREMAP_DONTUNMAP)) return ret; @@ -1048,6 +1064,15 @@ SYSCALL_DEFINE5(__retptr__(mremap), user_uintptr_t, addr, unsigned long, old_len goto out; }
+ ret = check_pcuabi_params(user_ptr, old_len ? old_len : new_len, + flags | MREMAP_FIXED, true, false, true); + if (ret) + goto out; + ret = check_pcuabi_params(new_user_ptr, new_len, flags, false, true, true); + if (ret) + goto out; + old_perm = reserv_vma_reserv_perms(vma); + if (is_vm_hugetlb_page(vma)) { struct hstate *h __maybe_unused = hstate_vma(vma);
@@ -1069,7 +1094,7 @@ SYSCALL_DEFINE5(__retptr__(mremap), user_uintptr_t, addr, unsigned long, old_len }
if (flags & (MREMAP_FIXED | MREMAP_DONTUNMAP)) { - ret = mremap_to(addr, old_len, new_addr, new_len, + ret = mremap_to(user_ptr, old_len, new_user_ptr, new_len, &locked, flags, &uf, &uf_unmap_early, &uf_unmap); goto out; @@ -1094,7 +1119,7 @@ SYSCALL_DEFINE5(__retptr__(mremap), user_uintptr_t, addr, unsigned long, old_len if (ret) goto out;
- ret = addr; + ret = user_ptr; goto out_unlocked; }
@@ -1148,7 +1173,7 @@ SYSCALL_DEFINE5(__retptr__(mremap), user_uintptr_t, addr, unsigned long, old_len locked = true; new_addr = addr; } - ret = addr; + ret = user_ptr; goto out; } } @@ -1172,8 +1197,14 @@ SYSCALL_DEFINE5(__retptr__(mremap), user_uintptr_t, addr, unsigned long, old_len goto out; }
- ret = move_vma(vma, addr, old_len, new_len, new_addr, + ret = move_vma(vma, user_ptr, old_len, new_len, new_addr, &locked, flags, &uf, &uf_unmap, NULL); + if (!IS_ERR_VALUE(ret)) { + if (reserv_is_supported(mm)) + ret = make_user_ptr_owning(new_addr, new_len, old_perm); + else + ret = (user_uintptr_t)new_addr; + } } out: if (offset_in_page(ret)) @@ -1185,8 +1216,5 @@ SYSCALL_DEFINE5(__retptr__(mremap), user_uintptr_t, addr, unsigned long, old_len userfaultfd_unmap_complete(mm, &uf_unmap_early); mremap_userfaultfd_complete(&uf, addr, ret, old_len); userfaultfd_unmap_complete(mm, &uf_unmap); - /* TODO [PCuABI] - derive proper capability */ - return IS_ERR_VALUE(ret) ? - ret : - (user_intptr_t)uaddr_to_user_ptr_safe((ptraddr_t)ret); + return ret; }