overlayfs fixes for 5.14-rc6
-----BEGIN PGP SIGNATURE----- iHUEABYIAB0WIQSQHSd0lITzzeNWNm3h3BK/laaZPAUCYRKSDgAKCRDh3BK/laaZ PKSNAQCd1yGLShL44sI5lCFnGjwHGCXdfU5b8sIxNBy5DOWvTwD/edF4eUJzyME+ mZ4AwnX70N2eHJCFH/uodL0Y9Sf3egM= =zUIV -----END PGP SIGNATURE----- Merge tag 'ovl-fixes-5.14-rc6-v2' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs Pull overlayfs fixes from Miklos Szeredi: "Fix several bugs in overlayfs" * tag 'ovl-fixes-5.14-rc6-v2' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs: ovl: prevent private clone if bind mount is not allowed ovl: fix uninitialized pointer read in ovl_lookup_real_one() ovl: fix deadlock in splice write ovl: skip stale entries in merge dir cache iteration
This commit is contained in:
commit
b3f0ccc59c
|
@ -1938,6 +1938,20 @@ void drop_collected_mounts(struct vfsmount *mnt)
|
|||
namespace_unlock();
|
||||
}
|
||||
|
||||
static bool has_locked_children(struct mount *mnt, struct dentry *dentry)
|
||||
{
|
||||
struct mount *child;
|
||||
|
||||
list_for_each_entry(child, &mnt->mnt_mounts, mnt_child) {
|
||||
if (!is_subdir(child->mnt_mountpoint, dentry))
|
||||
continue;
|
||||
|
||||
if (child->mnt.mnt_flags & MNT_LOCKED)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* clone_private_mount - create a private clone of a path
|
||||
* @path: path to clone
|
||||
|
@ -1953,10 +1967,19 @@ struct vfsmount *clone_private_mount(const struct path *path)
|
|||
struct mount *old_mnt = real_mount(path->mnt);
|
||||
struct mount *new_mnt;
|
||||
|
||||
down_read(&namespace_sem);
|
||||
if (IS_MNT_UNBINDABLE(old_mnt))
|
||||
return ERR_PTR(-EINVAL);
|
||||
goto invalid;
|
||||
|
||||
if (!check_mnt(old_mnt))
|
||||
goto invalid;
|
||||
|
||||
if (has_locked_children(old_mnt, path->dentry))
|
||||
goto invalid;
|
||||
|
||||
new_mnt = clone_mnt(old_mnt, path->dentry, CL_PRIVATE);
|
||||
up_read(&namespace_sem);
|
||||
|
||||
if (IS_ERR(new_mnt))
|
||||
return ERR_CAST(new_mnt);
|
||||
|
||||
|
@ -1964,6 +1987,10 @@ struct vfsmount *clone_private_mount(const struct path *path)
|
|||
new_mnt->mnt_ns = MNT_NS_INTERNAL;
|
||||
|
||||
return &new_mnt->mnt;
|
||||
|
||||
invalid:
|
||||
up_read(&namespace_sem);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(clone_private_mount);
|
||||
|
||||
|
@ -2315,19 +2342,6 @@ static int do_change_type(struct path *path, int ms_flags)
|
|||
return err;
|
||||
}
|
||||
|
||||
static bool has_locked_children(struct mount *mnt, struct dentry *dentry)
|
||||
{
|
||||
struct mount *child;
|
||||
list_for_each_entry(child, &mnt->mnt_mounts, mnt_child) {
|
||||
if (!is_subdir(child->mnt_mountpoint, dentry))
|
||||
continue;
|
||||
|
||||
if (child->mnt.mnt_flags & MNT_LOCKED)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static struct mount *__do_loopback(struct path *old_path, int recurse)
|
||||
{
|
||||
struct mount *mnt = ERR_PTR(-EINVAL), *old = real_mount(old_path->mnt);
|
||||
|
|
|
@ -392,6 +392,7 @@ static struct dentry *ovl_lookup_real_one(struct dentry *connected,
|
|||
*/
|
||||
take_dentry_name_snapshot(&name, real);
|
||||
this = lookup_one_len(name.name.name, connected, name.name.len);
|
||||
release_dentry_name_snapshot(&name);
|
||||
err = PTR_ERR(this);
|
||||
if (IS_ERR(this)) {
|
||||
goto fail;
|
||||
|
@ -406,7 +407,6 @@ static struct dentry *ovl_lookup_real_one(struct dentry *connected,
|
|||
}
|
||||
|
||||
out:
|
||||
release_dentry_name_snapshot(&name);
|
||||
dput(parent);
|
||||
inode_unlock(dir);
|
||||
return this;
|
||||
|
|
|
@ -392,6 +392,51 @@ static ssize_t ovl_write_iter(struct kiocb *iocb, struct iov_iter *iter)
|
|||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Calling iter_file_splice_write() directly from overlay's f_op may deadlock
|
||||
* due to lock order inversion between pipe->mutex in iter_file_splice_write()
|
||||
* and file_start_write(real.file) in ovl_write_iter().
|
||||
*
|
||||
* So do everything ovl_write_iter() does and call iter_file_splice_write() on
|
||||
* the real file.
|
||||
*/
|
||||
static ssize_t ovl_splice_write(struct pipe_inode_info *pipe, struct file *out,
|
||||
loff_t *ppos, size_t len, unsigned int flags)
|
||||
{
|
||||
struct fd real;
|
||||
const struct cred *old_cred;
|
||||
struct inode *inode = file_inode(out);
|
||||
struct inode *realinode = ovl_inode_real(inode);
|
||||
ssize_t ret;
|
||||
|
||||
inode_lock(inode);
|
||||
/* Update mode */
|
||||
ovl_copyattr(realinode, inode);
|
||||
ret = file_remove_privs(out);
|
||||
if (ret)
|
||||
goto out_unlock;
|
||||
|
||||
ret = ovl_real_fdget(out, &real);
|
||||
if (ret)
|
||||
goto out_unlock;
|
||||
|
||||
old_cred = ovl_override_creds(inode->i_sb);
|
||||
file_start_write(real.file);
|
||||
|
||||
ret = iter_file_splice_write(pipe, real.file, ppos, len, flags);
|
||||
|
||||
file_end_write(real.file);
|
||||
/* Update size */
|
||||
ovl_copyattr(realinode, inode);
|
||||
revert_creds(old_cred);
|
||||
fdput(real);
|
||||
|
||||
out_unlock:
|
||||
inode_unlock(inode);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ovl_fsync(struct file *file, loff_t start, loff_t end, int datasync)
|
||||
{
|
||||
struct fd real;
|
||||
|
@ -603,7 +648,7 @@ const struct file_operations ovl_file_operations = {
|
|||
.fadvise = ovl_fadvise,
|
||||
.flush = ovl_flush,
|
||||
.splice_read = generic_file_splice_read,
|
||||
.splice_write = iter_file_splice_write,
|
||||
.splice_write = ovl_splice_write,
|
||||
|
||||
.copy_file_range = ovl_copy_file_range,
|
||||
.remap_file_range = ovl_remap_file_range,
|
||||
|
|
|
@ -481,6 +481,8 @@ static int ovl_cache_update_ino(struct path *path, struct ovl_cache_entry *p)
|
|||
}
|
||||
this = lookup_one_len(p->name, dir, p->len);
|
||||
if (IS_ERR_OR_NULL(this) || !this->d_inode) {
|
||||
/* Mark a stale entry */
|
||||
p->is_whiteout = true;
|
||||
if (IS_ERR(this)) {
|
||||
err = PTR_ERR(this);
|
||||
this = NULL;
|
||||
|
@ -776,6 +778,9 @@ static int ovl_iterate(struct file *file, struct dir_context *ctx)
|
|||
if (err)
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
/* ovl_cache_update_ino() sets is_whiteout on stale entry */
|
||||
if (!p->is_whiteout) {
|
||||
if (!dir_emit(ctx, p->name, p->len, p->ino, p->type))
|
||||
break;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue