Use the recently introduced PCuABI reservation interfaces and add the relevant capability/reservation constraint checks on the different mremap syscall parameters.
Signed-off-by: Amit Daniel Kachhap amitdaniel.kachhap@arm.com --- mm/mremap.c | 78 +++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 64 insertions(+), 14 deletions(-)
diff --git a/mm/mremap.c b/mm/mremap.c index 70f4031df1f4..fb648147b5d4 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; - 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; @@ -963,9 +967,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_perm(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; } @@ -987,6 +1000,9 @@ static int vma_expandable(struct vm_area_struct *vma, unsigned long delta) 0, MAP_FIXED) & ~PAGE_MASK) return 0; } + if (!reserv_vma_range_within_reserv(vma, vma->vm_start, end - vma->vm_start)) + return 0; + return 1; }
@@ -997,19 +1013,22 @@ 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); + VMA_ITERATOR(vmi, current->mm, new_addr);
- /* @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 @@ -1022,6 +1041,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; @@ -1060,6 +1080,33 @@ SYSCALL_DEFINE5(__retptr__(mremap), user_uintptr_t, addr, unsigned long, old_len goto out; }
+ if (!reserv_is_supported(current->mm)) + goto skip_pcuabi_checks; + if (!check_user_ptr_owning(user_ptr, addr, old_len ? old_len : new_len)) + goto out; + if (!reserv_vma_cap_within_reserv(vma, user_ptr)) { + ret = -ERESERVATION; + goto out; + } + if (flags & MREMAP_FIXED) { + if (!check_user_ptr_owning(new_user_ptr, new_addr, new_len)) + goto out; + if (!reserv_vmi_cap_within_reserv(&vmi, new_user_ptr, true)) { + ret = -ERESERVATION; + goto out; + } + if (!reserv_vmi_range_mapped(&vmi, new_addr, new_len, true)) { + ret = -ENOMEM; + goto out; + } + } else { + if (!user_ptr_is_same((const void __user *)new_user_ptr, + (const void __user *)(user_uintptr_t)new_addr)) + goto out; + } + old_perm = reserv_vma_reserv_perm(vma); +skip_pcuabi_checks: + if (is_vm_hugetlb_page(vma)) { struct hstate *h __maybe_unused = hstate_vma(vma);
@@ -1081,7 +1128,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; @@ -1106,7 +1153,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; }
@@ -1160,7 +1207,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; } } @@ -1184,8 +1231,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)) @@ -1197,8 +1250,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; }