vm_mmap/vm_munmap/do_mmap are used across several places like filesystems, loaders and drivers to create memory mappings in the kernel.
At this point they have not been updated to handle user capabilities so limit PCuABI reservation to syscalls only.
Note: This commit may be temporary till full PCuABI support is added in kernel.
Signed-off-by: Amit Daniel Kachhap amit.kachhap@arm.com --- fs/aio.c | 2 +- include/linux/mm.h | 4 +++- ipc/shm.c | 2 +- mm/internal.h | 4 ++-- mm/mmap.c | 42 +++++++++++++++++++++++------------------- mm/nommu.c | 2 +- mm/util.c | 6 +++--- 7 files changed, 34 insertions(+), 28 deletions(-)
diff --git a/fs/aio.c b/fs/aio.c index 83ab611483ba..eadd9173b269 100644 --- a/fs/aio.c +++ b/fs/aio.c @@ -580,7 +580,7 @@ static int aio_setup_ring(struct kioctx *ctx, unsigned int nr_events)
ctx->mmap_base = do_mmap(ctx->aio_ring_file, 0, ctx->mmap_size, PROT_READ | PROT_WRITE, - MAP_SHARED, 0, &unused, NULL); + MAP_SHARED, 0, &unused, NULL, true); mmap_write_unlock(mm); if (IS_ERR((void *)ctx->mmap_base)) { ctx->mmap_size = 0; diff --git a/include/linux/mm.h b/include/linux/mm.h index 4206b761d777..67fe51267564 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -3140,9 +3140,11 @@ extern unsigned long get_unmapped_area(struct file *, unsigned long, unsigned lo extern unsigned long mmap_region(struct file *file, unsigned long addr, unsigned long len, vm_flags_t vm_flags, unsigned long pgoff, struct list_head *uf); + extern user_uintptr_t do_mmap(struct file *file, user_uintptr_t addr, unsigned long len, unsigned long prot, unsigned long flags, - unsigned long pgoff, unsigned long *populate, struct list_head *uf); + unsigned long pgoff, unsigned long *populate, struct list_head *uf, + bool reserv_ignore);
extern int do_vmi_munmap(struct vma_iterator *vmi, struct mm_struct *mm, user_uintptr_t start, size_t len, struct list_head *uf, diff --git a/ipc/shm.c b/ipc/shm.c index 16b75e8bcda1..87cd93788c5a 100644 --- a/ipc/shm.c +++ b/ipc/shm.c @@ -1671,7 +1671,7 @@ long do_shmat(int shmid, char __user *shmaddr, int shmflg, goto invalid; }
- addr = do_mmap(file, addr, size, prot, flags, 0, &populate, NULL); + addr = do_mmap(file, addr, size, prot, flags, 0, &populate, NULL, true); *raddr = addr; err = 0; if (IS_ERR_VALUE(addr)) diff --git a/mm/internal.h b/mm/internal.h index 4c26360de5a7..b5fdb2666577 100644 --- a/mm/internal.h +++ b/mm/internal.h @@ -779,8 +779,8 @@ extern u64 hwpoison_filter_memcg; extern u32 hwpoison_filter_enable;
extern user_uintptr_t __must_check vm_mmap_pgoff(struct file *, user_uintptr_t, - unsigned long, unsigned long, - unsigned long, unsigned long); + unsigned long, unsigned long, + unsigned long, unsigned long, bool reserve_ignore);
extern void set_pageblock_order(void); unsigned long reclaim_pages(struct list_head *folio_list); diff --git a/mm/mmap.c b/mm/mmap.c index 8f9c3d8686ab..34880a7c3c30 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -1229,7 +1229,8 @@ static inline bool file_mmap_ok(struct file *file, struct inode *inode, user_uintptr_t do_mmap(struct file *file, user_uintptr_t user_addr, unsigned long len, unsigned long prot, unsigned long flags, unsigned long pgoff, - unsigned long *populate, struct list_head *uf) + unsigned long *populate, struct list_head *uf, + bool reserve_ignore) { struct mm_struct *mm = current->mm; vm_flags_t vm_flags; @@ -1247,16 +1248,18 @@ user_uintptr_t do_mmap(struct file *file, user_uintptr_t user_addr, return -EINVAL;
#ifdef CONFIG_CHERI_PURECAP_UABI - if (cheri_tag_get(user_addr)) { - if (!capability_owns_range(user_addr, addr, len)) - return -EINVAL; - if (!reserv_mt_range_fully_mapped(&mm->reserv_mt, addr, len) || - !reserv_mt_capability_bound_valid(&mm->reserv_mt, user_addr)) - return -ERESERVATION; - } else { - if (!cheri_is_null_derived(user_addr)) - return -EINVAL; - is_reservation = true; + if (!reserve_ignore) { + if (cheri_tag_get(user_addr)) { + if (!capability_owns_range(user_addr, addr, len)) + return -EINVAL; + if (!reserv_mt_range_fully_mapped(&mm->reserv_mt, addr, len) || + !reserv_mt_capability_bound_valid(&mm->reserv_mt, user_addr)) + return -ERESERVATION; + } else { + if (!cheri_is_null_derived(user_addr)) + return -EINVAL; + is_reservation = true; + } } #endif /* CONFIG_CHERI_PURECAP_UABI */
@@ -1418,7 +1421,7 @@ user_uintptr_t do_mmap(struct file *file, user_uintptr_t user_addr, }
#ifdef CONFIG_CHERI_PURECAP_UABI - if (is_reservation) { + if (!reserve_ignore && is_reservation) { /* * Check if there is any overlap with the existing reservation. * This may help in filtering out any reservation error before @@ -1434,7 +1437,7 @@ user_uintptr_t do_mmap(struct file *file, user_uintptr_t user_addr, (flags & (MAP_POPULATE | MAP_NONBLOCK)) == MAP_POPULATE)) *populate = len; #ifdef CONFIG_CHERI_PURECAP_UABI - if (!IS_ERR_VALUE(addr)) { + if (!reserve_ignore && !IS_ERR_VALUE(addr)) { if (is_reservation) { ret = reserv_mt_insert_entry(&mm->reserv_mt, addr, len, prot); if (ret) @@ -1487,7 +1490,7 @@ user_uintptr_t ksys_mmap_pgoff(user_uintptr_t addr, unsigned long len, return PTR_ERR(file); }
- retval = vm_mmap_pgoff(file, addr, len, prot, flags, pgoff); + retval = vm_mmap_pgoff(file, addr, len, prot, flags, pgoff, false); out_fput: if (file) fput(file); @@ -2873,7 +2876,8 @@ unsigned long mmap_region(struct file *file, unsigned long addr, return error; }
-static int __vm_munmap(user_uintptr_t user_start, size_t len, bool downgrade) +static int __vm_munmap(user_uintptr_t user_start, size_t len, bool downgrade, + bool reserve_ignore) { int ret; struct mm_struct *mm = current->mm; @@ -2884,7 +2888,7 @@ static int __vm_munmap(user_uintptr_t user_start, size_t len, bool downgrade) if (mmap_write_lock_killable(mm)) return -EINTR;
- ret = do_vmi_munmap(&vmi, mm, user_start, len, &uf, downgrade, false); + ret = do_vmi_munmap(&vmi, mm, user_start, len, &uf, downgrade, reserve_ignore); /* * Returning 1 indicates mmap_lock is downgraded. * But 1 is not legal return value of vm_munmap() and munmap(), reset @@ -2903,7 +2907,7 @@ static int __vm_munmap(user_uintptr_t user_start, size_t len, bool downgrade) /* TODO [PCuABI] - Update the users of vm_munmap */ int vm_munmap(user_uintptr_t start, size_t len) { - return __vm_munmap(start, len, false); + return __vm_munmap(start, len, false, true); } EXPORT_SYMBOL(vm_munmap);
@@ -2914,7 +2918,7 @@ SYSCALL_DEFINE2(munmap, user_uintptr_t, addr, size_t, len) #else addr = untagged_addr(addr); #endif - return __vm_munmap(addr, len, true); + return __vm_munmap(addr, len, true, false); }
@@ -2990,7 +2994,7 @@ SYSCALL_DEFINE5(remap_file_pages, unsigned long, start, unsigned long, size,
file = get_file(vma->vm_file); ret = do_mmap(vma->vm_file, start, size, - prot, flags, pgoff, &populate, NULL); + prot, flags, pgoff, &populate, NULL, true); fput(file); out: mmap_write_unlock(mm); diff --git a/mm/nommu.c b/mm/nommu.c index f670d9979a26..b7d6a9180ca8 100644 --- a/mm/nommu.c +++ b/mm/nommu.c @@ -1255,7 +1255,7 @@ unsigned long ksys_mmap_pgoff(unsigned long addr, unsigned long len, goto out; }
- retval = vm_mmap_pgoff(file, addr, len, prot, flags, pgoff); + retval = vm_mmap_pgoff(file, addr, len, prot, flags, pgoff, true);
if (file) fput(file); diff --git a/mm/util.c b/mm/util.c index 6f5d9d864643..528d42d5211f 100644 --- a/mm/util.c +++ b/mm/util.c @@ -529,7 +529,7 @@ EXPORT_SYMBOL_GPL(account_locked_vm);
user_uintptr_t vm_mmap_pgoff(struct file *file, user_uintptr_t addr, unsigned long len, unsigned long prot, - unsigned long flag, unsigned long pgoff) + unsigned long flag, unsigned long pgoff, bool reserve_ignore) { user_uintptr_t ret; struct mm_struct *mm = current->mm; @@ -541,7 +541,7 @@ user_uintptr_t vm_mmap_pgoff(struct file *file, user_uintptr_t addr, if (mmap_write_lock_killable(mm)) return -EINTR; ret = do_mmap(file, addr, len, prot, flag, pgoff, &populate, - &uf); + &uf, reserve_ignore); mmap_write_unlock(mm); userfaultfd_unmap_complete(mm, &uf); if (populate) @@ -560,7 +560,7 @@ user_uintptr_t vm_mmap(struct file *file, user_uintptr_t addr, if (unlikely(offset_in_page(offset))) return -EINVAL;
- return vm_mmap_pgoff(file, addr, len, prot, flag, offset >> PAGE_SHIFT); + return vm_mmap_pgoff(file, addr, len, prot, flag, offset >> PAGE_SHIFT, true); } EXPORT_SYMBOL(vm_mmap);