diff options
Diffstat (limited to 'fs/proc/base.c')
| -rw-r--r-- | fs/proc/base.c | 28 | 
1 files changed, 23 insertions, 5 deletions
| diff --git a/fs/proc/base.c b/fs/proc/base.c index b6572944efc3..aaffc0c30216 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -235,6 +235,10 @@ static ssize_t get_mm_cmdline(struct mm_struct *mm, char __user *buf,  	if (env_start != arg_end || env_start >= env_end)  		env_start = env_end = arg_end; +	/* .. and limit it to a maximum of one page of slop */ +	if (env_end >= arg_end + PAGE_SIZE) +		env_end = arg_end + PAGE_SIZE - 1; +  	/* We're not going to care if "*ppos" has high bits set */  	pos = arg_start + *ppos; @@ -254,10 +258,19 @@ static ssize_t get_mm_cmdline(struct mm_struct *mm, char __user *buf,  	while (count) {  		int got;  		size_t size = min_t(size_t, PAGE_SIZE, count); +		long offset; -		got = access_remote_vm(mm, pos, page, size, FOLL_ANON); -		if (got <= 0) +		/* +		 * Are we already starting past the official end? +		 * We always include the last byte that is *supposed* +		 * to be NUL +		 */ +		offset = (pos >= arg_end) ? pos - arg_end + 1 : 0; + +		got = access_remote_vm(mm, pos - offset, page, size + offset, FOLL_ANON); +		if (got <= offset)  			break; +		got -= offset;  		/* Don't walk past a NUL character once you hit arg_end */  		if (pos + got >= arg_end) { @@ -276,12 +289,17 @@ static ssize_t get_mm_cmdline(struct mm_struct *mm, char __user *buf,  				n = arg_end - pos - 1;  			/* Cut off at first NUL after 'n' */ -			got = n + strnlen(page+n, got-n); -			if (!got) +			got = n + strnlen(page+n, offset+got-n); +			if (got < offset)  				break; +			got -= offset; + +			/* Include the NUL if it existed */ +			if (got < size) +				got++;  		} -		got -= copy_to_user(buf, page, got); +		got -= copy_to_user(buf, page+offset, got);  		if (unlikely(!got)) {  			if (!len)  				len = -EFAULT; |