于 2021年1月4日 GMT+08:00 下午3:28:35, Icenowy Zheng <icenowy@xxxxxxx> 写到: >The function ovl_dir_real_file() currently uses the semaphore of the >inode to synchronize write to the upperfile cache field. > >However, this function will get called by ovl_ioctl_set_flags(), which >utilizes the inode semaphore 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. > >Define a dedicated semaphore for the upperfile cache, so that the >deadlock won't happen. > >Fixes: 61536bed2149 ("ovl: support [S|G]ETFLAGS and FS[S|G]ETXATTR >ioctls for directories") >Cc: stable@xxxxxxxxxxxxxxx # v5.10 >Signed-off-by: Icenowy Zheng <icenowy@xxxxxxx> >--- Sorry for lack of changelog. A missing replacement of inode_lock() is added in v2. > fs/overlayfs/readdir.c | 10 +++++----- > 1 file changed, 5 insertions(+), 5 deletions(-) > >diff --git a/fs/overlayfs/readdir.c b/fs/overlayfs/readdir.c >index 01620ebae1bd..fa1844ff8db4 100644 >--- a/fs/overlayfs/readdir.c >+++ b/fs/overlayfs/readdir.c >@@ -56,6 +56,7 @@ struct ovl_dir_file { > struct list_head *cursor; > struct file *realfile; > struct file *upperfile; >+ struct semaphore upperfile_sem; > }; > >static struct ovl_cache_entry *ovl_cache_entry_from_node(struct rb_node >*n) >@@ -874,8 +875,6 @@ 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; >@@ -883,10 +882,10 @@ struct file *ovl_dir_real_file(const struct file >*file, bool want_upper) > ovl_path_upper(dentry, &upperpath); > realfile = ovl_dir_open_realfile(file, &upperpath); > >- inode_lock(inode); >+ down(&od->upperfile_sem); > if (!od->upperfile) { > if (IS_ERR(realfile)) { >- inode_unlock(inode); >+ up(&od->upperfile_sem); > return realfile; > } > smp_store_release(&od->upperfile, realfile); >@@ -896,7 +895,7 @@ struct file *ovl_dir_real_file(const struct file >*file, bool want_upper) > fput(realfile); > realfile = od->upperfile; > } >- inode_unlock(inode); >+ up(&od->upperfile_sem); > } > } > >@@ -959,6 +958,7 @@ static int ovl_dir_open(struct inode *inode, struct >file *file) > od->realfile = realfile; > od->is_real = ovl_dir_is_real(file->f_path.dentry); > od->is_upper = OVL_TYPE_UPPER(type); >+ sema_init(&od->upperfile_sem, 1); > file->private_data = od; > > return 0;