History

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.

Al Viro committed 17 years ago

Commit 2a32ceb1 parent f9dbd057 files · +25 −13Browse files

Unified · Split

7 files
  1. drivers/isdn/capi/capifs.c +1 −2
  2. fs/affs/super.c +1 −2
  3. fs/afs/super.c +2 −2
  4. fs/hpfs/super.c +1 −2
  5. fs/namespace.c +18 −3
  6. fs/reiserfs/super.c +1 −2
  7. include/linux/fs.h +1 −0
drivers/isdn/capi/capifs.c+1−2
@@ -75,8 +75,7 @@ static int capifs_remount(struct super_block *s, int *flags, char *data) · 74 unmodified lines
7575 }
7676 }
7777
78− kfree(s->s_options);
79− s->s_options = new_opt;
78+ replace_mount_options(s, new_opt);
8079
8180 config.setuid = setuid;
8281 config.setgid = setgid;
fs/affs/super.c+1−2
@@ -507,8 +507,7 @@ affs_remount(struct super_block *sb, int *flags, char *data) · 506 unmodified lines
507507 kfree(new_opts);
508508 return -EINVAL;
509509 }
510− kfree(sb->s_options);
511− sb->s_options = new_opts;
510+ replace_mount_options(sb, new_opts);
512511
513512 sbi->s_flags = mount_flags;
514513 sbi->s_mode = mode;
fs/afs/super.c+2−2
@@ -408,17 +408,17 @@ static int afs_get_sb(struct file_system_type *fs_type, · 407 unmodified lines
408408 deactivate_locked_super(sb);
409409 goto error;
410410 }
411− sb->s_options = new_opts;
411+ save_mount_options(sb, new_opts);
412412 sb->s_flags |= MS_ACTIVE;
413413 } else {
414414 _debug("reuse");
415− kfree(new_opts);
416415 ASSERTCMP(sb->s_flags, &, MS_ACTIVE);
417416 }
418417
419418 simple_set_mnt(mnt, sb);
420419 afs_put_volume(params.volume);
421420 afs_put_cell(params.cell);
421+ kfree(new_opts);
422422 _leave(" = 0 [%p]", sb);
423423 return 0;
424424
fs/hpfs/super.c+1−2
@@ -423,8 +423,7 @@ static int hpfs_remount_fs(struct super_block *s, int *flags, char *data) · 422 unmodified lines
423423
424424 if (!(*flags & MS_RDONLY)) mark_dirty(s);
425425
426− kfree(s->s_options);
427− s->s_options = new_opts;
426+ replace_mount_options(s, new_opts);
428427
429428 return 0;
430429
fs/namespace.c+18−3
@@ -695,12 +695,16 @@ static inline void mangle(struct seq_file *m, const char *s) · 694 unmodified lines
695695 */
696696 int generic_show_options(struct seq_file *m, struct vfsmount *mnt)
697697 {
698− const char *options = mnt->mnt_sb->s_options;
698+ const char *options;
699+
700+ rcu_read_lock();
701+ options = rcu_dereference(mnt->mnt_sb->s_options);
699702
700703 if (options != NULL && options[0]) {
701704 seq_putc(m, ',');
702705 mangle(m, options);
703706 }
707+ rcu_read_unlock();
704708
705709 return 0;
706710 }
@@ -721,11 +725,22 @@ EXPORT_SYMBOL(generic_show_options); · 14 unmodified lines
721725 */
722726 void save_mount_options(struct super_block *sb, char *options)
723727 {
724− kfree(sb->s_options);
725− sb->s_options = kstrdup(options, GFP_KERNEL);
728+ BUG_ON(sb->s_options);
729+ rcu_assign_pointer(sb->s_options, kstrdup(options, GFP_KERNEL));
726730 }
727731 EXPORT_SYMBOL(save_mount_options);
728732
733+void replace_mount_options(struct super_block *sb, char *options)
734+{
735+ char *old = sb->s_options;
736+ rcu_assign_pointer(sb->s_options, options);
737+ if (old) {
738+ synchronize_rcu();
739+ kfree(old);
740+ }
741+}
742+EXPORT_SYMBOL(replace_mount_options);
743+
729744 #ifdef CONFIG_PROC_FS
730745 /* iterator */
731746 static void *m_start(struct seq_file *m, loff_t *pos)
fs/reiserfs/super.c+1−2
@@ -1316,8 +1316,7 @@ static int reiserfs_remount(struct super_block *s, int *mount_flags, char *arg) · 1315 unmodified lines
13161316 }
13171317
13181318 out_ok:
1319− kfree(s->s_options);
1320− s->s_options = new_opts;
1319+ replace_mount_options(s, new_opts);
13211320 return 0;
13221321
13231322 out_err:
include/linux/fs.h+1−0
@@ -2368,6 +2368,7 @@ extern void file_update_time(struct file *file); · 2367 unmodified lines
23682368
23692369 extern int generic_show_options(struct seq_file *m, struct vfsmount *mnt);
23702370 extern void save_mount_options(struct super_block *sb, char *options);
2371+extern void replace_mount_options(struct super_block *sb, char *options);
23712372
23722373 static inline ino_t parent_ino(struct dentry *dentry)
23732374 {