Skip to content
Snippets Groups Projects
Commit b854cc65 authored by Miklos Szeredi's avatar Miklos Szeredi
Browse files

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: 61536bed ("ovl: support [S|G]ETFLAGS and FS[S|G]ETXATTR ioctls for directories")
Cc: stable@vger.kernel.org # v5.10
Reported-by: default avatarIcenowy Zheng <icenowy@aosc.io>
Tested-by: default avatarIcenowy Zheng <icenowy@aosc.io>
Signed-off-by: default avatarMiklos Szeredi <mszeredi@redhat.com>
parent f2b00be4
No related branches found
No related tags found
No related merge requests found
...@@ -865,7 +865,7 @@ struct file *ovl_dir_real_file(const struct file *file, bool want_upper) ...@@ -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 ovl_dir_file *od = file->private_data;
struct dentry *dentry = file->f_path.dentry; 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))) if (!OVL_TYPE_UPPER(ovl_path_type(dentry)))
return want_upper ? NULL : realfile; return want_upper ? NULL : realfile;
...@@ -874,29 +874,20 @@ struct file *ovl_dir_real_file(const struct file *file, bool want_upper) ...@@ -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 * Need to check if we started out being a lower dir, but got copied up
*/ */
if (!od->is_upper) { if (!od->is_upper) {
struct inode *inode = file_inode(file);
realfile = READ_ONCE(od->upperfile); realfile = READ_ONCE(od->upperfile);
if (!realfile) { if (!realfile) {
struct path upperpath; struct path upperpath;
ovl_path_upper(dentry, &upperpath); ovl_path_upper(dentry, &upperpath);
realfile = ovl_dir_open_realfile(file, &upperpath); realfile = ovl_dir_open_realfile(file, &upperpath);
if (IS_ERR(realfile))
return realfile;
inode_lock(inode); old = cmpxchg_release(&od->upperfile, NULL, realfile);
if (!od->upperfile) { if (old) {
if (IS_ERR(realfile)) { fput(realfile);
inode_unlock(inode); realfile = old;
return realfile;
}
smp_store_release(&od->upperfile, realfile);
} else {
/* somebody has beaten us to it */
if (!IS_ERR(realfile))
fput(realfile);
realfile = od->upperfile;
} }
inode_unlock(inode);
} }
} }
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment