diff --git a/fs/bad_inode.c b/fs/bad_inode.c index 0ef9bcb744dd..8e9127d4dcc1 100644 --- a/fs/bad_inode.c +++ b/fs/bad_inode.c @@ -207,11 +207,17 @@ void make_bad_inode(struct inode *inode) { remove_inode_hash(inode); + /* + * Taking the spinlock is a temporary hack to let lookup assert on the state, + * see lookup_inode_permission_may_exec(). + */ + spin_lock(&inode->i_lock); inode->i_mode = S_IFREG; simple_inode_init_ts(inode); inode->i_op = &bad_inode_ops; inode->i_opflags &= ~IOP_XATTR; inode->i_fop = &bad_file_ops; + spin_unlock(&inode->i_lock); } EXPORT_SYMBOL(make_bad_inode); diff --git a/fs/namei.c b/fs/namei.c index bf0f66f0e9b9..79cc14d635b5 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -626,12 +626,31 @@ EXPORT_SYMBOL(inode_permission); static __always_inline int lookup_inode_permission_may_exec(struct mnt_idmap *idmap, struct inode *inode, int mask) { - /* Lookup already checked this to return -ENOTDIR */ - VFS_BUG_ON_INODE(!S_ISDIR(inode->i_mode), inode); VFS_BUG_ON((mask & ~MAY_NOT_BLOCK) != 0); +#ifdef CONFIG_DEBUG_VFS + /* + * We skip the type check on the assumption this is a directory, which was + * checked for by our caller. + * + * However, there are bogus consumers of make_bad_inode() which can mess this up, + * to be fixed soon(tm). + * + * In the meantime make sure we are dealing with the expected state before tripping + * over. If this *is* a "bad inode", the resulting state is bug-compatible with + * historical behavior. See the previous remark about sorting this out. + */ + if (!S_ISDIR(inode->i_mode)) { + spin_lock(&inode->i_lock); + if (!is_bad_inode(inode)) + VFS_BUG_ON_INODE(!S_ISDIR(inode->i_mode), inode); + spin_unlock(&inode->i_lock); + } +#endif mask |= MAY_EXEC; + return inode_permission(idmap, inode, mask); +#if 0 if (unlikely(!(inode->i_opflags & (IOP_FASTPERM | IOP_FASTPERM_MAY_EXEC)))) return inode_permission(idmap, inode, mask); @@ -639,6 +658,7 @@ static __always_inline int lookup_inode_permission_may_exec(struct mnt_idmap *id return inode_permission(idmap, inode, mask); return security_inode_permission(inode, mask); +#endif } /**