diff --git a/fs/dcache.c b/fs/dcache.c index c17fd15b01d4..799d9e4f0bcd 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -1043,6 +1043,31 @@ struct dentry *d_find_alias(struct inode *inode) } EXPORT_SYMBOL(d_find_alias); +/* + * Caller MUST be holding rcu_read_lock() and be guaranteed + * that inode won't get freed until rcu_read_unlock(). + */ +struct dentry *d_find_alias_rcu(struct inode *inode) +{ + struct hlist_head *l = &inode->i_dentry; + struct dentry *de = NULL; + + spin_lock(&inode->i_lock); + // ->i_dentry and ->i_rcu are colocated, but the latter won't be + // used without having I_FREEING set, which means no aliases left + if (likely(!(inode->i_state & I_FREEING) && !hlist_empty(l))) { + if (S_ISDIR(inode->i_mode)) { + de = hlist_entry(l->first, struct dentry, d_u.d_alias); + } else { + hlist_for_each_entry(de, l, d_u.d_alias) + if (!d_unhashed(de)) + break; + } + } + spin_unlock(&inode->i_lock); + return de; +} + /* * Try to kill dentries associated with this inode. * WARNING: you must own a reference to inode. diff --git a/include/linux/dcache.h b/include/linux/dcache.h index d7b369fc15d3..c1e48014106f 100644 --- a/include/linux/dcache.h +++ b/include/linux/dcache.h @@ -262,6 +262,8 @@ extern void d_tmpfile(struct dentry *, struct inode *); extern struct dentry *d_find_alias(struct inode *); extern void d_prune_aliases(struct inode *); +extern struct dentry *d_find_alias_rcu(struct inode *); + /* test whether we have any submounts in a subdir tree */ extern int path_has_submounts(const struct path *); diff --git a/security/lsm_audit.c b/security/lsm_audit.c index a0cd28cd31a8..82ce14933513 100644 --- a/security/lsm_audit.c +++ b/security/lsm_audit.c @@ -291,18 +291,19 @@ static void dump_common_audit_data(struct audit_buffer *ab, struct dentry *dentry; struct inode *inode; + rcu_read_lock(); inode = a->u.inode; - dentry = d_find_alias(inode); + dentry = d_find_alias_rcu(inode); if (dentry) { audit_log_format(ab, " name="); spin_lock(&dentry->d_lock); audit_log_untrustedstring(ab, dentry->d_name.name); spin_unlock(&dentry->d_lock); - dput(dentry); } audit_log_format(ab, " dev="); audit_log_untrustedstring(ab, inode->i_sb->s_id); audit_log_format(ab, " ino=%lu", inode->i_ino); + rcu_read_unlock(); break; } case LSM_AUDIT_DATA_TASK: {