diff options
| author | Mark Brown <[email protected]> | 2015-10-12 18:09:27 +0100 | 
|---|---|---|
| committer | Mark Brown <[email protected]> | 2015-10-12 18:09:27 +0100 | 
| commit | 79828b4fa835f73cdaf4bffa48696abdcbea9d02 (patch) | |
| tree | 5e0fa7156acb75ba603022bc807df8f2fedb97a8 /fs/proc/base.c | |
| parent | 721b51fcf91898299d96f4b72cb9434cda29dce6 (diff) | |
| parent | 8c1a9d6323abf0fb1e5dad96cf3f1c783505ea5a (diff) | |
Merge remote-tracking branch 'asoc/fix/rt5645' into asoc-fix-rt5645
Diffstat (limited to 'fs/proc/base.c')
| -rw-r--r-- | fs/proc/base.c | 118 | 
1 files changed, 50 insertions, 68 deletions
diff --git a/fs/proc/base.c b/fs/proc/base.c index 87782e874b6a..b25eee4cead5 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -243,6 +243,11 @@ static ssize_t proc_pid_cmdline_read(struct file *file, char __user *buf,  	len1 = arg_end - arg_start;  	len2 = env_end - env_start; +	/* Empty ARGV. */ +	if (len1 == 0) { +		rv = 0; +		goto out_free_page; +	}  	/*  	 * Inherently racy -- command line shares address space  	 * with code and data. @@ -1225,10 +1230,9 @@ static ssize_t proc_loginuid_write(struct file * file, const char __user * buf,  				   size_t count, loff_t *ppos)  {  	struct inode * inode = file_inode(file); -	char *page, *tmp; -	ssize_t length;  	uid_t loginuid;  	kuid_t kloginuid; +	int rv;  	rcu_read_lock();  	if (current != pid_task(proc_pid(inode), PIDTYPE_PID)) { @@ -1237,46 +1241,28 @@ static ssize_t proc_loginuid_write(struct file * file, const char __user * buf,  	}  	rcu_read_unlock(); -	if (count >= PAGE_SIZE) -		count = PAGE_SIZE - 1; -  	if (*ppos != 0) {  		/* No partial writes. */  		return -EINVAL;  	} -	page = (char*)__get_free_page(GFP_TEMPORARY); -	if (!page) -		return -ENOMEM; -	length = -EFAULT; -	if (copy_from_user(page, buf, count)) -		goto out_free_page; -	page[count] = '\0'; -	loginuid = simple_strtoul(page, &tmp, 10); -	if (tmp == page) { -		length = -EINVAL; -		goto out_free_page; - -	} +	rv = kstrtou32_from_user(buf, count, 10, &loginuid); +	if (rv < 0) +		return rv;  	/* is userspace tring to explicitly UNSET the loginuid? */  	if (loginuid == AUDIT_UID_UNSET) {  		kloginuid = INVALID_UID;  	} else {  		kloginuid = make_kuid(file->f_cred->user_ns, loginuid); -		if (!uid_valid(kloginuid)) { -			length = -EINVAL; -			goto out_free_page; -		} +		if (!uid_valid(kloginuid)) +			return -EINVAL;  	} -	length = audit_set_loginuid(kloginuid); -	if (likely(length == 0)) -		length = count; - -out_free_page: -	free_page((unsigned long) page); -	return length; +	rv = audit_set_loginuid(kloginuid); +	if (rv < 0) +		return rv; +	return count;  }  static const struct file_operations proc_loginuid_operations = { @@ -1330,8 +1316,9 @@ static ssize_t proc_fault_inject_write(struct file * file,  			const char __user * buf, size_t count, loff_t *ppos)  {  	struct task_struct *task; -	char buffer[PROC_NUMBUF], *end; +	char buffer[PROC_NUMBUF];  	int make_it_fail; +	int rv;  	if (!capable(CAP_SYS_RESOURCE))  		return -EPERM; @@ -1340,9 +1327,9 @@ static ssize_t proc_fault_inject_write(struct file * file,  		count = sizeof(buffer) - 1;  	if (copy_from_user(buffer, buf, count))  		return -EFAULT; -	make_it_fail = simple_strtol(strstrip(buffer), &end, 0); -	if (*end) -		return -EINVAL; +	rv = kstrtoint(strstrip(buffer), 0, &make_it_fail); +	if (rv < 0) +		return rv;  	if (make_it_fail < 0 || make_it_fail > 1)  		return -EINVAL; @@ -1831,8 +1818,6 @@ end_instantiate:  	return dir_emit(ctx, name, len, 1, DT_UNKNOWN);  } -#ifdef CONFIG_CHECKPOINT_RESTORE -  /*   * dname_to_vma_addr - maps a dentry name into two unsigned longs   * which represent vma start and end addresses. @@ -1859,11 +1844,6 @@ static int map_files_d_revalidate(struct dentry *dentry, unsigned int flags)  	if (flags & LOOKUP_RCU)  		return -ECHILD; -	if (!capable(CAP_SYS_ADMIN)) { -		status = -EPERM; -		goto out_notask; -	} -  	inode = d_inode(dentry);  	task = get_proc_task(inode);  	if (!task) @@ -1952,6 +1932,29 @@ struct map_files_info {  	unsigned char	name[4*sizeof(long)+2]; /* max: %lx-%lx\0 */  }; +/* + * Only allow CAP_SYS_ADMIN to follow the links, due to concerns about how the + * symlinks may be used to bypass permissions on ancestor directories in the + * path to the file in question. + */ +static const char * +proc_map_files_follow_link(struct dentry *dentry, void **cookie) +{ +	if (!capable(CAP_SYS_ADMIN)) +		return ERR_PTR(-EPERM); + +	return proc_pid_follow_link(dentry, NULL); +} + +/* + * Identical to proc_pid_link_inode_operations except for follow_link() + */ +static const struct inode_operations proc_map_files_link_inode_operations = { +	.readlink	= proc_pid_readlink, +	.follow_link	= proc_map_files_follow_link, +	.setattr	= proc_setattr, +}; +  static int  proc_map_files_instantiate(struct inode *dir, struct dentry *dentry,  			   struct task_struct *task, const void *ptr) @@ -1967,7 +1970,7 @@ proc_map_files_instantiate(struct inode *dir, struct dentry *dentry,  	ei = PROC_I(inode);  	ei->op.proc_get_link = proc_map_files_get_link; -	inode->i_op = &proc_pid_link_inode_operations; +	inode->i_op = &proc_map_files_link_inode_operations;  	inode->i_size = 64;  	inode->i_mode = S_IFLNK; @@ -1991,10 +1994,6 @@ static struct dentry *proc_map_files_lookup(struct inode *dir,  	int result;  	struct mm_struct *mm; -	result = -EPERM; -	if (!capable(CAP_SYS_ADMIN)) -		goto out; -  	result = -ENOENT;  	task = get_proc_task(dir);  	if (!task) @@ -2048,10 +2047,6 @@ proc_map_files_readdir(struct file *file, struct dir_context *ctx)  	struct map_files_info *p;  	int ret; -	ret = -EPERM; -	if (!capable(CAP_SYS_ADMIN)) -		goto out; -  	ret = -ENOENT;  	task = get_proc_task(file_inode(file));  	if (!task) @@ -2240,7 +2235,6 @@ static const struct file_operations proc_timers_operations = {  	.llseek		= seq_lseek,  	.release	= seq_release_private,  }; -#endif /* CONFIG_CHECKPOINT_RESTORE */  static int proc_pident_instantiate(struct inode *dir,  	struct dentry *dentry, struct task_struct *task, const void *ptr) @@ -2476,32 +2470,20 @@ static ssize_t proc_coredump_filter_write(struct file *file,  {  	struct task_struct *task;  	struct mm_struct *mm; -	char buffer[PROC_NUMBUF], *end;  	unsigned int val;  	int ret;  	int i;  	unsigned long mask; -	ret = -EFAULT; -	memset(buffer, 0, sizeof(buffer)); -	if (count > sizeof(buffer) - 1) -		count = sizeof(buffer) - 1; -	if (copy_from_user(buffer, buf, count)) -		goto out_no_task; - -	ret = -EINVAL; -	val = (unsigned int)simple_strtoul(buffer, &end, 0); -	if (*end == '\n') -		end++; -	if (end - buffer == 0) -		goto out_no_task; +	ret = kstrtouint_from_user(buf, count, 0, &val); +	if (ret < 0) +		return ret;  	ret = -ESRCH;  	task = get_proc_task(file_inode(file));  	if (!task)  		goto out_no_task; -	ret = end - buffer;  	mm = get_task_mm(task);  	if (!mm)  		goto out_no_mm; @@ -2517,7 +2499,9 @@ static ssize_t proc_coredump_filter_write(struct file *file,   out_no_mm:  	put_task_struct(task);   out_no_task: -	return ret; +	if (ret < 0) +		return ret; +	return count;  }  static const struct file_operations proc_coredump_filter_operations = { @@ -2739,9 +2723,7 @@ static const struct inode_operations proc_task_inode_operations;  static const struct pid_entry tgid_base_stuff[] = {  	DIR("task",       S_IRUGO|S_IXUGO, proc_task_inode_operations, proc_task_operations),  	DIR("fd",         S_IRUSR|S_IXUSR, proc_fd_inode_operations, proc_fd_operations), -#ifdef CONFIG_CHECKPOINT_RESTORE  	DIR("map_files",  S_IRUSR|S_IXUSR, proc_map_files_inode_operations, proc_map_files_operations), -#endif  	DIR("fdinfo",     S_IRUSR|S_IXUSR, proc_fdinfo_inode_operations, proc_fdinfo_operations),  	DIR("ns",	  S_IRUSR|S_IXUGO, proc_ns_dir_inode_operations, proc_ns_dir_operations),  #ifdef CONFIG_NET  |