On 15/04/2024 15:21, Amit Daniel Kachhap wrote:
Use the recently introduced PCuABI reservation interfaces to add different parameter constraints for mmap/munmap syscall. The capability returned by mmap syscall is now bounded and is same as the reservation range. The in-kernel memory mapping vm_mmap() function do not check the constraints on parameters. These reservation checks added do not affect the compat64 code path.
Signed-off-by: Amit Daniel Kachhap amitdaniel.kachhap@arm.com
include/linux/mm.h | 4 ++++ mm/internal.h | 2 +- mm/mmap.c | 56 ++++++++++++++++++++++++++++++++++++++++++---- mm/util.c | 9 +------- 4 files changed, 58 insertions(+), 13 deletions(-)
diff --git a/include/linux/mm.h b/include/linux/mm.h index 6d62e91676cb..137dbd27db55 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -3411,6 +3411,10 @@ struct vm_unmapped_area_info { extern unsigned long vm_unmapped_area(struct vm_unmapped_area_info *info); +int check_pcuabi_params(user_uintptr_t user_ptr, unsigned long len,
unsigned long flags, bool enforce_cap_validity,bool enforce_range_mapped, bool reserv_lock);/* truncate.c */ extern void truncate_inode_pages(struct address_space *, loff_t); extern void truncate_inode_pages_range(struct address_space *, diff --git a/mm/internal.h b/mm/internal.h index 58df037c3824..3a88f1e2ffee 100644 --- a/mm/internal.h +++ b/mm/internal.h @@ -861,7 +861,7 @@ extern u64 hwpoison_filter_flags_value; extern u64 hwpoison_filter_memcg; extern u32 hwpoison_filter_enable; -extern user_uintptr_t __must_check vm_mmap_pgoff(struct file *, user_uintptr_t, +extern unsigned long __must_check vm_mmap_pgoff(struct file *, unsigned long, unsigned long, unsigned long, unsigned long, unsigned long); diff --git a/mm/mmap.c b/mm/mmap.c index 84e26bb7b203..cb069b76d761 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -1392,12 +1392,40 @@ unsigned long do_mmap(struct file *file, unsigned long addr, return addr; } -user_uintptr_t ksys_mmap_pgoff(user_uintptr_t addr, unsigned long len, +int check_pcuabi_params(user_uintptr_t user_ptr, unsigned long len,
unsigned long flags, bool enforce_cap_validity,bool enforce_range_mapped, bool reserv_lock)
The combination of enforce_cap_validity + enforce_range_mapped is pretty hard to read, especially as the function is directly called with true/false as parameters.
In practice, I think only enforce_cap_validity == false && enforce_range_mapped == true is useful. That corresponds to the standard mmap() case, as well as the new_addr case in mremap(), and shmat() (which is equivalent to mmap()). The old_addr case in mremap() corresponds in fact to the same checks as standard syscalls like munmap(), so it shouldn't be handled here.
+{
- ptraddr_t addr = (ptraddr_t)user_ptr;
 - int ret = -EINVAL;
 - VMA_ITERATOR(vmi, current->mm, addr);
 - if (!reserv_is_supported(current->mm))
 return 0;- if (!check_user_ptr_owning(user_ptr, addr, len)) {
 if (enforce_cap_validity || !user_ptr_is_same((const void __user *)user_ptr,(const void __user *)(user_uintptr_t)addr))return ret;return 0;- }
 - if (!reserv_vmi_cap_within_reserv(&vmi, user_ptr, reserv_lock))
 return -ERESERVATION;- if (!(flags & MREMAP_FIXED || flags & MAP_FIXED))
 
We cannot do this. MREMAP_FIXED is the same value as MAP_PRIVATE. The caller should check its own flags, we could pass a boolean instead.
return ret;- if (enforce_range_mapped && !reserv_vmi_range_mapped(&vmi, addr, len, reserv_lock))
 return -ENOMEM;- return 0;
 +}
+user_uintptr_t ksys_mmap_pgoff(user_uintptr_t user_ptr, unsigned long len, unsigned long prot, unsigned long flags, unsigned long fd, unsigned long pgoff) { struct file *file = NULL;
- user_uintptr_t retval;
 
- user_uintptr_t retval = -EINVAL;
 - ptraddr_t addr = (ptraddr_t)user_ptr;
 - bool new_reserv = true;
 if (!(flags & MAP_ANONYMOUS)) { audit_mmap_fd(fd, flags); @@ -1430,7 +1458,21 @@ user_uintptr_t ksys_mmap_pgoff(user_uintptr_t addr, unsigned long len, return PTR_ERR(file); }
- retval = check_pcuabi_params(user_ptr, len, flags, false, true, false);
 - if (retval)
 goto out_fput;- if (user_ptr_is_valid((const void __user *)user_ptr))
 new_reserv = true;
new_reserv = false surely?
- retval = vm_mmap_pgoff(file, addr, len, prot, flags, pgoff);
 - if (!IS_ERR_VALUE(retval) && reserv_is_supported(current->mm)) {
 if (new_reserv)retval = make_user_ptr_owning(retval, len,user_ptr_owning_perms_from_prot(prot,(flags & MAP_SHARED) ? false : true));elseretval = (user_uintptr_t)user_ptr_set_addr((void __user *)user_ptr, retval);
There's nothing to do in this case, the return value is exactly user_ptr as per the spec (in other words this operation should be a no-op).
Kevin
- }
 out_fput: if (file) fput(file); @@ -3097,9 +3139,15 @@ int vm_munmap(unsigned long start, size_t len) } EXPORT_SYMBOL(vm_munmap); -SYSCALL_DEFINE2(munmap, user_uintptr_t, addr, size_t, len) +SYSCALL_DEFINE2(munmap, user_uintptr_t, user_ptr, size_t, len) {
- addr = untagged_addr(addr);
 
- ptraddr_t addr = untagged_addr((ptraddr_t)user_ptr);
 - VMA_ITERATOR(vmi, current->mm, addr);
 - if (reserv_is_supported(current->mm) && !check_user_ptr_owning(user_ptr, addr, len))
 return -EINVAL;- if (!reserv_vmi_cap_within_reserv(&vmi, user_ptr, false))
  return __vm_munmap(addr, len, true);return -ERESERVATION;} diff --git a/mm/util.c b/mm/util.c index afd40ed9c3c8..bd69a417c6a9 100644 --- a/mm/util.c +++ b/mm/util.c @@ -540,7 +540,7 @@ int account_locked_vm(struct mm_struct *mm, unsigned long pages, bool inc) } EXPORT_SYMBOL_GPL(account_locked_vm); -user_uintptr_t vm_mmap_pgoff(struct file *file, user_uintptr_t addr, +unsigned long vm_mmap_pgoff(struct file *file, unsigned long addr, unsigned long len, unsigned long prot, unsigned long flag, unsigned long pgoff) { @@ -553,19 +553,12 @@ user_uintptr_t vm_mmap_pgoff(struct file *file, user_uintptr_t addr, if (!ret) { if (mmap_write_lock_killable(mm)) return -EINTR;
/** TODO [PCuABI] - might need propagating uintcap further down* to do_mmap to properly handle capabilities ret = do_mmap(file, addr, len, prot, flag, 0, pgoff, &populate, &uf); mmap_write_unlock(mm); userfaultfd_unmap_complete(mm, &uf); if (populate) mm_populate(ret, populate);*//* TODO [PCuABI] - derive proper capability */if (!IS_ERR_VALUE(ret)) } return ret;ret = (user_uintptr_t)uaddr_to_user_ptr_safe((ptraddr_t)ret);}