Skip to content

Commit 9e57e28

Browse files
Al Virogregkh
authored andcommitted
configfs: fix lockless traversals of ->s_children
[ Upstream commit 9b9e8bb ] Having the parent directory locked protects entries from removal by another thread, but it does *not* protect cursors from being moved around by lseek() - or freed, for that matter. Fixes: 6f61076 ("configfs: Introduce configfs_dirent_lock") Reviewed-by: Jan Kara <jack@suse.cz> Signed-off-by: Al Viro <viro@zeniv.linux.org.uk> Signed-off-by: Sasha Levin <sashal@kernel.org>
1 parent 38149b5 commit 9e57e28

1 file changed

Lines changed: 41 additions & 13 deletions

File tree

fs/configfs/dir.c

Lines changed: 41 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -225,15 +225,16 @@ static int configfs_dirent_exists(struct configfs_dirent *parent_sd,
225225
{
226226
struct configfs_dirent * sd;
227227

228+
spin_lock(&configfs_dirent_lock);
228229
list_for_each_entry(sd, &parent_sd->s_children, s_sibling) {
229230
if (sd->s_element) {
230-
const unsigned char *existing = configfs_get_name(sd);
231-
if (strcmp(existing, new))
232-
continue;
233-
else
231+
if (strcmp(configfs_get_name(sd), new) == 0) {
232+
spin_unlock(&configfs_dirent_lock);
234233
return -EEXIST;
234+
}
235235
}
236236
}
237+
spin_unlock(&configfs_dirent_lock);
237238

238239
return 0;
239240
}
@@ -551,11 +552,28 @@ static void configfs_detach_rollback(struct dentry *dentry)
551552
configfs_detach_rollback(sd->s_dentry);
552553
}
553554

555+
/*
556+
* Find the next non-cursor. configfs_dirent_lock held by caller.
557+
*/
558+
static struct configfs_dirent *next_dirent(struct configfs_dirent *parent,
559+
struct configfs_dirent *last)
560+
{
561+
struct configfs_dirent *s;
562+
563+
s = list_prepare_entry(last, &parent->s_children, s_sibling);
564+
565+
list_for_each_entry_continue(s, &parent->s_children, s_sibling) {
566+
if (s->s_element)
567+
return s;
568+
}
569+
return NULL;
570+
}
571+
554572
static void detach_attrs(struct config_item * item)
555573
{
556574
struct dentry * dentry = dget(item->ci_dentry);
557-
struct configfs_dirent * parent_sd;
558-
struct configfs_dirent * sd, * tmp;
575+
struct configfs_dirent *parent_sd;
576+
struct configfs_dirent *sd, *next;
559577

560578
if (!dentry)
561579
return;
@@ -564,15 +582,19 @@ static void detach_attrs(struct config_item * item)
564582
dentry->d_name.name);
565583

566584
parent_sd = dentry->d_fsdata;
567-
list_for_each_entry_safe(sd, tmp, &parent_sd->s_children, s_sibling) {
568-
if (!sd->s_element || !(sd->s_type & CONFIGFS_NOT_PINNED))
585+
586+
spin_lock(&configfs_dirent_lock);
587+
for (sd = next_dirent(parent_sd, NULL); sd; sd = next) {
588+
next = next_dirent(parent_sd, sd);
589+
if (!(sd->s_type & CONFIGFS_NOT_PINNED))
569590
continue;
570-
spin_lock(&configfs_dirent_lock);
571591
list_del_init(&sd->s_sibling);
572592
spin_unlock(&configfs_dirent_lock);
573593
configfs_drop_dentry(sd, dentry);
574594
configfs_put(sd);
595+
spin_lock(&configfs_dirent_lock);
575596
}
597+
spin_unlock(&configfs_dirent_lock);
576598

577599
/**
578600
* Drop reference from dget() on entrance.
@@ -621,18 +643,20 @@ static void detach_groups(struct config_group *group)
621643
struct dentry * dentry = dget(group->cg_item.ci_dentry);
622644
struct dentry *child;
623645
struct configfs_dirent *parent_sd;
624-
struct configfs_dirent *sd, *tmp;
646+
struct configfs_dirent *sd, *next;
625647

626648
if (!dentry)
627649
return;
628650

629651
parent_sd = dentry->d_fsdata;
630-
list_for_each_entry_safe(sd, tmp, &parent_sd->s_children, s_sibling) {
631-
if (!sd->s_element ||
632-
!(sd->s_type & CONFIGFS_USET_DEFAULT))
652+
spin_lock(&configfs_dirent_lock);
653+
for (sd = next_dirent(parent_sd, NULL); sd; sd = next) {
654+
next = next_dirent(parent_sd, sd);
655+
if (!(sd->s_type & CONFIGFS_USET_DEFAULT))
633656
continue;
634657

635658
child = sd->s_dentry;
659+
spin_unlock(&configfs_dirent_lock);
636660

637661
inode_lock(d_inode(child));
638662

@@ -644,7 +668,9 @@ static void detach_groups(struct config_group *group)
644668

645669
d_delete(child);
646670
dput(child);
671+
spin_lock(&configfs_dirent_lock);
647672
}
673+
spin_unlock(&configfs_dirent_lock);
648674

649675
/**
650676
* Drop reference from dget() on entrance.
@@ -1096,13 +1122,15 @@ configfs_find_subsys_dentry(struct configfs_dirent *root_sd,
10961122
struct configfs_dirent *p;
10971123
struct configfs_dirent *ret = NULL;
10981124

1125+
spin_lock(&configfs_dirent_lock);
10991126
list_for_each_entry(p, &root_sd->s_children, s_sibling) {
11001127
if (p->s_type & CONFIGFS_DIR &&
11011128
p->s_element == subsys_item) {
11021129
ret = p;
11031130
break;
11041131
}
11051132
}
1133+
spin_unlock(&configfs_dirent_lock);
11061134

11071135
return ret;
11081136
}

0 commit comments

Comments
 (0)