mm: introduce MAP_SHARED_VALIDATE, a mechanism to safely define new mmap flags
authorDan Williams <dan.j.williams@intel.com>
Wed, 1 Nov 2017 15:36:30 +0000 (16:36 +0100)
committerDan Williams <dan.j.williams@intel.com>
Fri, 3 Nov 2017 13:26:22 +0000 (06:26 -0700)
The mmap(2) syscall suffers from the ABI anti-pattern of not validating
unknown flags. However, proposals like MAP_SYNC need a mechanism to
define new behavior that is known to fail on older kernels without the
support. Define a new MAP_SHARED_VALIDATE flag pattern that is
guaranteed to fail on all legacy mmap implementations.

It is worth noting that the original proposal was for a standalone
MAP_VALIDATE flag. However, when that  could not be supported by all
archs Linus observed:

    I see why you *think* you want a bitmap. You think you want
    a bitmap because you want to make MAP_VALIDATE be part of MAP_SYNC
    etc, so that people can do

    ret = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED
    | MAP_SYNC, fd, 0);

    and "know" that MAP_SYNC actually takes.

    And I'm saying that whole wish is bogus. You're fundamentally
    depending on special semantics, just make it explicit. It's already
    not portable, so don't try to make it so.

    Rename that MAP_VALIDATE as MAP_SHARED_VALIDATE, make it have a value
    of 0x3, and make people do

    ret = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED_VALIDATE
    | MAP_SYNC, fd, 0);

    and then the kernel side is easier too (none of that random garbage
    playing games with looking at the "MAP_VALIDATE bit", but just another
    case statement in that map type thing.

    Boom. Done.

Similar to ->fallocate() we also want the ability to validate the
support for new flags on a per ->mmap() 'struct file_operations'
instance basis.  Towards that end arrange for flags to be generically
validated against a mmap_supported_flags exported by 'struct
file_operations'. By default all existing flags are implicitly
supported, but new flags require MAP_SHARED_VALIDATE and
per-instance-opt-in.

Cc: Jan Kara <jack@suse.cz>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Andy Lutomirski <luto@kernel.org>
Cc: Andrew Morton <akpm@linux-foundation.org>
Suggested-by: Christoph Hellwig <hch@lst.de>
Suggested-by: Linus Torvalds <torvalds@linux-foundation.org>
Reviewed-by: Ross Zwisler <ross.zwisler@linux.intel.com>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
Signed-off-by: Jan Kara <jack@suse.cz>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
arch/alpha/include/uapi/asm/mman.h
arch/mips/include/uapi/asm/mman.h
arch/parisc/include/uapi/asm/mman.h
arch/xtensa/include/uapi/asm/mman.h
include/linux/fs.h
include/linux/mman.h
include/uapi/asm-generic/mman-common.h
mm/mmap.c
tools/include/uapi/asm-generic/mman-common.h

index 3b26cc62dadb08d64b2d91780a92fd5458da76b6..f6d118aaedb943a691ca187fb33d5e0d3e69b6d6 100644 (file)
@@ -11,6 +11,7 @@
 
 #define MAP_SHARED     0x01            /* Share changes */
 #define MAP_PRIVATE    0x02            /* Changes are private */
+#define MAP_SHARED_VALIDATE 0x03       /* share + validate extension flags */
 #define MAP_TYPE       0x0f            /* Mask for type of mapping (OSF/1 is _wrong_) */
 #define MAP_FIXED      0x100           /* Interpret addr exactly */
 #define MAP_ANONYMOUS  0x10            /* don't use a file */
index da3216007fe0ccac2e0695c10b4e1cac1ada8b9d..93268e4cd3c7e05324bad0671e1435acdf38fc05 100644 (file)
@@ -28,6 +28,7 @@
  */
 #define MAP_SHARED     0x001           /* Share changes */
 #define MAP_PRIVATE    0x002           /* Changes are private */
+#define MAP_SHARED_VALIDATE 0x003      /* share + validate extension flags */
 #define MAP_TYPE       0x00f           /* Mask for type of mapping */
 #define MAP_FIXED      0x010           /* Interpret addr exactly */
 
index 775b5d5e41a1db26358b1457290a499d442bee71..bca652aa1677be338173da92e4d727f9f3ece609 100644 (file)
@@ -11,6 +11,7 @@
 
 #define MAP_SHARED     0x01            /* Share changes */
 #define MAP_PRIVATE    0x02            /* Changes are private */
+#define MAP_SHARED_VALIDATE 0x03       /* share + validate extension flags */
 #define MAP_TYPE       0x03            /* Mask for type of mapping */
 #define MAP_FIXED      0x04            /* Interpret addr exactly */
 #define MAP_ANONYMOUS  0x10            /* don't use a file */
index b15b278aa314b4923adfb856fad1e5925eedc83b..9ab426374714c16e30e50a465b97a8ef019eca84 100644 (file)
@@ -35,6 +35,7 @@
  */
 #define MAP_SHARED     0x001           /* Share changes */
 #define MAP_PRIVATE    0x002           /* Changes are private */
+#define MAP_SHARED_VALIDATE 0x003      /* share + validate extension flags */
 #define MAP_TYPE       0x00f           /* Mask for type of mapping */
 #define MAP_FIXED      0x010           /* Interpret addr exactly */
 
index 13dab191a23e61654d29a4bff297919105bb20a6..57added3201db18aa32f0e6d69fb37e0e02d6cdc 100644 (file)
@@ -1701,6 +1701,7 @@ struct file_operations {
        long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
        long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
        int (*mmap) (struct file *, struct vm_area_struct *);
+       unsigned long mmap_supported_flags;
        int (*open) (struct inode *, struct file *);
        int (*flush) (struct file *, fl_owner_t id);
        int (*release) (struct inode *, struct file *);
index edb6cf6a81ed8a11061f07c2c53e5819dd472bd1..74452e3f25360e39d663550382533ba1352a5f0a 100644 (file)
@@ -7,6 +7,45 @@
 #include <linux/atomic.h>
 #include <uapi/linux/mman.h>
 
+/*
+ * Arrange for legacy / undefined architecture specific flags to be
+ * ignored by default in LEGACY_MAP_MASK.
+ */
+#ifndef MAP_32BIT
+#define MAP_32BIT 0
+#endif
+#ifndef MAP_HUGE_2MB
+#define MAP_HUGE_2MB 0
+#endif
+#ifndef MAP_HUGE_1GB
+#define MAP_HUGE_1GB 0
+#endif
+#ifndef MAP_UNINITIALIZED
+#define MAP_UNINITIALIZED 0
+#endif
+
+/*
+ * The historical set of flags that all mmap implementations implicitly
+ * support when a ->mmap_validate() op is not provided in file_operations.
+ */
+#define LEGACY_MAP_MASK (MAP_SHARED \
+               | MAP_PRIVATE \
+               | MAP_FIXED \
+               | MAP_ANONYMOUS \
+               | MAP_DENYWRITE \
+               | MAP_EXECUTABLE \
+               | MAP_UNINITIALIZED \
+               | MAP_GROWSDOWN \
+               | MAP_LOCKED \
+               | MAP_NORESERVE \
+               | MAP_POPULATE \
+               | MAP_NONBLOCK \
+               | MAP_STACK \
+               | MAP_HUGETLB \
+               | MAP_32BIT \
+               | MAP_HUGE_2MB \
+               | MAP_HUGE_1GB)
+
 extern int sysctl_overcommit_memory;
 extern int sysctl_overcommit_ratio;
 extern unsigned long sysctl_overcommit_kbytes;
index 203268f9231e155d72307995989feab4857defe7..8ce7f5a0800fee5ad05e23a80448932d644681d3 100644 (file)
@@ -16,6 +16,7 @@
 
 #define MAP_SHARED     0x01            /* Share changes */
 #define MAP_PRIVATE    0x02            /* Changes are private */
+#define MAP_SHARED_VALIDATE 0x03       /* share + validate extension flags */
 #define MAP_TYPE       0x0f            /* Mask for type of mapping */
 #define MAP_FIXED      0x10            /* Interpret addr exactly */
 #define MAP_ANONYMOUS  0x20            /* don't use a file */
index 680506faceae91d9da5347f27f1271a9b542addf..924839fac0e6421a77839825a99833a342d3153c 100644 (file)
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -1387,9 +1387,24 @@ unsigned long do_mmap(struct file *file, unsigned long addr,
 
        if (file) {
                struct inode *inode = file_inode(file);
+               unsigned long flags_mask;
+
+               flags_mask = LEGACY_MAP_MASK | file->f_op->mmap_supported_flags;
 
                switch (flags & MAP_TYPE) {
                case MAP_SHARED:
+                       /*
+                        * Force use of MAP_SHARED_VALIDATE with non-legacy
+                        * flags. E.g. MAP_SYNC is dangerous to use with
+                        * MAP_SHARED as you don't know which consistency model
+                        * you will get. We silently ignore unsupported flags
+                        * with MAP_SHARED to preserve backward compatibility.
+                        */
+                       flags &= LEGACY_MAP_MASK;
+                       /* fall through */
+               case MAP_SHARED_VALIDATE:
+                       if (flags & ~flags_mask)
+                               return -EOPNOTSUPP;
                        if ((prot&PROT_WRITE) && !(file->f_mode&FMODE_WRITE))
                                return -EACCES;
 
index 203268f9231e155d72307995989feab4857defe7..8ce7f5a0800fee5ad05e23a80448932d644681d3 100644 (file)
@@ -16,6 +16,7 @@
 
 #define MAP_SHARED     0x01            /* Share changes */
 #define MAP_PRIVATE    0x02            /* Changes are private */
+#define MAP_SHARED_VALIDATE 0x03       /* share + validate extension flags */
 #define MAP_TYPE       0x0f            /* Mask for type of mapping */
 #define MAP_FIXED      0x10            /* Interpret addr exactly */
 #define MAP_ANONYMOUS  0x20            /* don't use a file */