Use the recently introduced PCuABI reservation interfaces to add different parameter constraints for mmap/munmap syscall. The capability returned by mmap syscalls is now bounded and is same as the reservation range. These reservation checks do not affect the compat64 code path.
Signed-off-by: Amit Daniel Kachhap amitdaniel.kachhap@arm.com --- include/linux/cheri.h | 3 +++ include/linux/mm.h | 2 +- mm/mmap.c | 58 ++++++++++++++++++++++++++++++++++++++----- mm/util.c | 7 ------ 4 files changed, 56 insertions(+), 14 deletions(-)
diff --git a/include/linux/cheri.h b/include/linux/cheri.h index e5f588b056ad..02ef0e911e63 100644 --- a/include/linux/cheri.h +++ b/include/linux/cheri.h @@ -37,6 +37,9 @@ (CHERI_PERM_GLOBAL | CHERI_PERM_SW_VMEM) #endif
+#define cheri_is_null_derived(cap) \ + cheri_is_equal_exact((uintcap_t)cheri_address_get(cap), cap) + /** * cheri_build_user_cap() - Create a userspace capability. * @addr: Requested capability address. diff --git a/include/linux/mm.h b/include/linux/mm.h index 06d2aee83aa4..8e6541a8d4f9 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -3186,7 +3186,7 @@ static inline void mm_populate(unsigned long addr, unsigned long len) {} /* These take the mm semaphore themselves */ extern int __must_check vm_brk(unsigned long, unsigned long); extern int __must_check vm_brk_flags(unsigned long, unsigned long, unsigned long); -extern int vm_munmap(unsigned long, size_t); +extern int vm_munmap(user_uintptr_t, size_t); extern user_uintptr_t __must_check vm_mmap(struct file *, user_uintptr_t, unsigned long, unsigned long, unsigned long, unsigned long); diff --git a/mm/mmap.c b/mm/mmap.c index 7fd7d3ac377b..95d16e306559 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -1238,6 +1238,7 @@ user_uintptr_t do_mmap(struct file *file, user_uintptr_t usrptr, int pkey = 0; unsigned long addr = (ptraddr_t)usrptr; bool new_caps = true; + bool ignore_reserv = true;
validate_mm(mm); *populate = 0; @@ -1247,6 +1248,7 @@ user_uintptr_t do_mmap(struct file *file, user_uintptr_t usrptr,
#ifdef CONFIG_CHERI_PURECAP_UABI if (test_bit(MMF_PCUABI_RESERVE, &mm->flags)) { + ignore_reserv = false; if (cheri_tag_get(usrptr)) new_caps = false; } @@ -1284,7 +1286,7 @@ user_uintptr_t do_mmap(struct file *file, user_uintptr_t usrptr, /* Obtain the address to map to. we verify (or select) it and ensure * that it represents a valid section of the address space. */ - addr = get_unmapped_area(file, addr, len, pgoff, flags); + addr = get_unmapped_area(file, usrptr, len, pgoff, flags); if (IS_ERR_VALUE(addr)) return addr;
@@ -1416,6 +1418,13 @@ user_uintptr_t do_mmap(struct file *file, user_uintptr_t usrptr, ((vm_flags & VM_LOCKED) || (flags & (MAP_POPULATE | MAP_NONBLOCK)) == MAP_POPULATE)) *populate = len; + if (!IS_ERR_VALUE(addr)) { + if (new_caps && !ignore_reserv) + usrptr = build_owning_capability(addr, len, + mapping_to_capability_perm(prot, + (flags & MAP_SHARED) ? false : true)); + return usrptr; + }
return addr; } @@ -1425,8 +1434,11 @@ user_uintptr_t ksys_mmap_pgoff(user_uintptr_t usrptr, unsigned long len, unsigned long fd, unsigned long pgoff) { struct file *file = NULL; - user_uintptr_t retval; + user_uintptr_t retval = -EINVAL; ptraddr_t addr = (ptraddr_t)usrptr; +#ifdef CONFIG_CHERI_PURECAP_UABI + VMA_ITERATOR(vmi, current->mm, addr); +#endif
if (!(flags & MAP_ANONYMOUS)) { audit_mmap_fd(fd, flags); @@ -1458,6 +1470,26 @@ user_uintptr_t ksys_mmap_pgoff(user_uintptr_t usrptr, unsigned long len, if (IS_ERR(file)) return PTR_ERR(file); } +#ifdef CONFIG_CHERI_PURECAP_UABI + if (!test_bit(MMF_PCUABI_RESERVE, ¤t->mm->flags)) + goto skip_pcuabi_checks; + if (cheri_tag_get(usrptr)) { + if (!(flags & MAP_FIXED) || !capability_owns_range(usrptr, addr, len)) + goto out_fput; + if (!reserv_vmi_valid_capability(&vmi, usrptr, false)) { + retval = -ERESERVATION; + goto out_fput; + } + if (!reserv_vmi_range_mapped(&vmi, addr, len, false)) { + retval = -ENOMEM; + goto out_fput; + } + } else { + if (!cheri_is_null_derived(usrptr)) + goto out_fput; + } +skip_pcuabi_checks: +#endif
retval = vm_mmap_pgoff(file, usrptr, len, prot, flags, pgoff); out_fput: @@ -2941,15 +2973,29 @@ static int __vm_munmap(unsigned long start, size_t len, bool downgrade) return ret; }
-int vm_munmap(unsigned long start, size_t len) +/* TODO [PCuABI] - Update the users of vm_munmap */ +int vm_munmap(user_uintptr_t usrptr, size_t len) { - return __vm_munmap(start, len, false); + return __vm_munmap((ptraddr_t)usrptr, len, false); } EXPORT_SYMBOL(vm_munmap);
-SYSCALL_DEFINE2(munmap, user_uintptr_t, addr, size_t, len) +SYSCALL_DEFINE2(munmap, user_uintptr_t, usrptr, size_t, len) { - addr = untagged_addr(addr); + ptraddr_t addr = untagged_addr((ptraddr_t)usrptr); + VMA_ITERATOR(vmi, current->mm, addr); + +#ifdef CONFIG_CHERI_PURECAP_UABI + usrptr = cheri_address_set(usrptr, addr); +#else + usrptr = addr; +#endif + if (test_bit(MMF_PCUABI_RESERVE, ¤t->mm->flags)) { + if (!capability_owns_range(usrptr, addr, len)) + return -EINVAL; + if (!reserv_vmi_valid_capability(&vmi, usrptr, false)) + return -ERESERVATION; + } return __vm_munmap(addr, len, true); }
diff --git a/mm/util.c b/mm/util.c index f6fbfa7d014e..d0f937c334f3 100644 --- a/mm/util.c +++ b/mm/util.c @@ -540,19 +540,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, 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; }