Skip to content

Commit be619f7

Browse files
committed
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/[email protected] Reviewed-by: Kees Cook <[email protected]> Link: https://lkml.kernel.org/r/[email protected] Signed-off-by: "Eric W. Biederman" <[email protected]>
1 parent d8b9cd5 commit be619f7

File tree

10 files changed

+100
-23
lines changed

10 files changed

+100
-23
lines changed

arch/x86/entry/entry_32.S

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -854,7 +854,7 @@ SYM_CODE_START(ret_from_fork)
854854
CALL_NOSPEC ebx
855855
/*
856856
* 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()
858858
* syscall.
859859
*/
860860
movl $0, PT_EAX(%esp)

arch/x86/entry/entry_64.S

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,7 @@ SYM_CODE_START(ret_from_fork)
293293
CALL_NOSPEC rbx
294294
/*
295295
* 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()
297297
* syscall.
298298
*/
299299
movq $0, RAX(%rsp)

arch/x86/kernel/unwind_frame.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,7 @@ bool unwind_next_frame(struct unwind_state *state)
275275
* This user_mode() check is slightly broader than a PF_KTHREAD
276276
* check because it also catches the awkward situation where a
277277
* 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.
279279
*/
280280
if (!user_mode(regs))
281281
goto the_end;

fs/exec.c

Lines changed: 86 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -448,6 +448,23 @@ static int count(struct user_arg_ptr argv, int max)
448448
return i;
449449
}
450450

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+
451468
static int bprm_stack_limits(struct linux_binprm *bprm)
452469
{
453470
unsigned long limit, ptr_size;
@@ -624,6 +641,20 @@ int copy_string_kernel(const char *arg, struct linux_binprm *bprm)
624641
}
625642
EXPORT_SYMBOL(copy_string_kernel);
626643

644+
static int copy_strings_kernel(int argc, const char *const *argv,
645+
struct linux_binprm *bprm)
646+
{
647+
while (argc-- > 0) {
648+
int ret = copy_string_kernel(argv[argc], bprm);
649+
if (ret < 0)
650+
return ret;
651+
if (fatal_signal_pending(current))
652+
return -ERESTARTNOHAND;
653+
cond_resched();
654+
}
655+
return 0;
656+
}
657+
627658
#ifdef CONFIG_MMU
628659

629660
/*
@@ -1991,7 +2022,60 @@ static int do_execveat_common(int fd, struct filename *filename,
19912022
return retval;
19922023
}
19932024

1994-
int do_execve(struct filename *filename,
2025+
int kernel_execve(const char *kernel_filename,
2026+
const char *const *argv, const char *const *envp)
2027+
{
2028+
struct filename *filename;
2029+
struct linux_binprm *bprm;
2030+
int fd = AT_FDCWD;
2031+
int retval;
2032+
2033+
filename = getname_kernel(kernel_filename);
2034+
if (IS_ERR(filename))
2035+
return PTR_ERR(filename);
2036+
2037+
bprm = alloc_bprm(fd, filename);
2038+
if (IS_ERR(bprm)) {
2039+
retval = PTR_ERR(bprm);
2040+
goto out_ret;
2041+
}
2042+
2043+
retval = count_strings_kernel(argv);
2044+
if (retval < 0)
2045+
goto out_free;
2046+
bprm->argc = retval;
2047+
2048+
retval = count_strings_kernel(envp);
2049+
if (retval < 0)
2050+
goto out_free;
2051+
bprm->envc = retval;
2052+
2053+
retval = bprm_stack_limits(bprm);
2054+
if (retval < 0)
2055+
goto out_free;
2056+
2057+
retval = copy_string_kernel(bprm->filename, bprm);
2058+
if (retval < 0)
2059+
goto out_free;
2060+
bprm->exec = bprm->p;
2061+
2062+
retval = copy_strings_kernel(bprm->envc, envp, bprm);
2063+
if (retval < 0)
2064+
goto out_free;
2065+
2066+
retval = copy_strings_kernel(bprm->argc, argv, bprm);
2067+
if (retval < 0)
2068+
goto out_free;
2069+
2070+
retval = bprm_execve(bprm, fd, filename, 0);
2071+
out_free:
2072+
free_bprm(bprm);
2073+
out_ret:
2074+
putname(filename);
2075+
return retval;
2076+
}
2077+
2078+
static int do_execve(struct filename *filename,
19952079
const char __user *const __user *__argv,
19962080
const char __user *const __user *__envp)
19972081
{
@@ -2000,7 +2084,7 @@ int do_execve(struct filename *filename,
20002084
return do_execveat_common(AT_FDCWD, filename, argv, envp, 0);
20012085
}
20022086

2003-
int do_execveat(int fd, struct filename *filename,
2087+
static int do_execveat(int fd, struct filename *filename,
20042088
const char __user *const __user *__argv,
20052089
const char __user *const __user *__envp,
20062090
int flags)

include/linux/binfmts.h

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -135,12 +135,7 @@ int copy_string_kernel(const char *arg, struct linux_binprm *bprm);
135135
extern void set_binfmt(struct linux_binfmt *new);
136136
extern ssize_t read_code(struct file *, unsigned long, loff_t, size_t);
137137

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);
145140

146141
#endif /* _LINUX_BINFMTS_H */

init/main.c

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1329,9 +1329,7 @@ static int run_init_process(const char *init_filename)
13291329
pr_debug(" with environment:\n");
13301330
for (p = envp_init; *p; p++)
13311331
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);
13351333
}
13361334

13371335
static int try_to_run_init_process(const char *init_filename)

kernel/umh.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -98,9 +98,9 @@ static int call_usermodehelper_exec_async(void *data)
9898

9999
commit_creds(new);
100100

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);
104104
out:
105105
sub_info->retval = retval;
106106
/*

security/tomoyo/common.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -425,7 +425,7 @@ struct tomoyo_request_info {
425425
struct tomoyo_obj_info *obj;
426426
/*
427427
* For holding parameters specific to execve() request.
428-
* NULL if not dealing do_execve().
428+
* NULL if not dealing execve().
429429
*/
430430
struct tomoyo_execve *ee;
431431
struct tomoyo_domain_info *domain;

security/tomoyo/domain.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -767,7 +767,7 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm)
767767

768768
/*
769769
* 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
771771
* has failed, for domain transition preference should be used with
772772
* destination domain defined.
773773
*/
@@ -810,7 +810,7 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm)
810810
snprintf(ee->tmp, TOMOYO_EXEC_TMPSIZE - 1, "<%s>",
811811
candidate->name);
812812
/*
813-
* Make do_execve() fail if domain transition across namespaces
813+
* Make execve() fail if domain transition across namespaces
814814
* has failed.
815815
*/
816816
reject_on_transition_failure = true;

security/tomoyo/tomoyo.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ static int tomoyo_bprm_check_security(struct linux_binprm *bprm)
9393
struct tomoyo_task *s = tomoyo_task(current);
9494

9595
/*
96-
* Execute permission is checked against pathname passed to do_execve()
96+
* Execute permission is checked against pathname passed to execve()
9797
* using current domain.
9898
*/
9999
if (!s->old_domain_info) {
@@ -307,7 +307,7 @@ static int tomoyo_file_fcntl(struct file *file, unsigned int cmd,
307307
*/
308308
static int tomoyo_file_open(struct file *f)
309309
{
310-
/* Don't check read permission here if called from do_execve(). */
310+
/* Don't check read permission here if called from execve(). */
311311
if (current->in_execve)
312312
return 0;
313313
return tomoyo_check_open_permission(tomoyo_domain(), &f->f_path,

0 commit comments

Comments
 (0)