mm/nommu: factor out check for NOMMU shared mappings into is_nommu_shared_mapping()
authorDavid Hildenbrand <david@redhat.com>
Mon, 2 Jan 2023 16:08:54 +0000 (17:08 +0100)
committerAndrew Morton <akpm@linux-foundation.org>
Thu, 19 Jan 2023 01:12:56 +0000 (17:12 -0800)
Patch series "mm/nommu: don't use VM_MAYSHARE for MAP_PRIVATE mappings".

Trying to reduce the confusion around VM_SHARED and VM_MAYSHARE first
requires !CONFIG_MMU to stop using VM_MAYSHARE for MAP_PRIVATE mappings.
CONFIG_MMU only sets VM_MAYSHARE for MAP_SHARED mappings.

This paves the way for further VM_MAYSHARE and VM_SHARED cleanups: for
example, renaming VM_MAYSHARED to VM_MAP_SHARED to make it cleaner what is
actually means.

Let's first get the weird case out of the way and not use VM_MAYSHARE in
MAP_PRIVATE mappings, using a new VM_MAYOVERLAY flag instead.

This patch (of 3):

We want to stop using VM_MAYSHARE in private mappings to pave the way for
clarifying the semantics of VM_MAYSHARE vs.  VM_SHARED and reduce the
confusion.  While CONFIG_MMU uses VM_MAYSHARE to represent MAP_SHARED,
!CONFIG_MMU also sets VM_MAYSHARE for selected R/O private file mappings
that are an effective overlay of a file mapping.

Let's factor out all relevant VM_MAYSHARE checks in !CONFIG_MMU code into
is_nommu_shared_mapping() first.

Note that whenever VM_SHARED is set, VM_MAYSHARE must be set as well
(unless there is a serious BUG).  So there is not need to test for
VM_SHARED manually.

No functional change intended.

Link: https://lkml.kernel.org/r/20230102160856.500584-1-david@redhat.com
Link: https://lkml.kernel.org/r/20230102160856.500584-2-david@redhat.com
Signed-off-by: David Hildenbrand <david@redhat.com>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: David Hildenbrand <david@redhat.com>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Jens Axboe <axboe@kernel.dk>
Cc: Nicolas Pitre <nico@fluxnic.net>
Cc: Pavel Begunkov <asml.silence@gmail.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
drivers/char/mem.c
fs/cramfs/inode.c
fs/proc/task_nommu.c
fs/ramfs/file-nommu.c
fs/romfs/mmap-nommu.c
include/linux/mm.h
io_uring/io_uring.c
mm/nommu.c

index 83bf2a4..ffb101d 100644 (file)
@@ -343,7 +343,7 @@ static unsigned zero_mmap_capabilities(struct file *file)
 /* can't do an in-place private mapping if there's no MMU */
 static inline int private_mapping_ok(struct vm_area_struct *vma)
 {
-       return vma->vm_flags & VM_MAYSHARE;
+       return is_nommu_shared_mapping(vma->vm_flags);
 }
 #else
 
index 61ccf77..50e4e06 100644 (file)
@@ -437,7 +437,7 @@ bailout:
 
 static int cramfs_physmem_mmap(struct file *file, struct vm_area_struct *vma)
 {
-       return vma->vm_flags & (VM_SHARED | VM_MAYSHARE) ? 0 : -ENOSYS;
+       return is_nommu_shared_mapping(vma->vm_flags) ? 0 : -ENOSYS;
 }
 
 static unsigned long cramfs_physmem_get_unmapped_area(struct file *file,
index 2fd06f5..0ec3507 100644 (file)
@@ -38,7 +38,7 @@ void task_mem(struct seq_file *m, struct mm_struct *mm)
                }
 
                if (atomic_read(&mm->mm_count) > 1 ||
-                   vma->vm_flags & VM_MAYSHARE) {
+                   is_nommu_shared_mapping(vma->vm_flags)) {
                        sbytes += size;
                } else {
                        bytes += size;
index cb240ea..cd45376 100644 (file)
@@ -264,7 +264,7 @@ out:
  */
 static int ramfs_nommu_mmap(struct file *file, struct vm_area_struct *vma)
 {
-       if (!(vma->vm_flags & (VM_SHARED | VM_MAYSHARE)))
+       if (!is_nommu_shared_mapping(vma->vm_flags))
                return -ENOSYS;
 
        file_accessed(file);
index 2c4a231..4578dc4 100644 (file)
@@ -63,7 +63,7 @@ static unsigned long romfs_get_unmapped_area(struct file *file,
  */
 static int romfs_mmap(struct file *file, struct vm_area_struct *vma)
 {
-       return vma->vm_flags & (VM_SHARED | VM_MAYSHARE) ? 0 : -ENOSYS;
+       return is_nommu_shared_mapping(vma->vm_flags) ? 0 : -ENOSYS;
 }
 
 static unsigned romfs_mmap_capabilities(struct file *file)
index eb5bfc7..791bac4 100644 (file)
@@ -1347,6 +1347,21 @@ static inline bool is_cow_mapping(vm_flags_t flags)
        return (flags & (VM_SHARED | VM_MAYWRITE)) == VM_MAYWRITE;
 }
 
+#ifndef CONFIG_MMU
+static inline bool is_nommu_shared_mapping(vm_flags_t flags)
+{
+       /*
+        * NOMMU shared mappings are ordinary MAP_SHARED mappings and selected
+        * R/O MAP_PRIVATE file mappings that are an effective R/O overlay of
+        * a file mapping. R/O MAP_PRIVATE mappings might still modify
+        * underlying memory if ptrace is active, so this is only possible if
+        * ptrace does not apply. Note that there is no mprotect() to upgrade
+        * write permissions later.
+        */
+       return flags & VM_MAYSHARE;
+}
+#endif
+
 #if defined(CONFIG_SPARSEMEM) && !defined(CONFIG_SPARSEMEM_VMEMMAP)
 #define SECTION_IN_PAGE_FLAGS
 #endif
index 2ac1cd8..3a934f7 100644 (file)
@@ -3206,7 +3206,7 @@ static __cold int io_uring_mmap(struct file *file, struct vm_area_struct *vma)
 
 static int io_uring_mmap(struct file *file, struct vm_area_struct *vma)
 {
-       return vma->vm_flags & (VM_SHARED | VM_MAYSHARE) ? 0 : -EINVAL;
+       return is_nommu_shared_mapping(vma->vm_flags) ? 0 : -EINVAL;
 }
 
 static unsigned int io_uring_nommu_mmap_capabilities(struct file *file)
index 5b83938..1671ebb 100644 (file)
@@ -958,9 +958,10 @@ static int do_mmap_private(struct vm_area_struct *vma,
         */
        if (capabilities & NOMMU_MAP_DIRECT) {
                ret = call_mmap(vma->vm_file, vma);
+               /* shouldn't return success if we're not sharing */
+               if (WARN_ON_ONCE(!is_nommu_shared_mapping(vma->vm_flags)))
+                       ret = -ENOSYS;
                if (ret == 0) {
-                       /* shouldn't return success if we're not sharing */
-                       BUG_ON(!(vma->vm_flags & VM_MAYSHARE));
                        vma->vm_region->vm_top = vma->vm_region->vm_end;
                        return 0;
                }
@@ -1106,7 +1107,7 @@ unsigned long do_mmap(struct file *file,
         *   these cases, sharing is handled in the driver or filesystem rather
         *   than here
         */
-       if (vm_flags & VM_MAYSHARE) {
+       if (is_nommu_shared_mapping(vm_flags)) {
                struct vm_region *pregion;
                unsigned long pglen, rpglen, pgend, rpgend, start;
 
@@ -1116,7 +1117,7 @@ unsigned long do_mmap(struct file *file,
                for (rb = rb_first(&nommu_region_tree); rb; rb = rb_next(rb)) {
                        pregion = rb_entry(rb, struct vm_region, vm_rb);
 
-                       if (!(pregion->vm_flags & VM_MAYSHARE))
+                       if (!is_nommu_shared_mapping(pregion->vm_flags))
                                continue;
 
                        /* search for overlapping mappings on the same file */
@@ -1600,7 +1601,7 @@ static unsigned long do_mremap(unsigned long addr,
        if (vma->vm_end != vma->vm_start + old_len)
                return (unsigned long) -EFAULT;
 
-       if (vma->vm_flags & VM_MAYSHARE)
+       if (is_nommu_shared_mapping(vma->vm_flags))
                return (unsigned long) -EPERM;
 
        if (new_len > vma->vm_region->vm_end - vma->vm_region->vm_start)