The mmap syscall suffers from the ABI anti-pattern of not validating
unknown flags. However, proposals like MAP_SYNC and MAP_DIRECT need a
mechanism to define new behavior that is known to fail on older kernels
without the feature. Use the fact that specifying MAP_SHARED and
MAP_PRIVATE at the same time is invalid as a cute hack to allow a new
set of validated flags to be introduced.

This also introduces the ->fmmap() file operation that is ->mmap() plus
flags. Each ->fmmap() implementation must fail requests when a locally
unsupported flag is specified.

Cc: Jan Kara <j...@suse.cz>
Cc: Arnd Bergmann <a...@arndb.de>
Cc: Andrew Morton <a...@linux-foundation.org>
Suggested-by: Christoph Hellwig <h...@lst.de>
Signed-off-by: Dan Williams <dan.j.willi...@intel.com>
---
 include/linux/fs.h                     |    7 +++++++
 include/linux/mm.h                     |    2 +-
 include/linux/mman.h                   |    3 +++
 include/uapi/asm-generic/mman-common.h |    1 +
 mm/mmap.c                              |   20 +++++++++++++++++---
 5 files changed, 29 insertions(+), 4 deletions(-)

diff --git a/include/linux/fs.h b/include/linux/fs.h
index 1104e5df39ef..bbe755d0caee 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -1674,6 +1674,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 *);
+       int (*fmmap) (struct file *, struct vm_area_struct *, unsigned long);
        int (*open) (struct inode *, struct file *);
        int (*flush) (struct file *, fl_owner_t id);
        int (*release) (struct inode *, struct file *);
@@ -1748,6 +1749,12 @@ static inline int call_mmap(struct file *file, struct 
vm_area_struct *vma)
        return file->f_op->mmap(file, vma);
 }
 
+static inline int call_fmmap(struct file *file, struct vm_area_struct *vma,
+               unsigned long flags)
+{
+       return file->f_op->fmmap(file, vma, flags);
+}
+
 ssize_t rw_copy_check_uvector(int type, const struct iovec __user * uvector,
                              unsigned long nr_segs, unsigned long fast_segs,
                              struct iovec *fast_pointer,
diff --git a/include/linux/mm.h b/include/linux/mm.h
index 46b9ac5e8569..49eef48da4b7 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -2090,7 +2090,7 @@ 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);
+       struct list_head *uf, unsigned long flags);
 extern unsigned long do_mmap(struct file *file, unsigned long addr,
        unsigned long len, unsigned long prot, unsigned long flags,
        vm_flags_t vm_flags, unsigned long pgoff, unsigned long *populate,
diff --git a/include/linux/mman.h b/include/linux/mman.h
index c8367041fafd..73d4ac7e7136 100644
--- a/include/linux/mman.h
+++ b/include/linux/mman.h
@@ -7,6 +7,9 @@
 #include <linux/atomic.h>
 #include <uapi/linux/mman.h>
 
+/* the MAP_VALIDATE set of supported flags */
+#define        MAP_SUPPORTED_MASK (0)
+
 extern int sysctl_overcommit_memory;
 extern int sysctl_overcommit_ratio;
 extern unsigned long sysctl_overcommit_kbytes;
diff --git a/include/uapi/asm-generic/mman-common.h 
b/include/uapi/asm-generic/mman-common.h
index 8c27db0c5c08..8bf8c7828275 100644
--- a/include/uapi/asm-generic/mman-common.h
+++ b/include/uapi/asm-generic/mman-common.h
@@ -24,6 +24,7 @@
 #else
 # define MAP_UNINITIALIZED 0x0         /* Don't support this flag */
 #endif
+#define MAP_VALIDATE (MAP_SHARED|MAP_PRIVATE) /* mechanism to define new 
shared semantics */
 
 /*
  * Flags for mlock
diff --git a/mm/mmap.c b/mm/mmap.c
index f19efcf75418..d2919a9e25bf 100644
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -1388,6 +1388,12 @@ unsigned long do_mmap(struct file *file, unsigned long 
addr,
                struct inode *inode = file_inode(file);
 
                switch (flags & MAP_TYPE) {
+               case MAP_VALIDATE:
+                       if (flags & ~(MAP_SUPPORTED_MASK | MAP_VALIDATE))
+                               return -EINVAL;
+                       if (!file->f_op->fmmap)
+                               return -EOPNOTSUPP;
+                       /* fall through */
                case MAP_SHARED:
                        if ((prot&PROT_WRITE) && !(file->f_mode&FMODE_WRITE))
                                return -EACCES;
@@ -1464,7 +1470,12 @@ unsigned long do_mmap(struct file *file, unsigned long 
addr,
                        vm_flags |= VM_NORESERVE;
        }
 
-       addr = mmap_region(file, addr, len, vm_flags, pgoff, uf);
+       if ((flags & MAP_VALIDATE) == MAP_VALIDATE)
+               flags &= MAP_SUPPORTED_MASK;
+       else
+               flags = 0;
+
+       addr = mmap_region(file, addr, len, vm_flags, pgoff, uf, flags);
        if (!IS_ERR_VALUE(addr) &&
            ((vm_flags & VM_LOCKED) ||
             (flags & (MAP_POPULATE | MAP_NONBLOCK)) == MAP_POPULATE))
@@ -1601,7 +1612,7 @@ static inline int accountable_mapping(struct file *file, 
vm_flags_t vm_flags)
 
 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)
+               struct list_head *uf, unsigned long flags)
 {
        struct mm_struct *mm = current->mm;
        struct vm_area_struct *vma, *prev;
@@ -1686,7 +1697,10 @@ unsigned long mmap_region(struct file *file, unsigned 
long addr,
                 * new file must not have been exposed to user-space, yet.
                 */
                vma->vm_file = get_file(file);
-               error = call_mmap(file, vma);
+               if (flags)
+                       error = call_fmmap(file, vma, flags);
+               else
+                       error = call_mmap(file, vma);
                if (error)
                        goto unmap_and_free_vma;
 

Reply via email to