get_mm_cmdline() leaks an uninitialized byte located at user-controlled offset in a newly-allocated kernel page in the following scenario. - When reading the last chunk of cmdline, access_remote_vm() fails to copy the requested number of bytes, but still copies enough bytes so that we get into the body of "if (pos + got >= arg_end)" statement. This can be arranged by user, for example, by applying mprotect(PROT_NONE) to the env block. - strnlen() doesn't find a NUL byte. This too can be arranged by user via suitable modifications of argument and env blocks. - The above causes the following condition to be true despite that no NUL byte was found: /* Include the NUL if it existed */ if (got < size) got++; The resulting increment causes the subsequent copy_to_user() to copy an extra byte from "page" to userspace. That byte might come from previous uses of memory referred by "page" before it was allocated by get_mm_cmdline(), potentially leaking data belonging to other processes or kernel. Fix this by ensuring that "size + offset" doesn't exceed the number of bytes copied by access_remote_vm(). Fixes: f5b65348fd77 ("proc: fix missing final NUL in get_mm_cmdline() rewrite") Cc: Alexey Dobriyan <adobriyan@xxxxxxxxx> Cc: Oleg Nesterov <oleg@xxxxxxxxxx> Signed-off-by: Alexey Izbyshev <izbyshev@xxxxxxxxx> --- This patch was initially sent to <security@xxxxxxxxxx> accompanied with a little program that exploits the bug to dump the kernel page used in get_mm_cmdline(). Thanks to Oleg Nesterov and Laura Abbott for their feedback! fs/proc/base.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/proc/base.c b/fs/proc/base.c index 255f6754c70d..6e30dd791761 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -275,6 +275,8 @@ static ssize_t get_mm_cmdline(struct mm_struct *mm, char __user *buf, if (got <= offset) break; got -= offset; + if (got < size) + size = got; /* Don't walk past a NUL character once you hit arg_end */ if (pos + got >= arg_end) { -- 2.21.0