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
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;
fs/affs/super.c+1−2
@@ -507,8 +507,7 @@ affs_remount(struct super_block *sb, int *flags, char *data) · 506 unmodified lines
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;
fs/afs/super.c+2−2
@@ -408,17 +408,17 @@ static int afs_get_sb(struct file_system_type *fs_type, · 407 unmodified lines
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
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
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
fs/namespace.c+18−3
@@ -695,12 +695,16 @@ static inline void mangle(struct seq_file *m, const char *s) · 694 unmodified lines
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); · 14 unmodified lines
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_FS744#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)
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
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:
include/linux/fs.h+1−0
@@ -2368,6 +2368,7 @@ extern void file_update_time(struct file *file); · 2367 unmodified lines
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{