Skip to content

Commit 76ae281

Browse files
biger410torvalds
authored andcommitted
configfs: fix race between dentry put and lookup
A race window in configfs, it starts from one dentry is UNHASHED and end before configfs_d_iput is called. In this window, if a lookup happen, since the original dentry was UNHASHED, so a new dentry will be allocated, and then in configfs_attach_attr(), sd->s_dentry will be updated to the new dentry. Then in configfs_d_iput(), BUG_ON(sd->s_dentry != dentry) will be triggered and system panic. sys_open: sys_close: ... fput dput dentry_kill __d_drop <--- dentry unhashed here, but sd->dentry still point to this dentry. lookup_real configfs_lookup configfs_attach_attr---> update sd->s_dentry to new allocated dentry here. d_kill configfs_d_iput <--- BUG_ON(sd->s_dentry != dentry) triggered here. To fix it, change configfs_d_iput to not update sd->s_dentry if sd->s_count > 2, that means there are another dentry is using the sd beside the one that is going to be put. Use configfs_dirent_lock in configfs_attach_attr to sync with configfs_d_iput. With the following steps, you can reproduce the bug. 1. enable ocfs2, this will mount configfs at /sys/kernel/config and fill configure in it. 2. run the following script. while [ 1 ]; do cat /sys/kernel/config/cluster/$your_cluster_name/idle_timeout_ms > /dev/null; done & while [ 1 ]; do cat /sys/kernel/config/cluster/$your_cluster_name/idle_timeout_ms > /dev/null; done & Signed-off-by: Junxiao Bi <[email protected]> Cc: Joel Becker <[email protected]> Cc: Al Viro <[email protected]> Cc: <[email protected]> Signed-off-by: Andrew Morton <[email protected]> Signed-off-by: Linus Torvalds <[email protected]>
1 parent 527d151 commit 76ae281

File tree

1 file changed

+14
-2
lines changed

1 file changed

+14
-2
lines changed

fs/configfs/dir.c

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,10 +56,19 @@ static void configfs_d_iput(struct dentry * dentry,
5656
struct configfs_dirent *sd = dentry->d_fsdata;
5757

5858
if (sd) {
59-
BUG_ON(sd->s_dentry != dentry);
6059
/* Coordinate with configfs_readdir */
6160
spin_lock(&configfs_dirent_lock);
62-
sd->s_dentry = NULL;
61+
/* Coordinate with configfs_attach_attr where will increase
62+
* sd->s_count and update sd->s_dentry to new allocated one.
63+
* Only set sd->dentry to null when this dentry is the only
64+
* sd owner.
65+
* If not do so, configfs_d_iput may run just after
66+
* configfs_attach_attr and set sd->s_dentry to null
67+
* even it's still in use.
68+
*/
69+
if (atomic_read(&sd->s_count) <= 2)
70+
sd->s_dentry = NULL;
71+
6372
spin_unlock(&configfs_dirent_lock);
6473
configfs_put(sd);
6574
}
@@ -416,8 +425,11 @@ static int configfs_attach_attr(struct configfs_dirent * sd, struct dentry * den
416425
struct configfs_attribute * attr = sd->s_element;
417426
int error;
418427

428+
spin_lock(&configfs_dirent_lock);
419429
dentry->d_fsdata = configfs_get(sd);
420430
sd->s_dentry = dentry;
431+
spin_unlock(&configfs_dirent_lock);
432+
421433
error = configfs_create(dentry, (attr->ca_mode & S_IALLUGO) | S_IFREG,
422434
configfs_init_file);
423435
if (error) {

0 commit comments

Comments
 (0)