mirror of
				git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
				synced 2025-10-31 16:54:21 +00:00 
			
		
		
		
	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:
		
							parent
							
								
									f9dbd05bc9
								
							
						
					
					
						commit
						2a32cebd6c
					
				
					 7 changed files with 25 additions and 13 deletions
				
			
		|  | @ -75,8 +75,7 @@ static int capifs_remount(struct super_block *s, int *flags, char *data) | |||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	kfree(s->s_options); | ||||
| 	s->s_options = new_opt; | ||||
| 	replace_mount_options(s, new_opt); | ||||
| 
 | ||||
| 	config.setuid  = setuid; | ||||
| 	config.setgid  = setgid; | ||||
|  |  | |||
|  | @ -507,8 +507,7 @@ affs_remount(struct super_block *sb, int *flags, char *data) | |||
| 		kfree(new_opts); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 	kfree(sb->s_options); | ||||
| 	sb->s_options = new_opts; | ||||
| 	replace_mount_options(sb, new_opts); | ||||
| 
 | ||||
| 	sbi->s_flags = mount_flags; | ||||
| 	sbi->s_mode  = mode; | ||||
|  |  | |||
|  | @ -408,17 +408,17 @@ static int afs_get_sb(struct file_system_type *fs_type, | |||
| 			deactivate_locked_super(sb); | ||||
| 			goto error; | ||||
| 		} | ||||
| 		sb->s_options = new_opts; | ||||
| 		save_mount_options(sb, new_opts); | ||||
| 		sb->s_flags |= MS_ACTIVE; | ||||
| 	} else { | ||||
| 		_debug("reuse"); | ||||
| 		kfree(new_opts); | ||||
| 		ASSERTCMP(sb->s_flags, &, MS_ACTIVE); | ||||
| 	} | ||||
| 
 | ||||
| 	simple_set_mnt(mnt, sb); | ||||
| 	afs_put_volume(params.volume); | ||||
| 	afs_put_cell(params.cell); | ||||
| 	kfree(new_opts); | ||||
| 	_leave(" = 0 [%p]", sb); | ||||
| 	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); | ||||
| 
 | ||||
| 	kfree(s->s_options); | ||||
| 	s->s_options = new_opts; | ||||
| 	replace_mount_options(s, new_opts); | ||||
| 
 | ||||
| 	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) | ||||
| { | ||||
| 	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]) { | ||||
| 		seq_putc(m, ','); | ||||
| 		mangle(m, options); | ||||
| 	} | ||||
| 	rcu_read_unlock(); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
|  | @ -721,11 +725,22 @@ EXPORT_SYMBOL(generic_show_options); | |||
|  */ | ||||
| void save_mount_options(struct super_block *sb, char *options) | ||||
| { | ||||
| 	kfree(sb->s_options); | ||||
| 	sb->s_options = kstrdup(options, GFP_KERNEL); | ||||
| 	BUG_ON(sb->s_options); | ||||
| 	rcu_assign_pointer(sb->s_options, kstrdup(options, GFP_KERNEL)); | ||||
| } | ||||
| 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 | ||||
| /* iterator */ | ||||
| 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: | ||||
| 	kfree(s->s_options); | ||||
| 	s->s_options = new_opts; | ||||
| 	replace_mount_options(s, new_opts); | ||||
| 	return 0; | ||||
| 
 | ||||
| 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 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) | ||||
| { | ||||
|  |  | |||
		Loading…
	
	Add table
		
		Reference in a new issue
	
	 Al Viro
						Al Viro