aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAl Viro <viro@zeniv.linux.org.uk>2009-05-08 16:05:57 -0400
committerAl Viro <viro@zeniv.linux.org.uk>2009-05-09 10:51:34 -0400
commit2a32cebd6cbcc43996c3e2d114fa32ba1e71192a (patch)
tree0ad94849a7bc3e97cd19978005d1898c8bb44b36
parentf9dbd05bc97d1d4f17c2057612f6a8e4dbd039e0 (diff)
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>
-rw-r--r--drivers/isdn/capi/capifs.c3
-rw-r--r--fs/affs/super.c3
-rw-r--r--fs/afs/super.c4
-rw-r--r--fs/hpfs/super.c3
-rw-r--r--fs/namespace.c21
-rw-r--r--fs/reiserfs/super.c3
-rw-r--r--include/linux/fs.h1
7 files changed, 25 insertions, 13 deletions
diff --git a/drivers/isdn/capi/capifs.c b/drivers/isdn/capi/capifs.c
index b129409925af..8f9f3b5a3e8c 100644
--- a/drivers/isdn/capi/capifs.c
+++ b/drivers/isdn/capi/capifs.c
@@ -75,8 +75,7 @@ static int capifs_remount(struct super_block *s, int *flags, char *data)
75 } 75 }
76 } 76 }
77 77
78 kfree(s->s_options); 78 replace_mount_options(s, new_opt);
79 s->s_options = new_opt;
80 79
81 config.setuid = setuid; 80 config.setuid = setuid;
82 config.setgid = setgid; 81 config.setgid = setgid;
diff --git a/fs/affs/super.c b/fs/affs/super.c
index 5ce695e707fe..63f5183f263b 100644
--- a/fs/affs/super.c
+++ b/fs/affs/super.c
@@ -507,8 +507,7 @@ affs_remount(struct super_block *sb, int *flags, char *data)
507 kfree(new_opts); 507 kfree(new_opts);
508 return -EINVAL; 508 return -EINVAL;
509 } 509 }
510 kfree(sb->s_options); 510 replace_mount_options(sb, new_opts);
511 sb->s_options = new_opts;
512 511
513 sbi->s_flags = mount_flags; 512 sbi->s_flags = mount_flags;
514 sbi->s_mode = mode; 513 sbi->s_mode = mode;
diff --git a/fs/afs/super.c b/fs/afs/super.c
index 2753f16dd315..76828e5f8a39 100644
--- a/fs/afs/super.c
+++ b/fs/afs/super.c
@@ -408,17 +408,17 @@ static int afs_get_sb(struct file_system_type *fs_type,
408 deactivate_locked_super(sb); 408 deactivate_locked_super(sb);
409 goto error; 409 goto error;
410 } 410 }
411 sb->s_options = new_opts; 411 save_mount_options(sb, new_opts);
412 sb->s_flags |= MS_ACTIVE; 412 sb->s_flags |= MS_ACTIVE;
413 } else { 413 } else {
414 _debug("reuse"); 414 _debug("reuse");
415 kfree(new_opts);
416 ASSERTCMP(sb->s_flags, &, MS_ACTIVE); 415 ASSERTCMP(sb->s_flags, &, MS_ACTIVE);
417 } 416 }
418 417
419 simple_set_mnt(mnt, sb); 418 simple_set_mnt(mnt, sb);
420 afs_put_volume(params.volume); 419 afs_put_volume(params.volume);
421 afs_put_cell(params.cell); 420 afs_put_cell(params.cell);
421 kfree(new_opts);
422 _leave(" = 0 [%p]", sb); 422 _leave(" = 0 [%p]", sb);
423 return 0; 423 return 0;
424 424
diff --git a/fs/hpfs/super.c b/fs/hpfs/super.c
index fecf402d7b8a..fc77965be841 100644
--- a/fs/hpfs/super.c
+++ b/fs/hpfs/super.c
@@ -423,8 +423,7 @@ static int hpfs_remount_fs(struct super_block *s, int *flags, char *data)
423 423
424 if (!(*flags & MS_RDONLY)) mark_dirty(s); 424 if (!(*flags & MS_RDONLY)) mark_dirty(s);
425 425
426 kfree(s->s_options); 426 replace_mount_options(s, new_opts);
427 s->s_options = new_opts;
428 427
429 return 0; 428 return 0;
430 429
diff --git a/fs/namespace.c b/fs/namespace.c
index 0d2003fb4377..134d494158d9 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -695,12 +695,16 @@ static inline void mangle(struct seq_file *m, const char *s)
695 */ 695 */
696int generic_show_options(struct seq_file *m, struct vfsmount *mnt) 696int generic_show_options(struct seq_file *m, struct vfsmount *mnt)
697{ 697{
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);
699 702
700 if (options != NULL && options[0]) { 703 if (options != NULL && options[0]) {
701 seq_putc(m, ','); 704 seq_putc(m, ',');
702 mangle(m, options); 705 mangle(m, options);
703 } 706 }
707 rcu_read_unlock();
704 708
705 return 0; 709 return 0;
706} 710}
@@ -721,11 +725,22 @@ EXPORT_SYMBOL(generic_show_options);
721 */ 725 */
722void save_mount_options(struct super_block *sb, char *options) 726void save_mount_options(struct super_block *sb, char *options)
723{ 727{
724 kfree(sb->s_options); 728 BUG_ON(sb->s_options);
725 sb->s_options = kstrdup(options, GFP_KERNEL); 729 rcu_assign_pointer(sb->s_options, kstrdup(options, GFP_KERNEL));
726} 730}
727EXPORT_SYMBOL(save_mount_options); 731EXPORT_SYMBOL(save_mount_options);
728 732
733void 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}
742EXPORT_SYMBOL(replace_mount_options);
743
729#ifdef CONFIG_PROC_FS 744#ifdef CONFIG_PROC_FS
730/* iterator */ 745/* iterator */
731static void *m_start(struct seq_file *m, loff_t *pos) 746static void *m_start(struct seq_file *m, loff_t *pos)
diff --git a/fs/reiserfs/super.c b/fs/reiserfs/super.c
index d444fe0013a4..1215a4f50cd2 100644
--- a/fs/reiserfs/super.c
+++ b/fs/reiserfs/super.c
@@ -1316,8 +1316,7 @@ static int reiserfs_remount(struct super_block *s, int *mount_flags, char *arg)
1316 } 1316 }
1317 1317
1318out_ok: 1318out_ok:
1319 kfree(s->s_options); 1319 replace_mount_options(s, new_opts);
1320 s->s_options = new_opts;
1321 return 0; 1320 return 0;
1322 1321
1323out_err: 1322out_err:
diff --git a/include/linux/fs.h b/include/linux/fs.h
index ed788426f464..3b534e527e09 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -2368,6 +2368,7 @@ extern void file_update_time(struct file *file);
2368 2368
2369extern int generic_show_options(struct seq_file *m, struct vfsmount *mnt); 2369extern int generic_show_options(struct seq_file *m, struct vfsmount *mnt);
2370extern void save_mount_options(struct super_block *sb, char *options); 2370extern void save_mount_options(struct super_block *sb, char *options);
2371extern void replace_mount_options(struct super_block *sb, char *options);
2371 2372
2372static inline ino_t parent_ino(struct dentry *dentry) 2373static inline ino_t parent_ino(struct dentry *dentry)
2373{ 2374{