Commit be619f7f authored by Eric W. Biederman's avatar Eric W. Biederman

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.deReviewed-by: default avatarKees Cook <keescook@chromium.org>
Link: https://lkml.kernel.org/r/87wo365ikj.fsf@x220.int.ebiederm.orgSigned-off-by: default avatar"Eric W. Biederman" <ebiederm@xmission.com>
parent d8b9cd54
...@@ -854,7 +854,7 @@ SYM_CODE_START(ret_from_fork) ...@@ -854,7 +854,7 @@ SYM_CODE_START(ret_from_fork)
CALL_NOSPEC ebx CALL_NOSPEC ebx
/* /*
* A kernel thread is allowed to return here after successfully * A kernel thread is allowed to return here after successfully
* calling do_execve(). Exit to userspace to complete the execve() * calling kernel_execve(). Exit to userspace to complete the execve()
* syscall. * syscall.
*/ */
movl $0, PT_EAX(%esp) movl $0, PT_EAX(%esp)
......
...@@ -293,7 +293,7 @@ SYM_CODE_START(ret_from_fork) ...@@ -293,7 +293,7 @@ SYM_CODE_START(ret_from_fork)
CALL_NOSPEC rbx CALL_NOSPEC rbx
/* /*
* A kernel thread is allowed to return here after successfully * A kernel thread is allowed to return here after successfully
* calling do_execve(). Exit to userspace to complete the execve() * calling kernel_execve(). Exit to userspace to complete the execve()
* syscall. * syscall.
*/ */
movq $0, RAX(%rsp) movq $0, RAX(%rsp)
......
...@@ -275,7 +275,7 @@ bool unwind_next_frame(struct unwind_state *state) ...@@ -275,7 +275,7 @@ bool unwind_next_frame(struct unwind_state *state)
* This user_mode() check is slightly broader than a PF_KTHREAD * This user_mode() check is slightly broader than a PF_KTHREAD
* check because it also catches the awkward situation where a * check because it also catches the awkward situation where a
* newly forked kthread transitions into a user task by calling * newly forked kthread transitions into a user task by calling
* do_execve(), which eventually clears PF_KTHREAD. * kernel_execve(), which eventually clears PF_KTHREAD.
*/ */
if (!user_mode(regs)) if (!user_mode(regs))
goto the_end; goto the_end;
......
...@@ -448,6 +448,23 @@ static int count(struct user_arg_ptr argv, int max) ...@@ -448,6 +448,23 @@ static int count(struct user_arg_ptr argv, int max)
return i; return i;
} }
static int count_strings_kernel(const char *const *argv)
{
int i;
if (!argv)
return 0;
for (i = 0; argv[i]; ++i) {
if (i >= MAX_ARG_STRINGS)
return -E2BIG;
if (fatal_signal_pending(current))
return -ERESTARTNOHAND;
cond_resched();
}
return i;
}
static int bprm_stack_limits(struct linux_binprm *bprm) static int bprm_stack_limits(struct linux_binprm *bprm)
{ {
unsigned long limit, ptr_size; unsigned long limit, ptr_size;
...@@ -624,6 +641,20 @@ int copy_string_kernel(const char *arg, struct linux_binprm *bprm) ...@@ -624,6 +641,20 @@ int copy_string_kernel(const char *arg, struct linux_binprm *bprm)
} }
EXPORT_SYMBOL(copy_string_kernel); EXPORT_SYMBOL(copy_string_kernel);
static int copy_strings_kernel(int argc, const char *const *argv,
struct linux_binprm *bprm)
{
while (argc-- > 0) {
int ret = copy_string_kernel(argv[argc], bprm);
if (ret < 0)
return ret;
if (fatal_signal_pending(current))
return -ERESTARTNOHAND;
cond_resched();
}
return 0;
}
#ifdef CONFIG_MMU #ifdef CONFIG_MMU
/* /*
...@@ -1991,7 +2022,60 @@ static int do_execveat_common(int fd, struct filename *filename, ...@@ -1991,7 +2022,60 @@ static int do_execveat_common(int fd, struct filename *filename,
return retval; return retval;
} }
int do_execve(struct filename *filename, int kernel_execve(const char *kernel_filename,
const char *const *argv, const char *const *envp)
{
struct filename *filename;
struct linux_binprm *bprm;
int fd = AT_FDCWD;
int retval;
filename = getname_kernel(kernel_filename);
if (IS_ERR(filename))
return PTR_ERR(filename);
bprm = alloc_bprm(fd, filename);
if (IS_ERR(bprm)) {
retval = PTR_ERR(bprm);
goto out_ret;
}
retval = count_strings_kernel(argv);
if (retval < 0)
goto out_free;
bprm->argc = retval;
retval = count_strings_kernel(envp);
if (retval < 0)
goto out_free;
bprm->envc = retval;
retval = bprm_stack_limits(bprm);
if (retval < 0)
goto out_free;
retval = copy_string_kernel(bprm->filename, bprm);
if (retval < 0)
goto out_free;
bprm->exec = bprm->p;
retval = copy_strings_kernel(bprm->envc, envp, bprm);
if (retval < 0)
goto out_free;
retval = copy_strings_kernel(bprm->argc, argv, bprm);
if (retval < 0)
goto out_free;
retval = bprm_execve(bprm, fd, filename, 0);
out_free:
free_bprm(bprm);
out_ret:
putname(filename);
return retval;
}
static int do_execve(struct filename *filename,
const char __user *const __user *__argv, const char __user *const __user *__argv,
const char __user *const __user *__envp) const char __user *const __user *__envp)
{ {
...@@ -2000,7 +2084,7 @@ int do_execve(struct filename *filename, ...@@ -2000,7 +2084,7 @@ int do_execve(struct filename *filename,
return do_execveat_common(AT_FDCWD, filename, argv, envp, 0); return do_execveat_common(AT_FDCWD, filename, argv, envp, 0);
} }
int do_execveat(int fd, struct filename *filename, static int do_execveat(int fd, struct filename *filename,
const char __user *const __user *__argv, const char __user *const __user *__argv,
const char __user *const __user *__envp, const char __user *const __user *__envp,
int flags) int flags)
......
...@@ -135,12 +135,7 @@ int copy_string_kernel(const char *arg, struct linux_binprm *bprm); ...@@ -135,12 +135,7 @@ int copy_string_kernel(const char *arg, struct linux_binprm *bprm);
extern void set_binfmt(struct linux_binfmt *new); extern void set_binfmt(struct linux_binfmt *new);
extern ssize_t read_code(struct file *, unsigned long, loff_t, size_t); extern ssize_t read_code(struct file *, unsigned long, loff_t, size_t);
extern int do_execve(struct filename *, int kernel_execve(const char *filename,
const char __user * const __user *, const char *const *argv, const char *const *envp);
const char __user * const __user *);
extern int do_execveat(int, struct filename *,
const char __user * const __user *,
const char __user * const __user *,
int);
#endif /* _LINUX_BINFMTS_H */ #endif /* _LINUX_BINFMTS_H */
...@@ -1329,9 +1329,7 @@ static int run_init_process(const char *init_filename) ...@@ -1329,9 +1329,7 @@ static int run_init_process(const char *init_filename)
pr_debug(" with environment:\n"); pr_debug(" with environment:\n");
for (p = envp_init; *p; p++) for (p = envp_init; *p; p++)
pr_debug(" %s\n", *p); pr_debug(" %s\n", *p);
return do_execve(getname_kernel(init_filename), return kernel_execve(init_filename, argv_init, envp_init);
(const char __user *const __user *)argv_init,
(const char __user *const __user *)envp_init);
} }
static int try_to_run_init_process(const char *init_filename) static int try_to_run_init_process(const char *init_filename)
......
...@@ -98,9 +98,9 @@ static int call_usermodehelper_exec_async(void *data) ...@@ -98,9 +98,9 @@ static int call_usermodehelper_exec_async(void *data)
commit_creds(new); commit_creds(new);
retval = do_execve(getname_kernel(sub_info->path), retval = kernel_execve(sub_info->path,
(const char __user *const __user *)sub_info->argv, (const char *const *)sub_info->argv,
(const char __user *const __user *)sub_info->envp); (const char *const *)sub_info->envp);
out: out:
sub_info->retval = retval; sub_info->retval = retval;
/* /*
......
...@@ -425,7 +425,7 @@ struct tomoyo_request_info { ...@@ -425,7 +425,7 @@ struct tomoyo_request_info {
struct tomoyo_obj_info *obj; struct tomoyo_obj_info *obj;
/* /*
* For holding parameters specific to execve() request. * For holding parameters specific to execve() request.
* NULL if not dealing do_execve(). * NULL if not dealing execve().
*/ */
struct tomoyo_execve *ee; struct tomoyo_execve *ee;
struct tomoyo_domain_info *domain; struct tomoyo_domain_info *domain;
......
...@@ -767,7 +767,7 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm) ...@@ -767,7 +767,7 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm)
/* /*
* Check for domain transition preference if "file execute" matched. * Check for domain transition preference if "file execute" matched.
* If preference is given, make do_execve() fail if domain transition * If preference is given, make execve() fail if domain transition
* has failed, for domain transition preference should be used with * has failed, for domain transition preference should be used with
* destination domain defined. * destination domain defined.
*/ */
...@@ -810,7 +810,7 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm) ...@@ -810,7 +810,7 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm)
snprintf(ee->tmp, TOMOYO_EXEC_TMPSIZE - 1, "<%s>", snprintf(ee->tmp, TOMOYO_EXEC_TMPSIZE - 1, "<%s>",
candidate->name); candidate->name);
/* /*
* Make do_execve() fail if domain transition across namespaces * Make execve() fail if domain transition across namespaces
* has failed. * has failed.
*/ */
reject_on_transition_failure = true; reject_on_transition_failure = true;
......
...@@ -93,7 +93,7 @@ static int tomoyo_bprm_check_security(struct linux_binprm *bprm) ...@@ -93,7 +93,7 @@ static int tomoyo_bprm_check_security(struct linux_binprm *bprm)
struct tomoyo_task *s = tomoyo_task(current); struct tomoyo_task *s = tomoyo_task(current);
/* /*
* Execute permission is checked against pathname passed to do_execve() * Execute permission is checked against pathname passed to execve()
* using current domain. * using current domain.
*/ */
if (!s->old_domain_info) { if (!s->old_domain_info) {
...@@ -307,7 +307,7 @@ static int tomoyo_file_fcntl(struct file *file, unsigned int cmd, ...@@ -307,7 +307,7 @@ static int tomoyo_file_fcntl(struct file *file, unsigned int cmd,
*/ */
static int tomoyo_file_open(struct file *f) static int tomoyo_file_open(struct file *f)
{ {
/* Don't check read permission here if called from do_execve(). */ /* Don't check read permission here if called from execve(). */
if (current->in_execve) if (current->in_execve)
return 0; return 0;
return tomoyo_check_open_permission(tomoyo_domain(), &f->f_path, return tomoyo_check_open_permission(tomoyo_domain(), &f->f_path,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment