Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

exec: Implement kernel_execve

To allow the kernel not to play games with set_fs to call exec
implement kernel_execve. The function kernel_execve takes pointers
into kernel memory and copies the values pointed to onto the new
userspace stack.

The calls with arguments from kernel space of do_execve are replaced
with calls to kernel_execve.

The calls do_execve and do_execveat are made static as there are now
no callers outside of exec.

The comments that mention do_execve are updated to refer to
kernel_execve or execve depending on the circumstances. In addition
to correcting the comments, this makes it easy to grep for do_execve
and verify it is not used.

Inspired-by: https://lkml.kernel.org/r/20200627072704.2447163-1-hch@lst.de
Reviewed-by: Kees Cook <keescook@chromium.org>
Link: https://lkml.kernel.org/r/87wo365ikj.fsf@x220.int.ebiederm.org
Signed-off-by: "Eric W. Biederman" <ebiederm@xmission.com>

+100 -23
+1 -1
arch/x86/entry/entry_32.S
··· 854 854 CALL_NOSPEC ebx 855 855 /* 856 856 * A kernel thread is allowed to return here after successfully 857 - * calling do_execve(). Exit to userspace to complete the execve() 857 + * calling kernel_execve(). Exit to userspace to complete the execve() 858 858 * syscall. 859 859 */ 860 860 movl $0, PT_EAX(%esp)
+1 -1
arch/x86/entry/entry_64.S
··· 293 293 CALL_NOSPEC rbx 294 294 /* 295 295 * A kernel thread is allowed to return here after successfully 296 - * calling do_execve(). Exit to userspace to complete the execve() 296 + * calling kernel_execve(). Exit to userspace to complete the execve() 297 297 * syscall. 298 298 */ 299 299 movq $0, RAX(%rsp)
+1 -1
arch/x86/kernel/unwind_frame.c
··· 275 275 * This user_mode() check is slightly broader than a PF_KTHREAD 276 276 * check because it also catches the awkward situation where a 277 277 * newly forked kthread transitions into a user task by calling 278 - * do_execve(), which eventually clears PF_KTHREAD. 278 + * kernel_execve(), which eventually clears PF_KTHREAD. 279 279 */ 280 280 if (!user_mode(regs)) 281 281 goto the_end;
+86 -2
fs/exec.c
··· 448 448 return i; 449 449 } 450 450 451 + static int count_strings_kernel(const char *const *argv) 452 + { 453 + int i; 454 + 455 + if (!argv) 456 + return 0; 457 + 458 + for (i = 0; argv[i]; ++i) { 459 + if (i >= MAX_ARG_STRINGS) 460 + return -E2BIG; 461 + if (fatal_signal_pending(current)) 462 + return -ERESTARTNOHAND; 463 + cond_resched(); 464 + } 465 + return i; 466 + } 467 + 451 468 static int bprm_stack_limits(struct linux_binprm *bprm) 452 469 { 453 470 unsigned long limit, ptr_size; ··· 640 623 return 0; 641 624 } 642 625 EXPORT_SYMBOL(copy_string_kernel); 626 + 627 + static int copy_strings_kernel(int argc, const char *const *argv, 628 + struct linux_binprm *bprm) 629 + { 630 + while (argc-- > 0) { 631 + int ret = copy_string_kernel(argv[argc], bprm); 632 + if (ret < 0) 633 + return ret; 634 + if (fatal_signal_pending(current)) 635 + return -ERESTARTNOHAND; 636 + cond_resched(); 637 + } 638 + return 0; 639 + } 643 640 644 641 #ifdef CONFIG_MMU 645 642 ··· 2022 1991 return retval; 2023 1992 } 2024 1993 2025 - int do_execve(struct filename *filename, 1994 + int kernel_execve(const char *kernel_filename, 1995 + const char *const *argv, const char *const *envp) 1996 + { 1997 + struct filename *filename; 1998 + struct linux_binprm *bprm; 1999 + int fd = AT_FDCWD; 2000 + int retval; 2001 + 2002 + filename = getname_kernel(kernel_filename); 2003 + if (IS_ERR(filename)) 2004 + return PTR_ERR(filename); 2005 + 2006 + bprm = alloc_bprm(fd, filename); 2007 + if (IS_ERR(bprm)) { 2008 + retval = PTR_ERR(bprm); 2009 + goto out_ret; 2010 + } 2011 + 2012 + retval = count_strings_kernel(argv); 2013 + if (retval < 0) 2014 + goto out_free; 2015 + bprm->argc = retval; 2016 + 2017 + retval = count_strings_kernel(envp); 2018 + if (retval < 0) 2019 + goto out_free; 2020 + bprm->envc = retval; 2021 + 2022 + retval = bprm_stack_limits(bprm); 2023 + if (retval < 0) 2024 + goto out_free; 2025 + 2026 + retval = copy_string_kernel(bprm->filename, bprm); 2027 + if (retval < 0) 2028 + goto out_free; 2029 + bprm->exec = bprm->p; 2030 + 2031 + retval = copy_strings_kernel(bprm->envc, envp, bprm); 2032 + if (retval < 0) 2033 + goto out_free; 2034 + 2035 + retval = copy_strings_kernel(bprm->argc, argv, bprm); 2036 + if (retval < 0) 2037 + goto out_free; 2038 + 2039 + retval = bprm_execve(bprm, fd, filename, 0); 2040 + out_free: 2041 + free_bprm(bprm); 2042 + out_ret: 2043 + putname(filename); 2044 + return retval; 2045 + } 2046 + 2047 + static int do_execve(struct filename *filename, 2026 2048 const char __user *const __user *__argv, 2027 2049 const char __user *const __user *__envp) 2028 2050 { ··· 2084 2000 return do_execveat_common(AT_FDCWD, filename, argv, envp, 0); 2085 2001 } 2086 2002 2087 - int do_execveat(int fd, struct filename *filename, 2003 + static int do_execveat(int fd, struct filename *filename, 2088 2004 const char __user *const __user *__argv, 2089 2005 const char __user *const __user *__envp, 2090 2006 int flags)
+2 -7
include/linux/binfmts.h
··· 135 135 extern void set_binfmt(struct linux_binfmt *new); 136 136 extern ssize_t read_code(struct file *, unsigned long, loff_t, size_t); 137 137 138 - extern int do_execve(struct filename *, 139 - const char __user * const __user *, 140 - const char __user * const __user *); 141 - extern int do_execveat(int, struct filename *, 142 - const char __user * const __user *, 143 - const char __user * const __user *, 144 - int); 138 + int kernel_execve(const char *filename, 139 + const char *const *argv, const char *const *envp); 145 140 146 141 #endif /* _LINUX_BINFMTS_H */
+1 -3
init/main.c
··· 1329 1329 pr_debug(" with environment:\n"); 1330 1330 for (p = envp_init; *p; p++) 1331 1331 pr_debug(" %s\n", *p); 1332 - return do_execve(getname_kernel(init_filename), 1333 - (const char __user *const __user *)argv_init, 1334 - (const char __user *const __user *)envp_init); 1332 + return kernel_execve(init_filename, argv_init, envp_init); 1335 1333 } 1336 1334 1337 1335 static int try_to_run_init_process(const char *init_filename)
+3 -3
kernel/umh.c
··· 98 98 99 99 commit_creds(new); 100 100 101 - retval = do_execve(getname_kernel(sub_info->path), 102 - (const char __user *const __user *)sub_info->argv, 103 - (const char __user *const __user *)sub_info->envp); 101 + retval = kernel_execve(sub_info->path, 102 + (const char *const *)sub_info->argv, 103 + (const char *const *)sub_info->envp); 104 104 out: 105 105 sub_info->retval = retval; 106 106 /*
+1 -1
security/tomoyo/common.h
··· 425 425 struct tomoyo_obj_info *obj; 426 426 /* 427 427 * For holding parameters specific to execve() request. 428 - * NULL if not dealing do_execve(). 428 + * NULL if not dealing execve(). 429 429 */ 430 430 struct tomoyo_execve *ee; 431 431 struct tomoyo_domain_info *domain;
+2 -2
security/tomoyo/domain.c
··· 767 767 768 768 /* 769 769 * Check for domain transition preference if "file execute" matched. 770 - * If preference is given, make do_execve() fail if domain transition 770 + * If preference is given, make execve() fail if domain transition 771 771 * has failed, for domain transition preference should be used with 772 772 * destination domain defined. 773 773 */ ··· 810 810 snprintf(ee->tmp, TOMOYO_EXEC_TMPSIZE - 1, "<%s>", 811 811 candidate->name); 812 812 /* 813 - * Make do_execve() fail if domain transition across namespaces 813 + * Make execve() fail if domain transition across namespaces 814 814 * has failed. 815 815 */ 816 816 reject_on_transition_failure = true;
+2 -2
security/tomoyo/tomoyo.c
··· 93 93 struct tomoyo_task *s = tomoyo_task(current); 94 94 95 95 /* 96 - * Execute permission is checked against pathname passed to do_execve() 96 + * Execute permission is checked against pathname passed to execve() 97 97 * using current domain. 98 98 */ 99 99 if (!s->old_domain_info) { ··· 307 307 */ 308 308 static int tomoyo_file_open(struct file *f) 309 309 { 310 - /* Don't check read permission here if called from do_execve(). */ 310 + /* Don't check read permission here if called from execve(). */ 311 311 if (current->in_execve) 312 312 return 0; 313 313 return tomoyo_check_open_permission(tomoyo_domain(), &f->f_path,