forked from Minki/linux
ovl: avoid deadlock on directory ioctl
The function ovl_dir_real_file() currently uses the inode lock to serialize
writes to the od->upperfile field.
However, this function will get called by ovl_ioctl_set_flags(), which
utilizes the inode lock too. In this case ovl_dir_real_file() will try to
claim a lock that is owned by a function in its call stack, which won't get
released before ovl_dir_real_file() returns.
Fix by replacing the open coded compare and exchange by an explicit atomic
op.
Fixes: 61536bed21
("ovl: support [S|G]ETFLAGS and FS[S|G]ETXATTR ioctls for directories")
Cc: stable@vger.kernel.org # v5.10
Reported-by: Icenowy Zheng <icenowy@aosc.io>
Tested-by: Icenowy Zheng <icenowy@aosc.io>
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
This commit is contained in:
parent
f2b00be488
commit
b854cc659d
@ -865,7 +865,7 @@ struct file *ovl_dir_real_file(const struct file *file, bool want_upper)
|
||||
|
||||
struct ovl_dir_file *od = file->private_data;
|
||||
struct dentry *dentry = file->f_path.dentry;
|
||||
struct file *realfile = od->realfile;
|
||||
struct file *old, *realfile = od->realfile;
|
||||
|
||||
if (!OVL_TYPE_UPPER(ovl_path_type(dentry)))
|
||||
return want_upper ? NULL : realfile;
|
||||
@ -874,29 +874,20 @@ struct file *ovl_dir_real_file(const struct file *file, bool want_upper)
|
||||
* Need to check if we started out being a lower dir, but got copied up
|
||||
*/
|
||||
if (!od->is_upper) {
|
||||
struct inode *inode = file_inode(file);
|
||||
|
||||
realfile = READ_ONCE(od->upperfile);
|
||||
if (!realfile) {
|
||||
struct path upperpath;
|
||||
|
||||
ovl_path_upper(dentry, &upperpath);
|
||||
realfile = ovl_dir_open_realfile(file, &upperpath);
|
||||
if (IS_ERR(realfile))
|
||||
return realfile;
|
||||
|
||||
inode_lock(inode);
|
||||
if (!od->upperfile) {
|
||||
if (IS_ERR(realfile)) {
|
||||
inode_unlock(inode);
|
||||
return realfile;
|
||||
}
|
||||
smp_store_release(&od->upperfile, realfile);
|
||||
} else {
|
||||
/* somebody has beaten us to it */
|
||||
if (!IS_ERR(realfile))
|
||||
fput(realfile);
|
||||
realfile = od->upperfile;
|
||||
old = cmpxchg_release(&od->upperfile, NULL, realfile);
|
||||
if (old) {
|
||||
fput(realfile);
|
||||
realfile = old;
|
||||
}
|
||||
inode_unlock(inode);
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user