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

Configure Feed

Select the types of activity you want to include in your feed.

mount: always duplicate mount

In the OPEN_TREE_NAMESPACE path vfs_open_tree() resolves a path via
filename_lookup() without holding namespace_lock. Between the lookup
and create_new_namespace() acquiring namespace_lock via
LOCK_MOUNT_EXACT_COPY() another thread can unmount the mount, setting
mnt->mnt_ns to NULL.

When create_new_namespace() then checks !mnt->mnt_ns it incorrectly
takes the swap-and-mntget path that was designed for fsmount()'s
detached mounts. This reuses a mount whose mnt_mp_list is in an
inconsistent state from the concurrent unmount, causing a general
protection fault in __umount_mnt() -> hlist_del_init(&mnt->mnt_mp_list)
during namespace teardown.

Remove the !mnt->mnt_ns special case entirely. Instead, always
duplicate the mount:

- For OPEN_TREE_NAMESPACE use __do_loopback() which will properly
clone the mount or reject it via may_copy_tree() if it was
unmounted in the race window.
- For fsmount() use clone_mnt() directly (via the new MOUNT_COPY_NEW
flag) since the mount is freshly created by vfs_create_mount() and
not in any namespace so __do_loopback()'s IS_MNT_UNBINDABLE,
may_copy_tree, and __has_locked_children checks don't apply.

Reported-by: syzbot+e4470cc28308f2081ec8@syzkaller.appspotmail.com
Signed-off-by: Christian Brauner <brauner@kernel.org>

+15 -21
+15 -21
fs/namespace.c
··· 3086 3086 return file; 3087 3087 } 3088 3088 3089 + enum mount_copy_flags_t { 3090 + MOUNT_COPY_RECURSIVE = (1 << 0), 3091 + MOUNT_COPY_NEW = (1 << 1), 3092 + }; 3093 + 3089 3094 static struct mnt_namespace *create_new_namespace(struct path *path, 3090 - bool recurse) 3095 + enum mount_copy_flags_t flags) 3091 3096 { 3092 3097 struct mnt_namespace *ns = current->nsproxy->mnt_ns; 3093 3098 struct user_namespace *user_ns = current_user_ns(); ··· 3101 3096 struct path to_path; 3102 3097 struct mount *mnt; 3103 3098 unsigned int copy_flags = 0; 3104 - bool locked = false; 3099 + bool locked = false, recurse = flags & MOUNT_COPY_RECURSIVE; 3105 3100 3106 3101 if (user_ns != ns->user_ns) 3107 3102 copy_flags |= CL_SLAVE; ··· 3140 3135 * the restrictions of creating detached bind-mounts. It has a 3141 3136 * lot saner and simpler semantics. 3142 3137 */ 3143 - mnt = real_mount(path->mnt); 3144 - if (!mnt->mnt_ns) { 3145 - /* 3146 - * If we're moving into a new mount namespace via 3147 - * fsmount() swap the mount ids so the nullfs mount id 3148 - * is the lowest in the mount namespace avoiding another 3149 - * useless copy. This is fine we're not attached to any 3150 - * mount namespace so the mount ids are pure decoration 3151 - * at that point. 3152 - */ 3153 - swap(mnt->mnt_id_unique, new_ns_root->mnt_id_unique); 3154 - swap(mnt->mnt_id, new_ns_root->mnt_id); 3155 - mntget(&mnt->mnt); 3156 - } else { 3138 + if (flags & MOUNT_COPY_NEW) 3139 + mnt = clone_mnt(real_mount(path->mnt), path->dentry, copy_flags); 3140 + else 3157 3141 mnt = __do_loopback(path, recurse, copy_flags); 3158 - } 3159 3142 scoped_guard(mount_writer) { 3160 3143 if (IS_ERR(mnt)) { 3161 3144 emptied_ns = new_ns; ··· 3172 3179 return new_ns; 3173 3180 } 3174 3181 3175 - static struct file *open_new_namespace(struct path *path, bool recurse) 3182 + static struct file *open_new_namespace(struct path *path, 3183 + enum mount_copy_flags_t flags) 3176 3184 { 3177 3185 struct mnt_namespace *new_ns; 3178 3186 3179 - new_ns = create_new_namespace(path, recurse); 3187 + new_ns = create_new_namespace(path, flags); 3180 3188 if (IS_ERR(new_ns)) 3181 3189 return ERR_CAST(new_ns); 3182 3190 return open_namespace_file(to_ns_common(new_ns)); ··· 3226 3232 return ERR_PTR(ret); 3227 3233 3228 3234 if (flags & OPEN_TREE_NAMESPACE) 3229 - return open_new_namespace(&path, (flags & AT_RECURSIVE)); 3235 + return open_new_namespace(&path, (flags & AT_RECURSIVE) ? MOUNT_COPY_RECURSIVE : 0); 3230 3236 3231 3237 if (flags & OPEN_TREE_CLONE) 3232 3238 return open_detached_copy(&path, flags); ··· 4513 4519 4514 4520 if (flags & FSMOUNT_NAMESPACE) 4515 4521 return FD_ADD((flags & FSMOUNT_CLOEXEC) ? O_CLOEXEC : 0, 4516 - open_new_namespace(&new_path, 0)); 4522 + open_new_namespace(&new_path, MOUNT_COPY_NEW)); 4517 4523 4518 4524 ns = alloc_mnt_ns(current->nsproxy->mnt_ns->user_ns, true); 4519 4525 if (IS_ERR(ns))