Fix races around the access to ->s_options

Put generic_show_options read access to s_options under rcu_read_lock,
split save_mount_options() into "we are setting it the first time"
(uses in foo_fill_super()) and "we are relacing and freeing the old one",
synchronize_rcu() before kfree() in the latter.

Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
This commit is contained in:
Al Viro 2009-05-08 16:05:57 -04:00
parent f9dbd05bc9
commit 2a32cebd6c
7 changed files with 25 additions and 13 deletions

View file

@ -695,12 +695,16 @@ static inline void mangle(struct seq_file *m, const char *s)
*/
int generic_show_options(struct seq_file *m, struct vfsmount *mnt)
{
const char *options = mnt->mnt_sb->s_options;
const char *options;
rcu_read_lock();
options = rcu_dereference(mnt->mnt_sb->s_options);
if (options != NULL && options[0]) {
seq_putc(m, ',');
mangle(m, options);
}
rcu_read_unlock();
return 0;
}
@ -721,11 +725,22 @@ EXPORT_SYMBOL(generic_show_options);
*/
void save_mount_options(struct super_block *sb, char *options)
{
kfree(sb->s_options);
sb->s_options = kstrdup(options, GFP_KERNEL);
BUG_ON(sb->s_options);
rcu_assign_pointer(sb->s_options, kstrdup(options, GFP_KERNEL));
}
EXPORT_SYMBOL(save_mount_options);
void replace_mount_options(struct super_block *sb, char *options)
{
char *old = sb->s_options;
rcu_assign_pointer(sb->s_options, options);
if (old) {
synchronize_rcu();
kfree(old);
}
}
EXPORT_SYMBOL(replace_mount_options);
#ifdef CONFIG_PROC_FS
/* iterator */
static void *m_start(struct seq_file *m, loff_t *pos)