Skip to content

Commit b854cc6

Browse files
author
Miklos Szeredi
committed
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: 61536be ("ovl: support [S|G]ETFLAGS and FS[S|G]ETXATTR ioctls for directories") Cc: [email protected] # v5.10 Reported-by: Icenowy Zheng <[email protected]> Tested-by: Icenowy Zheng <[email protected]> Signed-off-by: Miklos Szeredi <[email protected]>
1 parent f2b00be commit b854cc6

File tree

1 file changed

+7
-16
lines changed

1 file changed

+7
-16
lines changed

fs/overlayfs/readdir.c

Lines changed: 7 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -865,7 +865,7 @@ struct file *ovl_dir_real_file(const struct file *file, bool want_upper)
865865

866866
struct ovl_dir_file *od = file->private_data;
867867
struct dentry *dentry = file->f_path.dentry;
868-
struct file *realfile = od->realfile;
868+
struct file *old, *realfile = od->realfile;
869869

870870
if (!OVL_TYPE_UPPER(ovl_path_type(dentry)))
871871
return want_upper ? NULL : realfile;
@@ -874,29 +874,20 @@ struct file *ovl_dir_real_file(const struct file *file, bool want_upper)
874874
* Need to check if we started out being a lower dir, but got copied up
875875
*/
876876
if (!od->is_upper) {
877-
struct inode *inode = file_inode(file);
878-
879877
realfile = READ_ONCE(od->upperfile);
880878
if (!realfile) {
881879
struct path upperpath;
882880

883881
ovl_path_upper(dentry, &upperpath);
884882
realfile = ovl_dir_open_realfile(file, &upperpath);
883+
if (IS_ERR(realfile))
884+
return realfile;
885885

886-
inode_lock(inode);
887-
if (!od->upperfile) {
888-
if (IS_ERR(realfile)) {
889-
inode_unlock(inode);
890-
return realfile;
891-
}
892-
smp_store_release(&od->upperfile, realfile);
893-
} else {
894-
/* somebody has beaten us to it */
895-
if (!IS_ERR(realfile))
896-
fput(realfile);
897-
realfile = od->upperfile;
886+
old = cmpxchg_release(&od->upperfile, NULL, realfile);
887+
if (old) {
888+
fput(realfile);
889+
realfile = old;
898890
}
899-
inode_unlock(inode);
900891
}
901892
}
902893

0 commit comments

Comments
 (0)