Merge branch 'CVE-2014-7970' of git://git.kernel.org/pub/scm/linux/kernel/git/luto...
[sfrench/cifs-2.6.git] / fs / namespace.c
index 74647c2fe69ca3703c2c1492555f5c85eb73ce72..fbba8b17330d40d4daff7cf61763853cfd414395 100644 (file)
@@ -23,6 +23,7 @@
 #include <linux/proc_ns.h>
 #include <linux/magic.h>
 #include <linux/bootmem.h>
+#include <linux/task_work.h>
 #include "pnode.h"
 #include "internal.h"
 
@@ -224,6 +225,7 @@ static struct mount *alloc_vfsmnt(const char *name)
                INIT_LIST_HEAD(&mnt->mnt_share);
                INIT_LIST_HEAD(&mnt->mnt_slave_list);
                INIT_LIST_HEAD(&mnt->mnt_slave);
+               INIT_HLIST_NODE(&mnt->mnt_mp_list);
 #ifdef CONFIG_FSNOTIFY
                INIT_HLIST_HEAD(&mnt->mnt_fsnotify_marks);
 #endif
@@ -666,11 +668,45 @@ struct vfsmount *lookup_mnt(struct path *path)
        return m;
 }
 
-static struct mountpoint *new_mountpoint(struct dentry *dentry)
+/*
+ * __is_local_mountpoint - Test to see if dentry is a mountpoint in the
+ *                         current mount namespace.
+ *
+ * The common case is dentries are not mountpoints at all and that
+ * test is handled inline.  For the slow case when we are actually
+ * dealing with a mountpoint of some kind, walk through all of the
+ * mounts in the current mount namespace and test to see if the dentry
+ * is a mountpoint.
+ *
+ * The mount_hashtable is not usable in the context because we
+ * need to identify all mounts that may be in the current mount
+ * namespace not just a mount that happens to have some specified
+ * parent mount.
+ */
+bool __is_local_mountpoint(struct dentry *dentry)
+{
+       struct mnt_namespace *ns = current->nsproxy->mnt_ns;
+       struct mount *mnt;
+       bool is_covered = false;
+
+       if (!d_mountpoint(dentry))
+               goto out;
+
+       down_read(&namespace_sem);
+       list_for_each_entry(mnt, &ns->list, mnt_list) {
+               is_covered = (mnt->mnt_mountpoint == dentry);
+               if (is_covered)
+                       break;
+       }
+       up_read(&namespace_sem);
+out:
+       return is_covered;
+}
+
+static struct mountpoint *lookup_mountpoint(struct dentry *dentry)
 {
        struct hlist_head *chain = mp_hash(dentry);
        struct mountpoint *mp;
-       int ret;
 
        hlist_for_each_entry(mp, chain, m_hash) {
                if (mp->m_dentry == dentry) {
@@ -681,6 +717,14 @@ static struct mountpoint *new_mountpoint(struct dentry *dentry)
                        return mp;
                }
        }
+       return NULL;
+}
+
+static struct mountpoint *new_mountpoint(struct dentry *dentry)
+{
+       struct hlist_head *chain = mp_hash(dentry);
+       struct mountpoint *mp;
+       int ret;
 
        mp = kmalloc(sizeof(struct mountpoint), GFP_KERNEL);
        if (!mp)
@@ -695,6 +739,7 @@ static struct mountpoint *new_mountpoint(struct dentry *dentry)
        mp->m_dentry = dentry;
        mp->m_count = 1;
        hlist_add_head(&mp->m_hash, chain);
+       INIT_HLIST_HEAD(&mp->m_list);
        return mp;
 }
 
@@ -702,6 +747,7 @@ static void put_mountpoint(struct mountpoint *mp)
 {
        if (!--mp->m_count) {
                struct dentry *dentry = mp->m_dentry;
+               BUG_ON(!hlist_empty(&mp->m_list));
                spin_lock(&dentry->d_lock);
                dentry->d_flags &= ~DCACHE_MOUNTED;
                spin_unlock(&dentry->d_lock);
@@ -748,6 +794,7 @@ static void detach_mnt(struct mount *mnt, struct path *old_path)
        mnt->mnt_mountpoint = mnt->mnt.mnt_root;
        list_del_init(&mnt->mnt_child);
        hlist_del_init_rcu(&mnt->mnt_hash);
+       hlist_del_init(&mnt->mnt_mp_list);
        put_mountpoint(mnt->mnt_mp);
        mnt->mnt_mp = NULL;
 }
@@ -764,6 +811,7 @@ void mnt_set_mountpoint(struct mount *mnt,
        child_mnt->mnt_mountpoint = dget(mp->m_dentry);
        child_mnt->mnt_parent = mnt;
        child_mnt->mnt_mp = mp;
+       hlist_add_head(&child_mnt->mnt_mp_list, &mp->m_list);
 }
 
 /*
@@ -957,6 +1005,46 @@ static struct mount *clone_mnt(struct mount *old, struct dentry *root,
        return ERR_PTR(err);
 }
 
+static void cleanup_mnt(struct mount *mnt)
+{
+       /*
+        * This probably indicates that somebody messed
+        * up a mnt_want/drop_write() pair.  If this
+        * happens, the filesystem was probably unable
+        * to make r/w->r/o transitions.
+        */
+       /*
+        * The locking used to deal with mnt_count decrement provides barriers,
+        * so mnt_get_writers() below is safe.
+        */
+       WARN_ON(mnt_get_writers(mnt));
+       if (unlikely(mnt->mnt_pins.first))
+               mnt_pin_kill(mnt);
+       fsnotify_vfsmount_delete(&mnt->mnt);
+       dput(mnt->mnt.mnt_root);
+       deactivate_super(mnt->mnt.mnt_sb);
+       mnt_free_id(mnt);
+       call_rcu(&mnt->mnt_rcu, delayed_free_vfsmnt);
+}
+
+static void __cleanup_mnt(struct rcu_head *head)
+{
+       cleanup_mnt(container_of(head, struct mount, mnt_rcu));
+}
+
+static LLIST_HEAD(delayed_mntput_list);
+static void delayed_mntput(struct work_struct *unused)
+{
+       struct llist_node *node = llist_del_all(&delayed_mntput_list);
+       struct llist_node *next;
+
+       for (; node; node = next) {
+               next = llist_next(node);
+               cleanup_mnt(llist_entry(node, struct mount, mnt_llist));
+       }
+}
+static DECLARE_DELAYED_WORK(delayed_mntput_work, delayed_mntput);
+
 static void mntput_no_expire(struct mount *mnt)
 {
        rcu_read_lock();
@@ -982,24 +1070,18 @@ static void mntput_no_expire(struct mount *mnt)
        list_del(&mnt->mnt_instance);
        unlock_mount_hash();
 
-       /*
-        * This probably indicates that somebody messed
-        * up a mnt_want/drop_write() pair.  If this
-        * happens, the filesystem was probably unable
-        * to make r/w->r/o transitions.
-        */
-       /*
-        * The locking used to deal with mnt_count decrement provides barriers,
-        * so mnt_get_writers() below is safe.
-        */
-       WARN_ON(mnt_get_writers(mnt));
-       if (unlikely(mnt->mnt_pins.first))
-               mnt_pin_kill(mnt);
-       fsnotify_vfsmount_delete(&mnt->mnt);
-       dput(mnt->mnt.mnt_root);
-       deactivate_super(mnt->mnt.mnt_sb);
-       mnt_free_id(mnt);
-       call_rcu(&mnt->mnt_rcu, delayed_free_vfsmnt);
+       if (likely(!(mnt->mnt.mnt_flags & MNT_INTERNAL))) {
+               struct task_struct *task = current;
+               if (likely(!(task->flags & PF_KTHREAD))) {
+                       init_task_work(&mnt->mnt_rcu, __cleanup_mnt);
+                       if (!task_work_add(task, &mnt->mnt_rcu, true))
+                               return;
+               }
+               if (llist_add(&mnt->mnt_llist, &delayed_mntput_list))
+                       schedule_delayed_work(&delayed_mntput_work, 1);
+               return;
+       }
+       cleanup_mnt(mnt);
 }
 
 void mntput(struct vfsmount *mnt)
@@ -1272,6 +1354,7 @@ void umount_tree(struct mount *mnt, int how)
                if (how < 2)
                        p->mnt.mnt_flags |= MNT_SYNC_UMOUNT;
                if (mnt_has_parent(p)) {
+                       hlist_del_init(&p->mnt_mp_list);
                        put_mountpoint(p->mnt_mp);
                        mnt_add_count(p->mnt_parent, -1);
                        /* move the reference to mountpoint into ->mnt_ex_mountpoint */
@@ -1356,6 +1439,8 @@ static int do_umount(struct mount *mnt, int flags)
                 * Special case for "unmounting" root ...
                 * we just try to remount it readonly.
                 */
+               if (!capable(CAP_SYS_ADMIN))
+                       return -EPERM;
                down_write(&sb->s_umount);
                if (!(sb->s_flags & MS_RDONLY))
                        retval = do_remount_sb(sb, MS_RDONLY, NULL, 0);
@@ -1385,6 +1470,37 @@ static int do_umount(struct mount *mnt, int flags)
        return retval;
 }
 
+/*
+ * __detach_mounts - lazily unmount all mounts on the specified dentry
+ *
+ * During unlink, rmdir, and d_drop it is possible to loose the path
+ * to an existing mountpoint, and wind up leaking the mount.
+ * detach_mounts allows lazily unmounting those mounts instead of
+ * leaking them.
+ *
+ * The caller may hold dentry->d_inode->i_mutex.
+ */
+void __detach_mounts(struct dentry *dentry)
+{
+       struct mountpoint *mp;
+       struct mount *mnt;
+
+       namespace_lock();
+       mp = lookup_mountpoint(dentry);
+       if (!mp)
+               goto out_unlock;
+
+       lock_mount_hash();
+       while (!hlist_empty(&mp->m_list)) {
+               mnt = hlist_entry(mp->m_list.first, struct mount, mnt_mp_list);
+               umount_tree(mnt, 2);
+       }
+       unlock_mount_hash();
+       put_mountpoint(mp);
+out_unlock:
+       namespace_unlock();
+}
+
 /* 
  * Is the caller allowed to modify his namespace?
  */
@@ -1742,7 +1858,9 @@ retry:
        namespace_lock();
        mnt = lookup_mnt(path);
        if (likely(!mnt)) {
-               struct mountpoint *mp = new_mountpoint(dentry);
+               struct mountpoint *mp = lookup_mountpoint(dentry);
+               if (!mp)
+                       mp = new_mountpoint(dentry);
                if (IS_ERR(mp)) {
                        namespace_unlock();
                        mutex_unlock(&dentry->d_inode->i_mutex);
@@ -2398,21 +2516,9 @@ int copy_mount_options(const void __user * data, unsigned long *where)
        return 0;
 }
 
-int copy_mount_string(const void __user *data, char **where)
+char *copy_mount_string(const void __user *data)
 {
-       char *tmp;
-
-       if (!data) {
-               *where = NULL;
-               return 0;
-       }
-
-       tmp = strndup_user(data, PAGE_SIZE);
-       if (IS_ERR(tmp))
-               return PTR_ERR(tmp);
-
-       *where = tmp;
-       return 0;
+       return data ? strndup_user(data, PAGE_SIZE) : NULL;
 }
 
 /*
@@ -2429,7 +2535,7 @@ int copy_mount_string(const void __user *data, char **where)
  * Therefore, if this magic number is present, it carries no information
  * and must be discarded.
  */
-long do_mount(const char *dev_name, const char *dir_name,
+long do_mount(const char *dev_name, const char __user *dir_name,
                const char *type_page, unsigned long flags, void *data_page)
 {
        struct path path;
@@ -2441,15 +2547,11 @@ long do_mount(const char *dev_name, const char *dir_name,
                flags &= ~MS_MGC_MSK;
 
        /* Basic sanity checks */
-
-       if (!dir_name || !*dir_name || !memchr(dir_name, 0, PAGE_SIZE))
-               return -EINVAL;
-
        if (data_page)
                ((char *)data_page)[PAGE_SIZE - 1] = 0;
 
        /* ... and get the mountpoint */
-       retval = kern_path(dir_name, LOOKUP_FOLLOW, &path);
+       retval = user_path(dir_name, &path);
        if (retval)
                return retval;
 
@@ -2674,37 +2776,30 @@ SYSCALL_DEFINE5(mount, char __user *, dev_name, char __user *, dir_name,
 {
        int ret;
        char *kernel_type;
-       struct filename *kernel_dir;
        char *kernel_dev;
        unsigned long data_page;
 
-       ret = copy_mount_string(type, &kernel_type);
-       if (ret < 0)
+       kernel_type = copy_mount_string(type);
+       ret = PTR_ERR(kernel_type);
+       if (IS_ERR(kernel_type))
                goto out_type;
 
-       kernel_dir = getname(dir_name);
-       if (IS_ERR(kernel_dir)) {
-               ret = PTR_ERR(kernel_dir);
-               goto out_dir;
-       }
-
-       ret = copy_mount_string(dev_name, &kernel_dev);
-       if (ret < 0)
+       kernel_dev = copy_mount_string(dev_name);
+       ret = PTR_ERR(kernel_dev);
+       if (IS_ERR(kernel_dev))
                goto out_dev;
 
        ret = copy_mount_options(data, &data_page);
        if (ret < 0)
                goto out_data;
 
-       ret = do_mount(kernel_dev, kernel_dir->name, kernel_type, flags,
+       ret = do_mount(kernel_dev, dir_name, kernel_type, flags,
                (void *) data_page);
 
        free_page(data_page);
 out_data:
        kfree(kernel_dev);
 out_dev:
-       putname(kernel_dir);
-out_dir:
        kfree(kernel_type);
 out_type:
        return ret;