From: Amit Daniel Kachhap amitdaniel.kachhap@arm.com
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 to 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 other ABIs or compat.
Signed-off-by: Amit Daniel Kachhap amitdaniel.kachhap@arm.com Co-developed-by: Kevin Brodsky kevin.brodsky@arm.com Signed-off-by: Kevin Brodsky kevin.brodsky@arm.com --- include/linux/mm.h | 3 +++ mm/internal.h | 2 +- mm/mmap.c | 58 ++++++++++++++++++++++++++++++++++++++++++---- mm/util.c | 9 +------ 4 files changed, 59 insertions(+), 13 deletions(-)
diff --git a/include/linux/mm.h b/include/linux/mm.h index f9b5ad66a938..3d1e867bd903 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -3411,6 +3411,9 @@ struct vm_unmapped_area_info {
extern unsigned long vm_unmapped_area(struct vm_unmapped_area_info *info);
+int check_pcuabi_map_ptr_arg(user_uintptr_t user_ptr, unsigned long len, + bool map_fixed, bool locked); + /* 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 886a729fb13c..059a9d21ec54 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -1392,12 +1392,45 @@ 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_map_ptr_arg(user_uintptr_t user_ptr, unsigned long len, + bool map_fixed, bool locked) +{ + ptraddr_t addr = (ptraddr_t)user_ptr; + + if (!reserv_is_supported(current->mm)) + return 0; + + if (!check_user_ptr_owning(user_ptr, len)) { + if (!user_ptr_is_same((void __user *)user_ptr, as_user_ptr(addr))) + return -EINVAL; + /* + * Checking that the aligned range is wholly contained inside a + * reservation is sufficient. If the range is only partially + * contained within a reservation, get_unmapped_area will + * ensure that -ERESERVATION is returned. + */ + if (reserv_aligned_range_within_reserv(addr, len, locked)) + return -ERESERVATION; + return 0; + } + + if (!map_fixed) + return -EINVAL; + if (!reserv_cap_within_reserv(user_ptr, locked)) + return -ERESERVATION; + if (reserv_range_mapped(addr, len, locked)) + 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;
if (!(flags & MAP_ANONYMOUS)) { audit_mmap_fd(fd, flags); @@ -1430,7 +1463,18 @@ user_uintptr_t ksys_mmap_pgoff(user_uintptr_t addr, unsigned long len, return PTR_ERR(file); }
+ retval = check_pcuabi_map_ptr_arg(user_ptr, len, flags & MAP_FIXED, false); + if (retval) + goto out_fput; + retval = vm_mmap_pgoff(file, addr, len, prot, flags, pgoff); + if (!IS_ERR_VALUE(retval) && reserv_is_supported(current->mm)) { + if (user_ptr_is_valid((const void __user *)user_ptr)) + retval = user_ptr; + else + retval = reserv_make_user_ptr_owning((ptraddr_t)retval, + false); + } out_fput: if (file) fput(file); @@ -3082,9 +3126,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); + + if (reserv_is_supported(current->mm) && !check_user_ptr_owning(user_ptr, len)) + return -EINVAL; + if (!reserv_cap_within_reserv(user_ptr, false)) + return -ERESERVATION; + return __vm_munmap(addr, len, true); }
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)) - ret = (user_uintptr_t)uaddr_to_user_ptr_safe((ptraddr_t)ret); } return ret; }