Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

separate copying and locking mount tree on cross-userns copies

Rather than having propagate_mnt() check doing unprivileged copies,
lock them before commit_tree().

Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>

Al Viro 3bd045cc 6d7fbce7

+38 -29
+37 -22
fs/namespace.c
··· 1013 1013 1014 1014 mnt->mnt.mnt_flags = old->mnt.mnt_flags; 1015 1015 mnt->mnt.mnt_flags &= ~(MNT_WRITE_HOLD|MNT_MARKED|MNT_INTERNAL); 1016 - /* Don't allow unprivileged users to change mount flags */ 1017 - if (flag & CL_UNPRIVILEGED) { 1018 - mnt->mnt.mnt_flags |= MNT_LOCK_ATIME; 1019 - 1020 - if (mnt->mnt.mnt_flags & MNT_READONLY) 1021 - mnt->mnt.mnt_flags |= MNT_LOCK_READONLY; 1022 - 1023 - if (mnt->mnt.mnt_flags & MNT_NODEV) 1024 - mnt->mnt.mnt_flags |= MNT_LOCK_NODEV; 1025 - 1026 - if (mnt->mnt.mnt_flags & MNT_NOSUID) 1027 - mnt->mnt.mnt_flags |= MNT_LOCK_NOSUID; 1028 - 1029 - if (mnt->mnt.mnt_flags & MNT_NOEXEC) 1030 - mnt->mnt.mnt_flags |= MNT_LOCK_NOEXEC; 1031 - } 1032 - 1033 - /* Don't allow unprivileged users to reveal what is under a mount */ 1034 - if ((flag & CL_UNPRIVILEGED) && 1035 - (!(flag & CL_EXPIRE) || list_empty(&old->mnt_expire))) 1036 - mnt->mnt.mnt_flags |= MNT_LOCKED; 1037 1016 1038 1017 atomic_inc(&sb->s_active); 1039 1018 mnt->mnt.mnt_sb = sb; ··· 1816 1837 return 0; 1817 1838 } 1818 1839 1840 + static void lock_mnt_tree(struct mount *mnt) 1841 + { 1842 + struct mount *p; 1843 + 1844 + for (p = mnt; p; p = next_mnt(p, mnt)) { 1845 + int flags = p->mnt.mnt_flags; 1846 + /* Don't allow unprivileged users to change mount flags */ 1847 + flags |= MNT_LOCK_ATIME; 1848 + 1849 + if (flags & MNT_READONLY) 1850 + flags |= MNT_LOCK_READONLY; 1851 + 1852 + if (flags & MNT_NODEV) 1853 + flags |= MNT_LOCK_NODEV; 1854 + 1855 + if (flags & MNT_NOSUID) 1856 + flags |= MNT_LOCK_NOSUID; 1857 + 1858 + if (flags & MNT_NOEXEC) 1859 + flags |= MNT_LOCK_NOEXEC; 1860 + /* Don't allow unprivileged users to reveal what is under a mount */ 1861 + if (list_empty(&p->mnt_expire)) 1862 + flags |= MNT_LOCKED; 1863 + p->mnt.mnt_flags = flags; 1864 + } 1865 + } 1866 + 1819 1867 static void cleanup_group_ids(struct mount *mnt, struct mount *end) 1820 1868 { 1821 1869 struct mount *p; ··· 1960 1954 struct mountpoint *dest_mp, 1961 1955 struct path *parent_path) 1962 1956 { 1957 + struct user_namespace *user_ns = current->nsproxy->mnt_ns->user_ns; 1963 1958 HLIST_HEAD(tree_list); 1964 1959 struct mnt_namespace *ns = dest_mnt->mnt_ns; 1965 1960 struct mountpoint *smp; ··· 2011 2004 child->mnt_mountpoint); 2012 2005 if (q) 2013 2006 mnt_change_mountpoint(child, smp, q); 2007 + /* Notice when we are propagating across user namespaces */ 2008 + if (child->mnt_parent->mnt_ns->user_ns != user_ns) 2009 + lock_mnt_tree(child); 2014 2010 commit_tree(child); 2015 2011 } 2016 2012 put_mountpoint(smp); ··· 2951 2941 /* First pass: copy the tree topology */ 2952 2942 copy_flags = CL_COPY_UNBINDABLE | CL_EXPIRE; 2953 2943 if (user_ns != ns->user_ns) 2954 - copy_flags |= CL_SHARED_TO_SLAVE | CL_UNPRIVILEGED; 2944 + copy_flags |= CL_SHARED_TO_SLAVE; 2955 2945 new = copy_tree(old, old->mnt.mnt_root, copy_flags); 2956 2946 if (IS_ERR(new)) { 2957 2947 namespace_unlock(); 2958 2948 free_mnt_ns(new_ns); 2959 2949 return ERR_CAST(new); 2950 + } 2951 + if (user_ns != ns->user_ns) { 2952 + lock_mount_hash(); 2953 + lock_mnt_tree(new); 2954 + unlock_mount_hash(); 2960 2955 } 2961 2956 new_ns->root = new; 2962 2957 list_add_tail(&new_ns->list, &new->mnt_list);
-5
fs/pnode.c
··· 214 214 } 215 215 216 216 /* all accesses are serialized by namespace_sem */ 217 - static struct user_namespace *user_ns; 218 217 static struct mount *last_dest, *first_source, *last_source, *dest_master; 219 218 static struct mountpoint *mp; 220 219 static struct hlist_head *list; ··· 259 260 type |= CL_MAKE_SHARED; 260 261 } 261 262 262 - /* Notice when we are propagating across user namespaces */ 263 - if (m->mnt_ns->user_ns != user_ns) 264 - type |= CL_UNPRIVILEGED; 265 263 child = copy_tree(last_source, last_source->mnt.mnt_root, type); 266 264 if (IS_ERR(child)) 267 265 return PTR_ERR(child); ··· 299 303 * propagate_one(); everything is serialized by namespace_sem, 300 304 * so globals will do just fine. 301 305 */ 302 - user_ns = current->nsproxy->mnt_ns->user_ns; 303 306 last_dest = dest_mnt; 304 307 first_source = source_mnt; 305 308 last_source = source_mnt;
+1 -2
fs/pnode.h
··· 27 27 #define CL_MAKE_SHARED 0x08 28 28 #define CL_PRIVATE 0x10 29 29 #define CL_SHARED_TO_SLAVE 0x20 30 - #define CL_UNPRIVILEGED 0x40 31 - #define CL_COPY_MNT_NS_FILE 0x80 30 + #define CL_COPY_MNT_NS_FILE 0x40 32 31 33 32 #define CL_COPY_ALL (CL_COPY_UNBINDABLE | CL_COPY_MNT_NS_FILE) 34 33