do_shmat(): grab shp->shm_file earlier, switch to alloc_file_clone()
authorAl Viro <viro@zeniv.linux.org.uk>
Sun, 17 Jun 2018 16:24:00 +0000 (12:24 -0400)
committerAl Viro <viro@zeniv.linux.org.uk>
Thu, 12 Jul 2018 14:04:28 +0000 (10:04 -0400)
Acked-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
ipc/shm.c

index c702abd..7c8de95 100644 (file)
--- a/ipc/shm.c
+++ b/ipc/shm.c
@@ -1354,14 +1354,13 @@ long do_shmat(int shmid, char __user *shmaddr, int shmflg,
        struct shmid_kernel *shp;
        unsigned long addr = (unsigned long)shmaddr;
        unsigned long size;
-       struct file *file;
+       struct file *file, *base;
        int    err;
        unsigned long flags = MAP_SHARED;
        unsigned long prot;
        int acc_mode;
        struct ipc_namespace *ns;
        struct shm_file_data *sfd;
-       struct path path;
        int f_flags;
        unsigned long populate = 0;
 
@@ -1435,46 +1434,44 @@ long do_shmat(int shmid, char __user *shmaddr, int shmflg,
                goto out_unlock;
        }
 
-       path = shp->shm_file->f_path;
-       path_get(&path);
+       /*
+        * We need to take a reference to the real shm file to prevent the
+        * pointer from becoming stale in cases where the lifetime of the outer
+        * file extends beyond that of the shm segment.  It's not usually
+        * possible, but it can happen during remap_file_pages() emulation as
+        * that unmaps the memory, then does ->mmap() via file reference only.
+        * We'll deny the ->mmap() if the shm segment was since removed, but to
+        * detect shm ID reuse we need to compare the file pointers.
+        */
+       base = get_file(shp->shm_file);
        shp->shm_nattch++;
-       size = i_size_read(d_inode(path.dentry));
+       size = i_size_read(file_inode(base));
        ipc_unlock_object(&shp->shm_perm);
        rcu_read_unlock();
 
        err = -ENOMEM;
        sfd = kzalloc(sizeof(*sfd), GFP_KERNEL);
        if (!sfd) {
-               path_put(&path);
+               fput(base);
                goto out_nattch;
        }
 
-       file = alloc_file(&path, f_flags,
-                         is_file_hugepages(shp->shm_file) ?
+       file = alloc_file_clone(base, f_flags,
+                         is_file_hugepages(base) ?
                                &shm_file_operations_huge :
                                &shm_file_operations);
        err = PTR_ERR(file);
        if (IS_ERR(file)) {
                kfree(sfd);
-               path_put(&path);
+               fput(base);
                goto out_nattch;
        }
 
-       file->private_data = sfd;
-       file->f_mapping = shp->shm_file->f_mapping;
        sfd->id = shp->shm_perm.id;
        sfd->ns = get_ipc_ns(ns);
-       /*
-        * We need to take a reference to the real shm file to prevent the
-        * pointer from becoming stale in cases where the lifetime of the outer
-        * file extends beyond that of the shm segment.  It's not usually
-        * possible, but it can happen during remap_file_pages() emulation as
-        * that unmaps the memory, then does ->mmap() via file reference only.
-        * We'll deny the ->mmap() if the shm segment was since removed, but to
-        * detect shm ID reuse we need to compare the file pointers.
-        */
-       sfd->file = get_file(shp->shm_file);
+       sfd->file = base;
        sfd->vm_ops = NULL;
+       file->private_data = sfd;
 
        err = security_mmap_file(file, prot, flags);
        if (err)