cgroup: fix cgroup_path() vs rename() race, take 2
Subsystems (2)
PATCH v2 2/2 cgroup: fix cgroup_path() vs rename() race, take 2
rename() will change dentry->d_name. The result of this race can be worse than seeing partially rewritten name, but we might access a stale pointer because rename() will re-allocate memory to hold a longer name.
As accessing dentry->name must be protected by dentry->d_lock or parent inode's i_mutex, while on the other hand cgroup-path() can be called with some irq-safe spinlocks held, we can't generate cgroup path using dentry->d_name.
Alternatively we make a copy of dentry->d_name and save it in cgrp->name when a cgroup is created, and update cgrp->name at rename().
v2: make cgrp->name RCU safe.
--- block/blk-cgroup.h | 2 - include/linux/cgroup.h | 1 + kernel/cgroup.c | 113 ++++++++++++++++++++++++++++++++++++------------- 3 files changed, 85 insertions(+), 31 deletions(-)
block/blk-cgroup.h +0 -2
@@ -216,9 +216,7 @@ static inline int blkg_path(struct blkcg_gq *blkg, char *buf, int buflen) {int ret; -rcu_read_lock (); ret =cgroup_path (blkg->blkcg->css.cgroup, buf, buflen); -rcu_read_unlock ();if (ret)strncpy (buf,"<unavailable>" , buflen);return ret;
include/linux/cgroup.h +1 -0
@@ -171,6 +171,7 @@ struct cgroup {struct cgroup *parent;/* my parent */ struct dentry *dentry;/* cgroup fs entry, RCU protected */ +char __rcu *name;/* a copy of dentry->d_name */ /* Private pointers for each registered subsystem */ struct cgroup_subsys_state *subsys[CGROUP_SUBSYS_COUNT ];
kernel/cgroup.c +84 -29
206 lines not on this page · raw message
Signature · 1 line
1.8.0.2
Tejun Heo
maintainerHello, Li.
include/linux/cgroup.h struct cgroup { · line 174
Quoted 5 lines · Li Zefan
include/linux/cgroup.h +1
-0@@ -171,6 +171,7 @@ struct cgroup {struct cgroup *parent;/* my parent */ struct dentry *dentry;/* cgroup fs entry, RCU protected */ +char __rcu *name;/* a copy of dentry->d_name */
A brief explanation of why this is necessary and how rcu is used would be nice.
kernel/cgroup.c static int cgroup_get_rootdir(struct super_block *sb) · line 1579
Quoted 11 lines
kernel/cgroup.c +11
-0+static char *cgroup_alloc_name (struct dentry *dentry) +{ +char *name; + + name =kmalloc (dentry->d_name.len +1 ,GFP_KERNEL ); +if (!name) +return NULL ; +memcpy (name, dentry->d_name.name, dentry->d_name.len); + name[dentry->d_name.len] ='\0' ; +return name; +}
While d_name has length field, it's always properly NULL terminated, so kstrdup() should suffice here. Right, Al?
Quoted 1 line
(quoted diff) +0
-0@@ -1613,13 +1626,19 @@ static struct dentry *cgroup_mount(struct file_system_type *fs_type,
...
kernel/cgroup.c static struct dentry *cgroup_mount(struct file_system_type *fs_type, · line 1641
Quoted 7 lines
kernel/cgroup.c +6
-1- inode = sb->s_root->d_inode; + dentry = sb->s_root; + inode = dentry->d_inode; + + root_cgrp->name =cgroup_alloc_name (dentry); +if (!root_cgrp->name) + goto drop_new_super;
Don't we need an RCU assignment? Is it safe because it isn't online yet? But wouldn't this still trigger sparse warning?
kernel/cgroup.c static void cgroup_kill_sb(struct super_block *sb) { · line 1773
Quoted 5 lines
kernel/cgroup.c +1
-0@@ -1751,6 +1770,8 @@ static void cgroup_kill_sb(struct super_block *sb) {mutex_unlock (&cgroup_root_mutex);mutex_unlock (&cgroup_mutex); +synchronize_rcu ();
An explanation on what we're synchronizing would be nice. Barriers without explanation sucks because there's nothing directly linking the barriers to the things which are being protected.
Quoted 4 lines
kernel/cgroup.c +0
-0@@ -2539,13 +2558,41 @@ static int cgroup_file_release(struct inode *inode, struct file *file)static int cgroup_rename (struct inode *old_dir,struct dentry *old_dentry,struct inode *new_dir,struct dentry *new_dentry) {
...
kernel/cgroup.c static int cgroup_file_release(struct inode *inode, struct file *file) · line 2593
Quoted 4 lines
kernel/cgroup.c +4
-0+ old_name = cgrp->name; +rcu_assign_pointer (cgrp->name, name); + +synchronize_rcu ();
Please don't call synchronize_rcu() from interface which is directly visible to userland. It leads to sporadic difficult-to-reproduce latencies which hurt enough in corner cases and this is kmalloc memory. It's not like kfree_rcu() is difficult to use or anything.
kernel/cgroup.c static long cgroup_create(struct cgroup *parent, struct dentry *dentry, · line 4196
Quoted 12 lines
kernel/cgroup.c +5
-0+kfree (old_name); +return 0 ; }static struct simple_xattrs *__d_xattrs (struct dentry *dentry)@@ -4144,9 +4191,13 @@ static long cgroup_create(struct cgroup *parent, struct dentry *dentry,if (!cgrp)return -ENOMEM ; + cgrp->name =cgroup_alloc_name (dentry); +if (!cgrp->name) + goto err_free_cgrp;
Ditto with assignment.
Thanks.
Signature · 1 line
tejun
Quoted 12 lines · Tejun Heo
Hello, Li.
include/linux/cgroup.h struct cgroup { · line 174
Quoted 5 lines · Li Zefan
include/linux/cgroup.h +1
-0@@ -171,6 +171,7 @@ struct cgroup {struct cgroup *parent;/* my parent */ struct dentry *dentry;/* cgroup fs entry, RCU protected */ +char __rcu *name;/* a copy of dentry->d_name */ A brief explanation of why this is necessary and how rcu is used would be nice.
The comments in cgroup_path() explains why we can't use dentry->d_name, which suggests why cgrp->name is needed. I'll revise the comments there, and add comments on the rcu thing.
Quoted 6 lines
Quoted 6 lines
kernel/cgroup.c +6
-0+static char *cgroup_alloc_name (struct dentry *dentry) +{ +char *name; + + name =kmalloc (dentry->d_name.len +1 ,GFP_KERNEL ); +if (!name)
Quoted 9 lines
kernel/cgroup.c static int cgroup_get_rootdir(struct super_block *sb) · line 1579
Quoted 5 lines
kernel/cgroup.c +5
-0+return NULL ; +memcpy (name, dentry->d_name.name, dentry->d_name.len); + name[dentry->d_name.len] ='\0' ; +return name; +}While
d_namehas length field, it's always properly NULL terminated, sokstrdup()should suffice here. Right, Al?
Oh you're right. We pass dentry->d_name.name to printk %s in other places.
Quoted 13 lines
Quoted 1 line
(quoted diff) +0
-0@@ -1613,13 +1626,19 @@ static struct dentry *cgroup_mount(struct file_system_type *fs_type,...
kernel/cgroup.c static struct dentry *cgroup_mount(struct file_system_type *fs_type, · line 1641
Quoted 7 lines
kernel/cgroup.c +6
-1- inode = sb->s_root->d_inode; + dentry = sb->s_root; + inode = dentry->d_inode; + + root_cgrp->name =cgroup_alloc_name (dentry); +if (!root_cgrp->name) + goto drop_new_super;Don't we need an RCU assignment? Is it safe because it isn't online yet? But wouldn't this still trigger sparse warning?
Yeah, it's safe.
To be frank, I haven't used sparse for years. Will check.
Quoted 15 lines
kernel/cgroup.c static void cgroup_kill_sb(struct super_block *sb) { · line 1773
Quoted 5 lines
kernel/cgroup.c +1
-0@@ -1751,6 +1770,8 @@ static void cgroup_kill_sb(struct super_block *sb) {mutex_unlock (&cgroup_root_mutex);mutex_unlock (&cgroup_mutex); +synchronize_rcu ();An explanation on what we're synchronizing would be nice. Barriers without explanation sucks because there's nothing directly linking the barriers to the things which are being protected.
Quoted 4 lines
kernel/cgroup.c +0
-0@@ -2539,13 +2558,41 @@ static int cgroup_file_release(struct inode *inode, struct file *file)static int cgroup_rename (struct inode *old_dir,struct dentry *old_dentry,struct inode *new_dir,struct dentry *new_dentry) {...
Quoted 10 lines
kernel/cgroup.c static int cgroup_file_release(struct inode *inode, struct file *file) · line 2593
Quoted 4 lines
kernel/cgroup.c +4
-0+ old_name = cgrp->name; +rcu_assign_pointer (cgrp->name, name); + +synchronize_rcu ();Please don't call
synchronize_rcu()from interface which is directly visible to userland. It leads to sporadic difficult-to-reproduce latencies which hurt enough in corner cases and this is kmalloc memory. It's not likekfree_rcu()is difficult to use or anything.
ok
Quoted 17 lines
kernel/cgroup.c static long cgroup_create(struct cgroup *parent, struct dentry *dentry, · line 4196
Quoted 12 lines
kernel/cgroup.c +5
-0+kfree (old_name); +return 0 ; }static struct simple_xattrs *__d_xattrs (struct dentry *dentry)@@ -4144,9 +4191,13 @@ static long cgroup_create(struct cgroup *parent, struct dentry *dentry,if (!cgrp)return -ENOMEM ; + cgrp->name =cgroup_alloc_name (dentry); +if (!cgrp->name) + goto err_free_cgrp;Ditto with assignment.
Thanks.