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:
Родитель
f9dbd05bc9
Коммит
2a32cebd6c
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
Загрузка…
Ссылка в новой задаче