proc: fix missing final NUL in get_mm_cmdline() rewrite
The rewrite of the cmdline fetching missed the fact that we used to also return the final terminating NUL character of the last argument. I hadn't noticed, and none of the tools I tested cared, but something obviously must care, because Michal Kubecek noticed the change in behavior. Tweak the "find the end" logic to actually include the NUL character, and once past the eend of argv, always start the strnlen() at the expected (original) argument end. This whole "allow people to rewrite their arguments in place" is a nasty hack and requires that odd slop handling at the end of the argv array, but it's our traditional model, so we continue to support it. Repored-and-bisected-by: Michal Kubecek <mkubecek@suse.cz> Reviewed-and-tested-by: Michal Kubecek <mkubecek@suse.cz> Cc: Alexey Dobriyan <adobriyan@gmail.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
parent
ba4dbdedd3
commit
f5b65348fd
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue