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
Родитель f9dbd05bc9
Коммит 2a32cebd6c
7 изменённых файлов: 25 добавлений и 13 удалений

Просмотреть файл

@ -75,8 +75,7 @@ static int capifs_remount(struct super_block *s, int *flags, char *data)
} }
} }
kfree(s->s_options); replace_mount_options(s, new_opt);
s->s_options = new_opt;
config.setuid = setuid; config.setuid = setuid;
config.setgid = setgid; config.setgid = setgid;

Просмотреть файл

@ -507,8 +507,7 @@ affs_remount(struct super_block *sb, int *flags, char *data)
kfree(new_opts); kfree(new_opts);
return -EINVAL; return -EINVAL;
} }
kfree(sb->s_options); replace_mount_options(sb, new_opts);
sb->s_options = new_opts;
sbi->s_flags = mount_flags; sbi->s_flags = mount_flags;
sbi->s_mode = mode; sbi->s_mode = mode;

Просмотреть файл

@ -408,17 +408,17 @@ static int afs_get_sb(struct file_system_type *fs_type,
deactivate_locked_super(sb); deactivate_locked_super(sb);
goto error; goto error;
} }
sb->s_options = new_opts; save_mount_options(sb, new_opts);
sb->s_flags |= MS_ACTIVE; sb->s_flags |= MS_ACTIVE;
} else { } else {
_debug("reuse"); _debug("reuse");
kfree(new_opts);
ASSERTCMP(sb->s_flags, &, MS_ACTIVE); ASSERTCMP(sb->s_flags, &, MS_ACTIVE);
} }
simple_set_mnt(mnt, sb); simple_set_mnt(mnt, sb);
afs_put_volume(params.volume); afs_put_volume(params.volume);
afs_put_cell(params.cell); afs_put_cell(params.cell);
kfree(new_opts);
_leave(" = 0 [%p]", sb); _leave(" = 0 [%p]", sb);
return 0; return 0;

Просмотреть файл

@ -423,8 +423,7 @@ static int hpfs_remount_fs(struct super_block *s, int *flags, char *data)
if (!(*flags & MS_RDONLY)) mark_dirty(s); if (!(*flags & MS_RDONLY)) mark_dirty(s);
kfree(s->s_options); replace_mount_options(s, new_opts);
s->s_options = new_opts;
return 0; return 0;

Просмотреть файл

@ -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) 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]) { if (options != NULL && options[0]) {
seq_putc(m, ','); seq_putc(m, ',');
mangle(m, options); mangle(m, options);
} }
rcu_read_unlock();
return 0; return 0;
} }
@ -721,11 +725,22 @@ EXPORT_SYMBOL(generic_show_options);
*/ */
void save_mount_options(struct super_block *sb, char *options) void save_mount_options(struct super_block *sb, char *options)
{ {
kfree(sb->s_options); BUG_ON(sb->s_options);
sb->s_options = kstrdup(options, GFP_KERNEL); rcu_assign_pointer(sb->s_options, kstrdup(options, GFP_KERNEL));
} }
EXPORT_SYMBOL(save_mount_options); 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 #ifdef CONFIG_PROC_FS
/* iterator */ /* iterator */
static void *m_start(struct seq_file *m, loff_t *pos) static void *m_start(struct seq_file *m, loff_t *pos)

Просмотреть файл

@ -1316,8 +1316,7 @@ static int reiserfs_remount(struct super_block *s, int *mount_flags, char *arg)
} }
out_ok: out_ok:
kfree(s->s_options); replace_mount_options(s, new_opts);
s->s_options = new_opts;
return 0; return 0;
out_err: out_err:

Просмотреть файл

@ -2368,6 +2368,7 @@ extern void file_update_time(struct file *file);
extern int generic_show_options(struct seq_file *m, struct vfsmount *mnt); extern int generic_show_options(struct seq_file *m, struct vfsmount *mnt);
extern void save_mount_options(struct super_block *sb, char *options); extern void save_mount_options(struct super_block *sb, char *options);
extern void replace_mount_options(struct super_block *sb, char *options);
static inline ino_t parent_ino(struct dentry *dentry) static inline ino_t parent_ino(struct dentry *dentry)
{ {