diff options
author | Kees Cook <keescook@chromium.org> | 2017-07-10 18:52:54 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2017-07-10 19:32:36 -0400 |
commit | 67c6777a5d331dda32a4c4a1bf0cac85bdaaaed8 (patch) | |
tree | c5ae9869b8e0a3e81091bb08597ea54346655824 | |
parent | a73dc5370e153ac63718d850bddf0c9aa9d871e6 (diff) |
binfmt_elf: safely increment argv pointers
When building the argv/envp pointers, the envp is needlessly
pre-incremented instead of just continuing after the argv pointers are
finished. In some (likely impossible) race where the strings could be
changed from userspace between copy_strings() and here, it might be
possible to confuse the envp position. Instead, just use sp like
everything else.
Link: http://lkml.kernel.org/r/20170622173838.GA43308@beast
Signed-off-by: Kees Cook <keescook@chromium.org>
Cc: Rik van Riel <riel@redhat.com>
Cc: Daniel Micay <danielmicay@gmail.com>
Cc: Qualys Security Advisory <qsa@qualys.com>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: "H. Peter Anvin" <hpa@zytor.com>
Cc: Alexander Viro <viro@zeniv.linux.org.uk>
Cc: Dmitry Safonov <dsafonov@virtuozzo.com>
Cc: Andy Lutomirski <luto@amacapital.net>
Cc: Grzegorz Andrejczuk <grzegorz.andrejczuk@intel.com>
Cc: Masahiro Yamada <yamada.masahiro@socionext.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r-- | fs/binfmt_elf.c | 20 |
1 files changed, 9 insertions, 11 deletions
diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index 7465c3ea5dd5..879ff9c7ffd0 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c | |||
@@ -163,8 +163,6 @@ create_elf_tables(struct linux_binprm *bprm, struct elfhdr *exec, | |||
163 | unsigned long p = bprm->p; | 163 | unsigned long p = bprm->p; |
164 | int argc = bprm->argc; | 164 | int argc = bprm->argc; |
165 | int envc = bprm->envc; | 165 | int envc = bprm->envc; |
166 | elf_addr_t __user *argv; | ||
167 | elf_addr_t __user *envp; | ||
168 | elf_addr_t __user *sp; | 166 | elf_addr_t __user *sp; |
169 | elf_addr_t __user *u_platform; | 167 | elf_addr_t __user *u_platform; |
170 | elf_addr_t __user *u_base_platform; | 168 | elf_addr_t __user *u_base_platform; |
@@ -304,38 +302,38 @@ create_elf_tables(struct linux_binprm *bprm, struct elfhdr *exec, | |||
304 | /* Now, let's put argc (and argv, envp if appropriate) on the stack */ | 302 | /* Now, let's put argc (and argv, envp if appropriate) on the stack */ |
305 | if (__put_user(argc, sp++)) | 303 | if (__put_user(argc, sp++)) |
306 | return -EFAULT; | 304 | return -EFAULT; |
307 | argv = sp; | ||
308 | envp = argv + argc + 1; | ||
309 | 305 | ||
310 | /* Populate argv and envp */ | 306 | /* Populate list of argv pointers back to argv strings. */ |
311 | p = current->mm->arg_end = current->mm->arg_start; | 307 | p = current->mm->arg_end = current->mm->arg_start; |
312 | while (argc-- > 0) { | 308 | while (argc-- > 0) { |
313 | size_t len; | 309 | size_t len; |
314 | if (__put_user((elf_addr_t)p, argv++)) | 310 | if (__put_user((elf_addr_t)p, sp++)) |
315 | return -EFAULT; | 311 | return -EFAULT; |
316 | len = strnlen_user((void __user *)p, MAX_ARG_STRLEN); | 312 | len = strnlen_user((void __user *)p, MAX_ARG_STRLEN); |
317 | if (!len || len > MAX_ARG_STRLEN) | 313 | if (!len || len > MAX_ARG_STRLEN) |
318 | return -EINVAL; | 314 | return -EINVAL; |
319 | p += len; | 315 | p += len; |
320 | } | 316 | } |
321 | if (__put_user(0, argv)) | 317 | if (__put_user(0, sp++)) |
322 | return -EFAULT; | 318 | return -EFAULT; |
323 | current->mm->arg_end = current->mm->env_start = p; | 319 | current->mm->arg_end = p; |
320 | |||
321 | /* Populate list of envp pointers back to envp strings. */ | ||
322 | current->mm->env_end = current->mm->env_start = p; | ||
324 | while (envc-- > 0) { | 323 | while (envc-- > 0) { |
325 | size_t len; | 324 | size_t len; |
326 | if (__put_user((elf_addr_t)p, envp++)) | 325 | if (__put_user((elf_addr_t)p, sp++)) |
327 | return -EFAULT; | 326 | return -EFAULT; |
328 | len = strnlen_user((void __user *)p, MAX_ARG_STRLEN); | 327 | len = strnlen_user((void __user *)p, MAX_ARG_STRLEN); |
329 | if (!len || len > MAX_ARG_STRLEN) | 328 | if (!len || len > MAX_ARG_STRLEN) |
330 | return -EINVAL; | 329 | return -EINVAL; |
331 | p += len; | 330 | p += len; |
332 | } | 331 | } |
333 | if (__put_user(0, envp)) | 332 | if (__put_user(0, sp++)) |
334 | return -EFAULT; | 333 | return -EFAULT; |
335 | current->mm->env_end = p; | 334 | current->mm->env_end = p; |
336 | 335 | ||
337 | /* Put the elf_info on the stack in the right place. */ | 336 | /* Put the elf_info on the stack in the right place. */ |
338 | sp = (elf_addr_t __user *)envp + 1; | ||
339 | if (copy_to_user(sp, elf_info, ei_index * sizeof(elf_addr_t))) | 337 | if (copy_to_user(sp, elf_info, ei_index * sizeof(elf_addr_t))) |
340 | return -EFAULT; | 338 | return -EFAULT; |
341 | return 0; | 339 | return 0; |