mirror of
				https://github.com/torvalds/linux.git
				synced 2025-10-31 00:28:52 +02:00 
			
		
		
		
	Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
Pull more vfs updates from Al Viro: "Embarrassing braino fix + pipe page accounting + fixing an eyesore in find_filesystem() (checking that s1 is equal to prefix of s2 of given length can be done in many ways, but "compare strlen(s1) with length and then do strncmp()" is not a good one...)" * 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs: [regression] fix braino in fs/dlm/user.c pipe: limit the per-user amount of pages allocated in pipes find_filesystem(): simplify comparison
This commit is contained in:
		
						commit
						eadee0ce6f
					
				
					 7 changed files with 91 additions and 6 deletions
				
			
		|  | @ -32,6 +32,8 @@ Currently, these files are in /proc/sys/fs: | ||||||
| - nr_open | - nr_open | ||||||
| - overflowuid | - overflowuid | ||||||
| - overflowgid | - overflowgid | ||||||
|  | - pipe-user-pages-hard | ||||||
|  | - pipe-user-pages-soft | ||||||
| - protected_hardlinks | - protected_hardlinks | ||||||
| - protected_symlinks | - protected_symlinks | ||||||
| - suid_dumpable | - suid_dumpable | ||||||
|  | @ -159,6 +161,27 @@ The default is 65534. | ||||||
| 
 | 
 | ||||||
| ============================================================== | ============================================================== | ||||||
| 
 | 
 | ||||||
|  | pipe-user-pages-hard: | ||||||
|  | 
 | ||||||
|  | Maximum total number of pages a non-privileged user may allocate for pipes. | ||||||
|  | Once this limit is reached, no new pipes may be allocated until usage goes | ||||||
|  | below the limit again. When set to 0, no limit is applied, which is the default | ||||||
|  | setting. | ||||||
|  | 
 | ||||||
|  | ============================================================== | ||||||
|  | 
 | ||||||
|  | pipe-user-pages-soft: | ||||||
|  | 
 | ||||||
|  | Maximum total number of pages a non-privileged user may allocate for pipes | ||||||
|  | before the pipe size gets limited to a single page. Once this limit is reached, | ||||||
|  | new pipes will be limited to a single page in size for this user in order to | ||||||
|  | limit total memory usage, and trying to increase them using fcntl() will be | ||||||
|  | denied until usage goes below the limit again. The default value allows to | ||||||
|  | allocate up to 1024 pipes at their default size. When set to 0, no limit is | ||||||
|  | applied. | ||||||
|  | 
 | ||||||
|  | ============================================================== | ||||||
|  | 
 | ||||||
| protected_hardlinks: | protected_hardlinks: | ||||||
| 
 | 
 | ||||||
| A long-standing class of security issues is the hardlink-based | A long-standing class of security issues is the hardlink-based | ||||||
|  |  | ||||||
|  | @ -516,7 +516,7 @@ static ssize_t device_write(struct file *file, const char __user *buf, | ||||||
| 		return -EINVAL; | 		return -EINVAL; | ||||||
| 
 | 
 | ||||||
| 	kbuf = memdup_user_nul(buf, count); | 	kbuf = memdup_user_nul(buf, count); | ||||||
| 	if (!IS_ERR(kbuf)) | 	if (IS_ERR(kbuf)) | ||||||
| 		return PTR_ERR(kbuf); | 		return PTR_ERR(kbuf); | ||||||
| 
 | 
 | ||||||
| 	if (check_version(kbuf)) { | 	if (check_version(kbuf)) { | ||||||
|  |  | ||||||
|  | @ -46,9 +46,9 @@ void put_filesystem(struct file_system_type *fs) | ||||||
| static struct file_system_type **find_filesystem(const char *name, unsigned len) | static struct file_system_type **find_filesystem(const char *name, unsigned len) | ||||||
| { | { | ||||||
| 	struct file_system_type **p; | 	struct file_system_type **p; | ||||||
| 	for (p=&file_systems; *p; p=&(*p)->next) | 	for (p = &file_systems; *p; p = &(*p)->next) | ||||||
| 		if (strlen((*p)->name) == len && | 		if (strncmp((*p)->name, name, len) == 0 && | ||||||
| 		    strncmp((*p)->name, name, len) == 0) | 		    !(*p)->name[len]) | ||||||
| 			break; | 			break; | ||||||
| 	return p; | 	return p; | ||||||
| } | } | ||||||
|  |  | ||||||
							
								
								
									
										47
									
								
								fs/pipe.c
									
									
									
									
									
								
							
							
						
						
									
										47
									
								
								fs/pipe.c
									
									
									
									
									
								
							|  | @ -38,6 +38,12 @@ unsigned int pipe_max_size = 1048576; | ||||||
|  */ |  */ | ||||||
| unsigned int pipe_min_size = PAGE_SIZE; | unsigned int pipe_min_size = PAGE_SIZE; | ||||||
| 
 | 
 | ||||||
|  | /* Maximum allocatable pages per user. Hard limit is unset by default, soft
 | ||||||
|  |  * matches default values. | ||||||
|  |  */ | ||||||
|  | unsigned long pipe_user_pages_hard; | ||||||
|  | unsigned long pipe_user_pages_soft = PIPE_DEF_BUFFERS * INR_OPEN_CUR; | ||||||
|  | 
 | ||||||
| /*
 | /*
 | ||||||
|  * We use a start+len construction, which provides full use of the  |  * We use a start+len construction, which provides full use of the  | ||||||
|  * allocated memory. |  * allocated memory. | ||||||
|  | @ -583,20 +589,49 @@ pipe_fasync(int fd, struct file *filp, int on) | ||||||
| 	return retval; | 	return retval; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static void account_pipe_buffers(struct pipe_inode_info *pipe, | ||||||
|  |                                  unsigned long old, unsigned long new) | ||||||
|  | { | ||||||
|  | 	atomic_long_add(new - old, &pipe->user->pipe_bufs); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool too_many_pipe_buffers_soft(struct user_struct *user) | ||||||
|  | { | ||||||
|  | 	return pipe_user_pages_soft && | ||||||
|  | 	       atomic_long_read(&user->pipe_bufs) >= pipe_user_pages_soft; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool too_many_pipe_buffers_hard(struct user_struct *user) | ||||||
|  | { | ||||||
|  | 	return pipe_user_pages_hard && | ||||||
|  | 	       atomic_long_read(&user->pipe_bufs) >= pipe_user_pages_hard; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| struct pipe_inode_info *alloc_pipe_info(void) | struct pipe_inode_info *alloc_pipe_info(void) | ||||||
| { | { | ||||||
| 	struct pipe_inode_info *pipe; | 	struct pipe_inode_info *pipe; | ||||||
| 
 | 
 | ||||||
| 	pipe = kzalloc(sizeof(struct pipe_inode_info), GFP_KERNEL); | 	pipe = kzalloc(sizeof(struct pipe_inode_info), GFP_KERNEL); | ||||||
| 	if (pipe) { | 	if (pipe) { | ||||||
| 		pipe->bufs = kzalloc(sizeof(struct pipe_buffer) * PIPE_DEF_BUFFERS, GFP_KERNEL); | 		unsigned long pipe_bufs = PIPE_DEF_BUFFERS; | ||||||
|  | 		struct user_struct *user = get_current_user(); | ||||||
|  | 
 | ||||||
|  | 		if (!too_many_pipe_buffers_hard(user)) { | ||||||
|  | 			if (too_many_pipe_buffers_soft(user)) | ||||||
|  | 				pipe_bufs = 1; | ||||||
|  | 			pipe->bufs = kzalloc(sizeof(struct pipe_buffer) * pipe_bufs, GFP_KERNEL); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
| 		if (pipe->bufs) { | 		if (pipe->bufs) { | ||||||
| 			init_waitqueue_head(&pipe->wait); | 			init_waitqueue_head(&pipe->wait); | ||||||
| 			pipe->r_counter = pipe->w_counter = 1; | 			pipe->r_counter = pipe->w_counter = 1; | ||||||
| 			pipe->buffers = PIPE_DEF_BUFFERS; | 			pipe->buffers = pipe_bufs; | ||||||
|  | 			pipe->user = user; | ||||||
|  | 			account_pipe_buffers(pipe, 0, pipe_bufs); | ||||||
| 			mutex_init(&pipe->mutex); | 			mutex_init(&pipe->mutex); | ||||||
| 			return pipe; | 			return pipe; | ||||||
| 		} | 		} | ||||||
|  | 		free_uid(user); | ||||||
| 		kfree(pipe); | 		kfree(pipe); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | @ -607,6 +642,8 @@ void free_pipe_info(struct pipe_inode_info *pipe) | ||||||
| { | { | ||||||
| 	int i; | 	int i; | ||||||
| 
 | 
 | ||||||
|  | 	account_pipe_buffers(pipe, pipe->buffers, 0); | ||||||
|  | 	free_uid(pipe->user); | ||||||
| 	for (i = 0; i < pipe->buffers; i++) { | 	for (i = 0; i < pipe->buffers; i++) { | ||||||
| 		struct pipe_buffer *buf = pipe->bufs + i; | 		struct pipe_buffer *buf = pipe->bufs + i; | ||||||
| 		if (buf->ops) | 		if (buf->ops) | ||||||
|  | @ -998,6 +1035,7 @@ static long pipe_set_size(struct pipe_inode_info *pipe, unsigned long nr_pages) | ||||||
| 			memcpy(bufs + head, pipe->bufs, tail * sizeof(struct pipe_buffer)); | 			memcpy(bufs + head, pipe->bufs, tail * sizeof(struct pipe_buffer)); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	account_pipe_buffers(pipe, pipe->buffers, nr_pages); | ||||||
| 	pipe->curbuf = 0; | 	pipe->curbuf = 0; | ||||||
| 	kfree(pipe->bufs); | 	kfree(pipe->bufs); | ||||||
| 	pipe->bufs = bufs; | 	pipe->bufs = bufs; | ||||||
|  | @ -1069,6 +1107,11 @@ long pipe_fcntl(struct file *file, unsigned int cmd, unsigned long arg) | ||||||
| 		if (!capable(CAP_SYS_RESOURCE) && size > pipe_max_size) { | 		if (!capable(CAP_SYS_RESOURCE) && size > pipe_max_size) { | ||||||
| 			ret = -EPERM; | 			ret = -EPERM; | ||||||
| 			goto out; | 			goto out; | ||||||
|  | 		} else if ((too_many_pipe_buffers_hard(pipe->user) || | ||||||
|  | 			    too_many_pipe_buffers_soft(pipe->user)) && | ||||||
|  | 		           !capable(CAP_SYS_RESOURCE) && !capable(CAP_SYS_ADMIN)) { | ||||||
|  | 			ret = -EPERM; | ||||||
|  | 			goto out; | ||||||
| 		} | 		} | ||||||
| 		ret = pipe_set_size(pipe, nr_pages); | 		ret = pipe_set_size(pipe, nr_pages); | ||||||
| 		break; | 		break; | ||||||
|  |  | ||||||
|  | @ -42,6 +42,7 @@ struct pipe_buffer { | ||||||
|  *	@fasync_readers: reader side fasync |  *	@fasync_readers: reader side fasync | ||||||
|  *	@fasync_writers: writer side fasync |  *	@fasync_writers: writer side fasync | ||||||
|  *	@bufs: the circular array of pipe buffers |  *	@bufs: the circular array of pipe buffers | ||||||
|  |  *	@user: the user who created this pipe | ||||||
|  **/ |  **/ | ||||||
| struct pipe_inode_info { | struct pipe_inode_info { | ||||||
| 	struct mutex mutex; | 	struct mutex mutex; | ||||||
|  | @ -57,6 +58,7 @@ struct pipe_inode_info { | ||||||
| 	struct fasync_struct *fasync_readers; | 	struct fasync_struct *fasync_readers; | ||||||
| 	struct fasync_struct *fasync_writers; | 	struct fasync_struct *fasync_writers; | ||||||
| 	struct pipe_buffer *bufs; | 	struct pipe_buffer *bufs; | ||||||
|  | 	struct user_struct *user; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| /*
 | /*
 | ||||||
|  | @ -123,6 +125,8 @@ void pipe_unlock(struct pipe_inode_info *); | ||||||
| void pipe_double_lock(struct pipe_inode_info *, struct pipe_inode_info *); | void pipe_double_lock(struct pipe_inode_info *, struct pipe_inode_info *); | ||||||
| 
 | 
 | ||||||
| extern unsigned int pipe_max_size, pipe_min_size; | extern unsigned int pipe_max_size, pipe_min_size; | ||||||
|  | extern unsigned long pipe_user_pages_hard; | ||||||
|  | extern unsigned long pipe_user_pages_soft; | ||||||
| int pipe_proc_fn(struct ctl_table *, int, void __user *, size_t *, loff_t *); | int pipe_proc_fn(struct ctl_table *, int, void __user *, size_t *, loff_t *); | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -835,6 +835,7 @@ struct user_struct { | ||||||
| #endif | #endif | ||||||
| 	unsigned long locked_shm; /* How many pages of mlocked shm ? */ | 	unsigned long locked_shm; /* How many pages of mlocked shm ? */ | ||||||
| 	unsigned long unix_inflight;	/* How many files in flight in unix sockets */ | 	unsigned long unix_inflight;	/* How many files in flight in unix sockets */ | ||||||
|  | 	atomic_long_t pipe_bufs;  /* how many pages are allocated in pipe buffers */ | ||||||
| 
 | 
 | ||||||
| #ifdef CONFIG_KEYS | #ifdef CONFIG_KEYS | ||||||
| 	struct key *uid_keyring;	/* UID specific keyring */ | 	struct key *uid_keyring;	/* UID specific keyring */ | ||||||
|  |  | ||||||
|  | @ -1757,6 +1757,20 @@ static struct ctl_table fs_table[] = { | ||||||
| 		.proc_handler	= &pipe_proc_fn, | 		.proc_handler	= &pipe_proc_fn, | ||||||
| 		.extra1		= &pipe_min_size, | 		.extra1		= &pipe_min_size, | ||||||
| 	}, | 	}, | ||||||
|  | 	{ | ||||||
|  | 		.procname	= "pipe-user-pages-hard", | ||||||
|  | 		.data		= &pipe_user_pages_hard, | ||||||
|  | 		.maxlen		= sizeof(pipe_user_pages_hard), | ||||||
|  | 		.mode		= 0644, | ||||||
|  | 		.proc_handler	= proc_doulongvec_minmax, | ||||||
|  | 	}, | ||||||
|  | 	{ | ||||||
|  | 		.procname	= "pipe-user-pages-soft", | ||||||
|  | 		.data		= &pipe_user_pages_soft, | ||||||
|  | 		.maxlen		= sizeof(pipe_user_pages_soft), | ||||||
|  | 		.mode		= 0644, | ||||||
|  | 		.proc_handler	= proc_doulongvec_minmax, | ||||||
|  | 	}, | ||||||
| 	{ } | 	{ } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 Linus Torvalds
						Linus Torvalds