7.3-stable review patch. If anyone has any objections, please let me know. ------------------ From: Chris Mason commit e34d2ef818e4c7676d8d8e1bf43e6a1ed39efb2a upstream. An unprivileged user crashes the kernel by mounting overlayfs with the volatile option. A dentry that serves as the parent of a lookup must be a directory. ovl_create_volatile_dirty() walks work/incompat/volatile/dirty by name, and ovl_lookup_or_create() returns an existing entry whatever its type. A file or a symlink that another task creates during the mount becomes the parent of the next lookup, which calls a NULL ->lookup. An unprivileged user mounted overlayfs with "volatile" in a user namespace while a second process created a symlink named work/incompat, and the mount task oopsed on a NULL ->lookup call. Fix this by returning -ENOTDIR from ovl_lookup_or_create() when the caller asked for a directory and the existing entry is a non-dir. Fixes: c86243b090bc ("ovl: provide a mount option "volatile"") Cc: stable@vger.kernel.org Signed-off-by: Chris Mason Signed-off-by: Josef Bacik Signed-off-by: Amir Goldstein Signed-off-by: Greg Kroah-Hartman --- fs/overlayfs/super.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) --- a/fs/overlayfs/super.c +++ b/fs/overlayfs/super.c @@ -621,16 +621,21 @@ cleanup_temp: static struct dentry *ovl_lookup_or_create(struct ovl_fs *ofs, struct dentry *parent, - const char *name, umode_t mode) + const char *name, bool is_dir) { struct dentry *child; child = ovl_start_creating_upper(ofs, parent, &QSTR(name)); if (!IS_ERR(child)) { - if (!child->d_inode) + if (!child->d_inode) { child = ovl_create_real(ofs, parent, child, &QSTR(name), - OVL_CATTR(mode)); + OVL_CATTR(is_dir ? S_IFDIR : + S_IFREG)); + } else if (is_dir && !d_can_lookup(child)) { + end_creating(child); + child = ERR_PTR(-ENOTDIR); + } end_creating_keep(child); } dput(parent); @@ -652,7 +657,7 @@ static int ovl_create_volatile_dirty(str const char *const *name = volatile_path; for (ctr = ARRAY_SIZE(volatile_path); ctr; ctr--, name++) { - d = ovl_lookup_or_create(ofs, d, *name, ctr > 1 ? S_IFDIR : S_IFREG); + d = ovl_lookup_or_create(ofs, d, *name, ctr > 1); if (IS_ERR(d)) return PTR_ERR(d); }