From c1892c37769cf89c7e7ba57528ae2ccb5d153c9b Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Wed, 3 Aug 2016 13:44:27 +0200 Subject: vfs: fix deadlock in file_remove_privs() on overlayfs file_remove_privs() is called with inode lock on file_inode(), which proceeds to calling notify_change() on file->f_path.dentry. Which triggers the WARN_ON_ONCE(!inode_is_locked(inode)) in addition to deadlocking later when ovl_setattr tries to lock the underlying inode again. Fix this mess by not mixing the layers, but doing everything on underlying dentry/inode. Signed-off-by: Miklos Szeredi Fixes: 07a2daab49c5 ("ovl: Copy up underlying inode's ->i_mode to overlay inode") Cc: diff --git a/fs/inode.c b/fs/inode.c index 4ccbc21..68db390 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -1740,8 +1740,8 @@ static int __remove_privs(struct dentry *dentry, int kill) */ int file_remove_privs(struct file *file) { - struct dentry *dentry = file->f_path.dentry; - struct inode *inode = d_inode(dentry); + struct dentry *dentry = file_dentry(file); + struct inode *inode = file_inode(file); int kill; int error = 0; @@ -1749,7 +1749,7 @@ int file_remove_privs(struct file *file) if (IS_NOSEC(inode)) return 0; - kill = file_needs_remove_privs(file); + kill = dentry_needs_remove_privs(dentry); if (kill < 0) return kill; if (kill) -- cgit v0.10.2 From 3e5a4c94a4f88b545d64bbfc7647b06b92c66e98 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Wed, 3 Aug 2016 13:44:27 +0200 Subject: vfs: remove file_needs_remove_privs() This function is now unused. Signed-off-by: Miklos Szeredi diff --git a/include/linux/fs.h b/include/linux/fs.h index dd28814..3888341 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2726,10 +2726,6 @@ extern void free_inode_nonrcu(struct inode *inode); extern int should_remove_suid(struct dentry *); extern int file_remove_privs(struct file *); extern int dentry_needs_remove_privs(struct dentry *dentry); -static inline int file_needs_remove_privs(struct file *file) -{ - return dentry_needs_remove_privs(file->f_path.dentry); -} extern void __insert_inode_hash(struct inode *, unsigned long hashval); static inline void insert_inode_hash(struct inode *inode) -- cgit v0.10.2 From f0fce87c36aec5c4a895c78b76396f5727047b93 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Wed, 3 Aug 2016 13:44:27 +0200 Subject: vfs: make dentry_needs_remove_privs() internal Only used by the vfs. Signed-off-by: Miklos Szeredi diff --git a/fs/inode.c b/fs/inode.c index 68db390..2029357 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -1720,7 +1720,6 @@ int dentry_needs_remove_privs(struct dentry *dentry) mask |= ATTR_KILL_PRIV; return mask; } -EXPORT_SYMBOL(dentry_needs_remove_privs); static int __remove_privs(struct dentry *dentry, int kill) { diff --git a/fs/internal.h b/fs/internal.h index f57ced5..30b568f 100644 --- a/fs/internal.h +++ b/fs/internal.h @@ -114,6 +114,7 @@ extern int vfs_open(const struct path *, struct file *, const struct cred *); */ extern long prune_icache_sb(struct super_block *sb, struct shrink_control *sc); extern void inode_add_lru(struct inode *inode); +extern int dentry_needs_remove_privs(struct dentry *dentry); /* * fs-writeback.c diff --git a/include/linux/fs.h b/include/linux/fs.h index 3888341..448641b 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2725,7 +2725,6 @@ extern struct inode *new_inode(struct super_block *sb); extern void free_inode_nonrcu(struct inode *inode); extern int should_remove_suid(struct dentry *); extern int file_remove_privs(struct file *); -extern int dentry_needs_remove_privs(struct dentry *dentry); extern void __insert_inode_hash(struct inode *, unsigned long hashval); static inline void insert_inode_hash(struct inode *inode) -- cgit v0.10.2