lkml.org 
[lkml]   [2020]   [Jun]   [18]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 5/6] exec: add a kernel_execveat helper
    Date
    Add a kernel_execveat helper to execute a binary with kernel space argv
    and envp pointers. Switch executing init and user mode helpers to this
    new helper instead of relying on the implicit set_fs(KERNEL_DS) for early
    init code and kernel threads, and move the getname call into the
    do_execve helper.

    Signed-off-by: Christoph Hellwig <hch@lst.de>
    ---
    fs/exec.c | 109 ++++++++++++++++++++++++++++++++--------
    include/linux/binfmts.h | 6 +--
    init/main.c | 6 +--
    kernel/umh.c | 8 ++-
    4 files changed, 95 insertions(+), 34 deletions(-)

    diff --git a/fs/exec.c b/fs/exec.c
    index 34781db6bf6889..7923b8334ae600 100644
    --- a/fs/exec.c
    +++ b/fs/exec.c
    @@ -435,6 +435,21 @@ static int count_strings(const char __user *const __user *argv)
    return i;
    }

    +static int count_kernel_strings(const char *const *argv)
    +{
    + int i;
    +
    + if (!argv)
    + return 0;
    +
    + for (i = 0; argv[i]; i++) {
    + if (i >= MAX_ARG_STRINGS)
    + return -E2BIG;
    + }
    +
    + return i;
    +}
    +
    static int check_arg_limit(struct linux_binprm *bprm)
    {
    unsigned long limit, ptr_size;
    @@ -611,6 +626,19 @@ int copy_string_kernel(const char *arg, struct linux_binprm *bprm)
    }
    EXPORT_SYMBOL(copy_string_kernel);

    +static int copy_strings_kernel(int argc, const char *const *argv,
    + struct linux_binprm *bprm)
    +{
    + int ret;
    +
    + while (argc-- > 0) {
    + ret = copy_string_kernel(argv[argc], bprm);
    + if (ret)
    + break;
    + }
    + return ret;
    +}
    +
    #ifdef CONFIG_MMU

    /*
    @@ -1793,9 +1821,11 @@ static int exec_binprm(struct linux_binprm *bprm)
    return 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 *envp,
    + const char *const *kernel_argv,
    + const char *const *kernel_envp,
    int flags, struct file *file)
    {
    char *pathbuf = NULL;
    @@ -1876,16 +1906,30 @@ int do_execveat(int fd, struct filename *filename,
    if (retval)
    goto out_unmark;

    - bprm->argc = count_strings(argv);
    - if (bprm->argc < 0) {
    - retval = bprm->argc;
    - goto out;
    - }
    + if (unlikely(kernel_argv)) {
    + bprm->argc = count_kernel_strings(kernel_argv);
    + if (bprm->argc < 0) {
    + retval = bprm->argc;
    + goto out;
    + }

    - bprm->envc = count_strings(envp);
    - if (bprm->envc < 0) {
    - retval = bprm->envc;
    - goto out;
    + bprm->envc = count_kernel_strings(kernel_envp);
    + if (bprm->envc < 0) {
    + retval = bprm->envc;
    + goto out;
    + }
    + } else {
    + bprm->argc = count_strings(argv);
    + if (bprm->argc < 0) {
    + retval = bprm->argc;
    + goto out;
    + }
    +
    + bprm->envc = count_strings(envp);
    + if (bprm->envc < 0) {
    + retval = bprm->envc;
    + goto out;
    + }
    }

    retval = check_arg_limit(bprm);
    @@ -1902,13 +1946,22 @@ int do_execveat(int fd, struct filename *filename,
    goto out;

    bprm->exec = bprm->p;
    - retval = copy_strings(bprm->envc, envp, bprm);
    - if (retval < 0)
    - goto out;

    - retval = copy_strings(bprm->argc, argv, bprm);
    - if (retval < 0)
    - goto out;
    + if (unlikely(kernel_argv)) {
    + retval = copy_strings_kernel(bprm->envc, kernel_envp, bprm);
    + if (retval < 0)
    + goto out;
    + retval = copy_strings_kernel(bprm->argc, kernel_argv, bprm);
    + if (retval < 0)
    + goto out;
    + } else {
    + retval = copy_strings(bprm->envc, envp, bprm);
    + if (retval < 0)
    + goto out;
    + retval = copy_strings(bprm->argc, argv, bprm);
    + if (retval < 0)
    + goto out;
    + }

    retval = exec_binprm(bprm);
    if (retval < 0)
    @@ -1959,6 +2012,23 @@ int do_execveat(int fd, struct filename *filename,
    return retval;
    }

    +static int do_execveat(int fd, const char *filename,
    + const char __user *const __user *argv,
    + const char __user *const __user *envp, int flags)
    +{
    + int lookup_flags = (flags & AT_EMPTY_PATH) ? LOOKUP_EMPTY : 0;
    + struct filename *name = getname_flags(filename, lookup_flags, NULL);
    +
    + return __do_execveat(fd, name, argv, envp, NULL, NULL, flags, NULL);
    +}
    +
    +int kernel_execveat(int fd, const char *filename, const char *const *argv,
    + const char *const *envp, int flags, struct file *file)
    +{
    + return __do_execveat(fd, getname_kernel(filename), NULL, NULL, argv,
    + envp, flags, file);
    +}
    +
    void set_binfmt(struct linux_binfmt *new)
    {
    struct mm_struct *mm = current->mm;
    @@ -1988,7 +2058,7 @@ SYSCALL_DEFINE3(execve,
    const char __user *const __user *, argv,
    const char __user *const __user *, envp)
    {
    - return do_execveat(AT_FDCWD, getname(filename), argv, envp, 0, NULL);
    + return do_execveat(AT_FDCWD, filename, argv, envp, 0);
    }

    SYSCALL_DEFINE5(execveat,
    @@ -1997,8 +2067,5 @@ SYSCALL_DEFINE5(execveat,
    const char __user *const __user *, envp,
    int, flags)
    {
    - int lookup_flags = (flags & AT_EMPTY_PATH) ? LOOKUP_EMPTY : 0;
    - struct filename *name = getname_flags(filename, lookup_flags, NULL);
    -
    - return do_execveat(fd, name, argv, envp, flags, NULL);
    + return do_execveat(fd, filename, argv, envp, flags);
    }
    diff --git a/include/linux/binfmts.h b/include/linux/binfmts.h
    index bed702e4b1fbd9..1e61c980c16354 100644
    --- a/include/linux/binfmts.h
    +++ b/include/linux/binfmts.h
    @@ -134,9 +134,7 @@ int copy_string_kernel(const char *arg, struct linux_binprm *bprm);
    extern void set_binfmt(struct linux_binfmt *new);
    extern ssize_t read_code(struct file *, unsigned long, loff_t, size_t);

    -int do_execveat(int fd, struct filename *filename,
    - const char __user *const __user *__argv,
    - const char __user *const __user *__envp,
    - int flags, struct file *file);
    +int kernel_execveat(int fd, const char *filename, const char *const *argv,
    + const char *const *envp, int flags, struct file *file);

    #endif /* _LINUX_BINFMTS_H */
    diff --git a/init/main.c b/init/main.c
    index 838950ea7bca22..33de235dc2aa00 100644
    --- a/init/main.c
    +++ b/init/main.c
    @@ -1329,10 +1329,8 @@ static int run_init_process(const char *init_filename)
    pr_debug(" with environment:\n");
    for (p = envp_init; *p; p++)
    pr_debug(" %s\n", *p);
    - return do_execveat(AT_FDCWD, getname_kernel(init_filename),
    - (const char __user *const __user *)argv_init,
    - (const char __user *const __user *)envp_init,
    - 0, NULL);
    + return kernel_execveat(AT_FDCWD, init_filename, argv_init, envp_init, 0,
    + NULL);
    }

    static int try_to_run_init_process(const char *init_filename)
    diff --git a/kernel/umh.c b/kernel/umh.c
    index 7aa9a5817582ca..1284823dbad338 100644
    --- a/kernel/umh.c
    +++ b/kernel/umh.c
    @@ -103,11 +103,9 @@ static int call_usermodehelper_exec_async(void *data)
    commit_creds(new);

    sub_info->pid = task_pid_nr(current);
    - retval = do_execveat(AT_FDCWD,
    - sub_info->path ? getname_kernel(sub_info->path) : NULL,
    - (const char __user *const __user *)sub_info->argv,
    - (const char __user *const __user *)sub_info->envp,
    - 0, sub_info->file);
    + retval = kernel_execveat(AT_FDCWD, sub_info->path,
    + (const char *const *)sub_info->argv,
    + (const char *const *)sub_info->envp, 0, sub_info->file);
    if (sub_info->file && !retval)
    current->flags |= PF_UMH;
    out:
    --
    2.26.2
    \
     
     \ /
      Last update: 2020-06-18 16:48    [W:3.175 / U:0.024 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site