|
@@ -20,6 +20,7 @@
|
|
|
#include <linux/fs_struct.h> /* get_fs_root et.al. */
|
|
|
#include <linux/fsnotify.h> /* fsnotify_vfsmount_delete */
|
|
|
#include <linux/uaccess.h>
|
|
|
+#include <linux/proc_fs.h>
|
|
|
#include "pnode.h"
|
|
|
#include "internal.h"
|
|
|
|
|
@@ -1308,6 +1309,26 @@ static int mount_is_safe(struct path *path)
|
|
|
#endif
|
|
|
}
|
|
|
|
|
|
+static bool mnt_ns_loop(struct path *path)
|
|
|
+{
|
|
|
+ /* Could bind mounting the mount namespace inode cause a
|
|
|
+ * mount namespace loop?
|
|
|
+ */
|
|
|
+ struct inode *inode = path->dentry->d_inode;
|
|
|
+ struct proc_inode *ei;
|
|
|
+ struct mnt_namespace *mnt_ns;
|
|
|
+
|
|
|
+ if (!proc_ns_inode(inode))
|
|
|
+ return false;
|
|
|
+
|
|
|
+ ei = PROC_I(inode);
|
|
|
+ if (ei->ns_ops != &mntns_operations)
|
|
|
+ return false;
|
|
|
+
|
|
|
+ mnt_ns = ei->ns;
|
|
|
+ return current->nsproxy->mnt_ns->seq >= mnt_ns->seq;
|
|
|
+}
|
|
|
+
|
|
|
struct mount *copy_tree(struct mount *mnt, struct dentry *dentry,
|
|
|
int flag)
|
|
|
{
|
|
@@ -1655,6 +1676,10 @@ static int do_loopback(struct path *path, const char *old_name,
|
|
|
if (err)
|
|
|
return err;
|
|
|
|
|
|
+ err = -EINVAL;
|
|
|
+ if (mnt_ns_loop(&old_path))
|
|
|
+ goto out;
|
|
|
+
|
|
|
err = lock_mount(path);
|
|
|
if (err)
|
|
|
goto out;
|
|
@@ -2261,6 +2286,15 @@ dput_out:
|
|
|
return retval;
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * Assign a sequence number so we can detect when we attempt to bind
|
|
|
+ * mount a reference to an older mount namespace into the current
|
|
|
+ * mount namespace, preventing reference counting loops. A 64bit
|
|
|
+ * number incrementing at 10Ghz will take 12,427 years to wrap which
|
|
|
+ * is effectively never, so we can ignore the possibility.
|
|
|
+ */
|
|
|
+static atomic64_t mnt_ns_seq = ATOMIC64_INIT(1);
|
|
|
+
|
|
|
static struct mnt_namespace *alloc_mnt_ns(void)
|
|
|
{
|
|
|
struct mnt_namespace *new_ns;
|
|
@@ -2268,6 +2302,7 @@ static struct mnt_namespace *alloc_mnt_ns(void)
|
|
|
new_ns = kmalloc(sizeof(struct mnt_namespace), GFP_KERNEL);
|
|
|
if (!new_ns)
|
|
|
return ERR_PTR(-ENOMEM);
|
|
|
+ new_ns->seq = atomic64_add_return(1, &mnt_ns_seq);
|
|
|
atomic_set(&new_ns->count, 1);
|
|
|
new_ns->root = NULL;
|
|
|
INIT_LIST_HEAD(&new_ns->list);
|
|
@@ -2681,3 +2716,63 @@ bool our_mnt(struct vfsmount *mnt)
|
|
|
{
|
|
|
return check_mnt(real_mount(mnt));
|
|
|
}
|
|
|
+
|
|
|
+static void *mntns_get(struct task_struct *task)
|
|
|
+{
|
|
|
+ struct mnt_namespace *ns = NULL;
|
|
|
+ struct nsproxy *nsproxy;
|
|
|
+
|
|
|
+ rcu_read_lock();
|
|
|
+ nsproxy = task_nsproxy(task);
|
|
|
+ if (nsproxy) {
|
|
|
+ ns = nsproxy->mnt_ns;
|
|
|
+ get_mnt_ns(ns);
|
|
|
+ }
|
|
|
+ rcu_read_unlock();
|
|
|
+
|
|
|
+ return ns;
|
|
|
+}
|
|
|
+
|
|
|
+static void mntns_put(void *ns)
|
|
|
+{
|
|
|
+ put_mnt_ns(ns);
|
|
|
+}
|
|
|
+
|
|
|
+static int mntns_install(struct nsproxy *nsproxy, void *ns)
|
|
|
+{
|
|
|
+ struct fs_struct *fs = current->fs;
|
|
|
+ struct mnt_namespace *mnt_ns = ns;
|
|
|
+ struct path root;
|
|
|
+
|
|
|
+ if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_CHROOT))
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ if (fs->users != 1)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ get_mnt_ns(mnt_ns);
|
|
|
+ put_mnt_ns(nsproxy->mnt_ns);
|
|
|
+ nsproxy->mnt_ns = mnt_ns;
|
|
|
+
|
|
|
+ /* Find the root */
|
|
|
+ root.mnt = &mnt_ns->root->mnt;
|
|
|
+ root.dentry = mnt_ns->root->mnt.mnt_root;
|
|
|
+ path_get(&root);
|
|
|
+ while(d_mountpoint(root.dentry) && follow_down_one(&root))
|
|
|
+ ;
|
|
|
+
|
|
|
+ /* Update the pwd and root */
|
|
|
+ set_fs_pwd(fs, &root);
|
|
|
+ set_fs_root(fs, &root);
|
|
|
+
|
|
|
+ path_put(&root);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+const struct proc_ns_operations mntns_operations = {
|
|
|
+ .name = "mnt",
|
|
|
+ .type = CLONE_NEWNS,
|
|
|
+ .get = mntns_get,
|
|
|
+ .put = mntns_put,
|
|
|
+ .install = mntns_install,
|
|
|
+};
|