mm/nommu: don't use VM_MAYSHARE for MAP_PRIVATE mappings
authorDavid Hildenbrand <david@redhat.com>
Mon, 2 Jan 2023 16:08:55 +0000 (17:08 +0100)
committerAndrew Morton <akpm@linux-foundation.org>
Thu, 19 Jan 2023 01:12:57 +0000 (17:12 -0800)
Let's stop using VM_MAYSHARE for MAP_PRIVATE mappings and use
VM_MAYOVERLAY instead.  Rewrite determine_vm_flags() to make the whole
logic easier to digest, and to cleanly separate MAP_PRIVATE vs.
MAP_SHARED.

No functional change intended.

Link: https://lkml.kernel.org/r/20230102160856.500584-3-david@redhat.com
Signed-off-by: David Hildenbrand <david@redhat.com>
Cc: Arnd Bergmann <arnd@arndb.de>
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>
include/linux/mm.h
mm/nommu.c

index 791bac4..8a85633 100644 (file)
@@ -276,7 +276,12 @@ extern unsigned int kobjsize(const void *objp);
 #define VM_MAYSHARE    0x00000080
 
 #define VM_GROWSDOWN   0x00000100      /* general info on the segment */
+#ifdef CONFIG_MMU
 #define VM_UFFD_MISSING        0x00000200      /* missing pages tracking */
+#else /* CONFIG_MMU */
+#define VM_MAYOVERLAY  0x00000200      /* nommu: R/O MAP_PRIVATE mapping that might overlay a file mapping */
+#define VM_UFFD_MISSING        0
+#endif /* CONFIG_MMU */
 #define VM_PFNMAP      0x00000400      /* Page-ranges managed without "struct page", just pure PFN */
 #define VM_UFFD_WP     0x00001000      /* wrprotect pages tracking */
 
@@ -1358,7 +1363,7 @@ static inline bool is_nommu_shared_mapping(vm_flags_t flags)
         * ptrace does not apply. Note that there is no mprotect() to upgrade
         * write permissions later.
         */
-       return flags & VM_MAYSHARE;
+       return flags & (VM_MAYSHARE | VM_MAYOVERLAY);
 }
 #endif
 
index 1671ebb..df1711a 100644 (file)
@@ -892,29 +892,36 @@ static unsigned long determine_vm_flags(struct file *file,
        unsigned long vm_flags;
 
        vm_flags = calc_vm_prot_bits(prot, 0) | calc_vm_flag_bits(flags);
-       /* vm_flags |= mm->def_flags; */
 
-       if (!(capabilities & NOMMU_MAP_DIRECT)) {
-               /* attempt to share read-only copies of mapped file chunks */
+       if (!file) {
+               /*
+                * MAP_ANONYMOUS. MAP_SHARED is mapped to MAP_PRIVATE, because
+                * there is no fork().
+                */
                vm_flags |= VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC;
-               if (file && !(prot & PROT_WRITE))
-                       vm_flags |= VM_MAYSHARE;
+       } else if (flags & MAP_PRIVATE) {
+               /* MAP_PRIVATE file mapping */
+               if (capabilities & NOMMU_MAP_DIRECT)
+                       vm_flags |= (capabilities & NOMMU_VMFLAGS);
+               else
+                       vm_flags |= VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC;
+
+               if (!(prot & PROT_WRITE) && !current->ptrace)
+                       /*
+                        * R/O private file mapping which cannot be used to
+                        * modify memory, especially also not via active ptrace
+                        * (e.g., set breakpoints) or later by upgrading
+                        * permissions (no mprotect()). We can try overlaying
+                        * the file mapping, which will work e.g., on chardevs,
+                        * ramfs/tmpfs/shmfs and romfs/cramf.
+                        */
+                       vm_flags |= VM_MAYOVERLAY;
        } else {
-               /* overlay a shareable mapping on the backing device or inode
-                * if possible - used for chardevs, ramfs/tmpfs/shmfs and
-                * romfs/cramfs */
-               vm_flags |= VM_MAYSHARE | (capabilities & NOMMU_VMFLAGS);
-               if (flags & MAP_SHARED)
-                       vm_flags |= VM_SHARED;
+               /* MAP_SHARED file mapping: NOMMU_MAP_DIRECT is set. */
+               vm_flags |= VM_SHARED | VM_MAYSHARE |
+                           (capabilities & NOMMU_VMFLAGS);
        }
 
-       /* refuse to let anyone share private mappings with this process if
-        * it's being traced - otherwise breakpoints set in it may interfere
-        * with another untraced process
-        */
-       if ((flags & MAP_PRIVATE) && current->ptrace)
-               vm_flags &= ~VM_MAYSHARE;
-
        return vm_flags;
 }
 
@@ -952,9 +959,11 @@ static int do_mmap_private(struct vm_area_struct *vma,
        void *base;
        int ret, order;
 
-       /* invoke the file's mapping function so that it can keep track of
-        * shared mappings on devices or memory
-        * - VM_MAYSHARE will be set if it may attempt to share
+       /*
+        * Invoke the file's mapping function so that it can keep track of
+        * shared mappings on devices or memory. VM_MAYOVERLAY will be set if
+        * it may attempt to share, which will make is_nommu_shared_mapping()
+        * happy.
         */
        if (capabilities & NOMMU_MAP_DIRECT) {
                ret = call_mmap(vma->vm_file, vma);