On Mon, Aug 29, 2016 at 08:06:39PM +0800, Zhao Lei wrote:
> Current call_usermodehelper_exec() can not set pid namespace for
> the executed program, because we need addition fork to make pid
> namespace active.
> 
> This patch add above function for call_usermodehelper_exec().
> When init_intermediate callback return -EAGAIN, the usermodehelper
> will fork again to make pid namespace active, and run program
> in the child process.

I am not sure that we need to make this extra fork(). When I commented
the previous patches, I said that we need to make fork() after
setns(CLONE_NEWPID). But we alread have one fork(), why we can't set a
required pidns before kernel_thread(call_usermodehelper_exec_async) and
then switch back into the origin pidns.

> 
> This function is helpful for coredump to run pipe_program in
> specific container environment.
> 
> Signed-off-by: Zhao Lei <zhao...@cn.fujitsu.com>
> ---
>  fs/coredump.c               |   3 +-
>  include/linux/kmod.h        |   2 +
>  init/do_mounts_initrd.c     |   3 +-
>  kernel/kmod.c               | 133 
> ++++++++++++++++++++++++++++++++++++++------
>  lib/kobject_uevent.c        |   3 +-
>  security/keys/request_key.c |   4 +-
>  6 files changed, 127 insertions(+), 21 deletions(-)
> 
> diff --git a/fs/coredump.c b/fs/coredump.c
> index 281b768..ceb0ee8 100644
> --- a/fs/coredump.c
> +++ b/fs/coredump.c
> @@ -641,7 +641,8 @@ void do_coredump(const siginfo_t *siginfo)
>               retval = -ENOMEM;
>               sub_info = call_usermodehelper_setup(helper_argv[0],
>                                               helper_argv, NULL, GFP_KERNEL,
> -                                             umh_pipe_setup, NULL, &cprm);
> +                                             NULL, umh_pipe_setup,
> +                                             NULL, &cprm);
>               if (sub_info)
>                       retval = call_usermodehelper_exec(sub_info,
>                                                         UMH_WAIT_EXEC);
> diff --git a/include/linux/kmod.h b/include/linux/kmod.h
> index fcfd2bf..8fb8c0e 100644
> --- a/include/linux/kmod.h
> +++ b/include/linux/kmod.h
> @@ -61,6 +61,7 @@ struct subprocess_info {
>       char **envp;
>       int wait;
>       int retval;
> +     int (*init_intermediate)(struct subprocess_info *info);
>       int (*init)(struct subprocess_info *info, struct cred *new);
>       void (*cleanup)(struct subprocess_info *info);
>       void *data;
> @@ -71,6 +72,7 @@ call_usermodehelper(char *path, char **argv, char **envp, 
> int wait);
>  
>  extern struct subprocess_info *
>  call_usermodehelper_setup(char *path, char **argv, char **envp, gfp_t 
> gfp_mask,
> +                       int (*init_intermediate)(struct subprocess_info 
> *info),
>                         int (*init)(struct subprocess_info *info, struct cred 
> *new),
>                         void (*cleanup)(struct subprocess_info *), void 
> *data);
>  
> diff --git a/init/do_mounts_initrd.c b/init/do_mounts_initrd.c
> index a1000ca..bb5dce5 100644
> --- a/init/do_mounts_initrd.c
> +++ b/init/do_mounts_initrd.c
> @@ -72,7 +72,8 @@ static void __init handle_initrd(void)
>       current->flags |= PF_FREEZER_SKIP;
>  
>       info = call_usermodehelper_setup("/linuxrc", argv, envp_init,
> -                                      GFP_KERNEL, init_linuxrc, NULL, NULL);
> +                                      GFP_KERNEL, NULL, init_linuxrc, NULL,
> +                                      NULL);
>       if (!info)
>               return;
>       call_usermodehelper_exec(info, UMH_WAIT_PROC);
> diff --git a/kernel/kmod.c b/kernel/kmod.c
> index 0277d12..30a5802 100644
> --- a/kernel/kmod.c
> +++ b/kernel/kmod.c
> @@ -91,7 +91,7 @@ static int call_modprobe(char *module_name, int wait)
>       argv[4] = NULL;
>  
>       info = call_usermodehelper_setup(modprobe_path, argv, envp, GFP_KERNEL,
> -                                      NULL, free_modprobe_argv, NULL);
> +                                      NULL, NULL, free_modprobe_argv, NULL);
>       if (!info)
>               goto free_module_name;
>  
> @@ -209,14 +209,11 @@ static void umh_complete(struct subprocess_info 
> *sub_info)
>               call_usermodehelper_freeinfo(sub_info);
>  }
>  
> -/*
> - * This is the task which runs the usermode application
> - */
> -static int call_usermodehelper_exec_async(void *data)
> +static int __call_usermodehelper_exec_doexec(void *data)
>  {
>       struct subprocess_info *sub_info = data;
>       struct cred *new;
> -     int retval;
> +     int retval = 0;
>  
>       spin_lock_irq(&current->sighand->siglock);
>       flush_signal_handlers(current, 1);
> @@ -228,10 +225,11 @@ static int call_usermodehelper_exec_async(void *data)
>        */
>       set_user_nice(current, 0);
>  
> -     retval = -ENOMEM;
>       new = prepare_kernel_cred(current);
> -     if (!new)
> +     if (!new) {
> +             retval = -ENOMEM;
>               goto out;
> +     }
>  
>       spin_lock(&umh_sysctl_lock);
>       new->cap_bset = cap_intersect(usermodehelper_bset, new->cap_bset);
> @@ -248,20 +246,121 @@ static int call_usermodehelper_exec_async(void *data)
>       }
>  
>       commit_creds(new);
> -
>       retval = do_execve(getname_kernel(sub_info->path),
> -                        (const char __user *const __user *)sub_info->argv,
> -                        (const char __user *const __user *)sub_info->envp);
> +                (const char __user *const __user *)sub_info->argv,
> +                (const char __user *const __user *)sub_info->envp);
> +
>  out:
> -     sub_info->retval = retval;
> +     return retval;
> +}
> +
> +static int call_usermodehelper_exec_doexec(void *data)
> +{
> +     struct subprocess_info *sub_info = data;
> +     int ret = __call_usermodehelper_exec_doexec(data);
> +
>       /*
> -      * call_usermodehelper_exec_sync() will call umh_complete
> -      * if UHM_WAIT_PROC.
> +      * If it is called in non-sync mode:
> +      * On fail:
> +      *   should set sub_info->retval, call umh_complete and exit process.
> +      * On success:
> +      *   should set sub_info->retval, call umh_complete and return to
> +      *   user-mode program.
> +      * It it is called in sync mode:
> +      * On fail:
> +      *   should set sub_info->retval and exit process, the parent process
> +      *   will wait and call umh_complete()
> +      * On success:
> +      *   should return to user-mode program, the parent process will wait
> +      *   and get program's ret from wait(), and call umh_complete().
>        */
> +     sub_info->retval = ret;
> +
>       if (!(sub_info->wait & UMH_WAIT_PROC))
>               umh_complete(sub_info);
> -     if (!retval)
> +
> +     if (!ret)
>               return 0;
> +
> +     /*
> +      * see comment in call_usermodehelper_exec_sync() before change
> +      * it to do_exit(ret).
> +      */
> +     do_exit(0);
> +}
> +
> +/*
> + * This is the task which runs the usermode application
> + */
> +static int call_usermodehelper_exec_async(void *data)
> +{
> +     struct subprocess_info *sub_info = data;
> +     int ret = 0;
> +     int refork = 0;
> +
> +     if (sub_info->init_intermediate) {
> +             ret = sub_info->init_intermediate(sub_info);
> +             switch (ret) {
> +             case 0:
> +                     break;
> +             case -EAGAIN:
> +                     /*
> +                      * if pid namespace is changed,
> +                      * we need refork to make it active.
> +                      */
> +                     ret = 0;
> +                     refork = 1;
> +                     break;
> +             default:
> +                     goto out;
> +             }
> +     }
> +
> +     if (refork) {
> +             /*
> +              * Code copyed from call_usermodehelper_exec_work() and
> +              * call_usermodehelper_exec_sync(), see comments in these
> +              * functions for detail.
> +              */
> +             pid_t pid;
> +
> +             if (sub_info->wait & UMH_WAIT_PROC) {
> +                     kernel_sigaction(SIGCHLD, SIG_DFL);
> +                     pid = kernel_thread(call_usermodehelper_exec_doexec,
> +                                         sub_info, SIGCHLD);
> +                     if (pid < 0) {
> +                             ret = pid;
> +                     } else {
> +                             ret = -ECHILD;
> +                             sys_wait4(pid, (int __user *)&ret, 0, NULL);
> +                     }
> +                     kernel_sigaction(SIGCHLD, SIG_IGN);
> +
> +                     sub_info->retval = ret;
> +             } else {
> +                     pid = kernel_thread(call_usermodehelper_exec_doexec,
> +                                         sub_info, SIGCHLD);
> +                     if (pid < 0) {
> +                             sub_info->retval = pid;
> +                             umh_complete(sub_info);
> +                     }
> +             }
> +     } else {
> +             ret = __call_usermodehelper_exec_doexec(data);
> +
> +out:
> +             /*
> +              * see comment in call_usermodehelper_exec_doexec() for detail
> +              */
> +             sub_info->retval = ret;
> +
> +             if (!(sub_info->wait & UMH_WAIT_PROC))
> +                     umh_complete(sub_info);
> +
> +             if (!ret)
> +                     return 0;
> +     }
> +
>       do_exit(0);
>  }
>  
> @@ -518,6 +617,7 @@ static void helper_unlock(void)
>   */
>  struct subprocess_info *call_usermodehelper_setup(char *path, char **argv,
>               char **envp, gfp_t gfp_mask,
> +             int (*init_intermediate)(struct subprocess_info *info),
>               int (*init)(struct subprocess_info *info, struct cred *new),
>               void (*cleanup)(struct subprocess_info *info),
>               void *data)
> @@ -533,6 +633,7 @@ struct subprocess_info *call_usermodehelper_setup(char 
> *path, char **argv,
>       sub_info->envp = envp;
>  
>       sub_info->cleanup = cleanup;
> +     sub_info->init_intermediate = init_intermediate;
>       sub_info->init = init;
>       sub_info->data = data;
>    out:
> @@ -619,7 +720,7 @@ int call_usermodehelper(char *path, char **argv, char 
> **envp, int wait)
>       gfp_t gfp_mask = (wait == UMH_NO_WAIT) ? GFP_ATOMIC : GFP_KERNEL;
>  
>       info = call_usermodehelper_setup(path, argv, envp, gfp_mask,
> -                                      NULL, NULL, NULL);
> +                                      NULL, NULL, NULL, NULL);
>       if (info == NULL)
>               return -ENOMEM;
>  
> diff --git a/lib/kobject_uevent.c b/lib/kobject_uevent.c
> index f6c2c1e..7e571f0 100644
> --- a/lib/kobject_uevent.c
> +++ b/lib/kobject_uevent.c
> @@ -345,7 +345,8 @@ int kobject_uevent_env(struct kobject *kobj, enum 
> kobject_action action,
>               retval = -ENOMEM;
>               info = call_usermodehelper_setup(env->argv[0], env->argv,
>                                                env->envp, GFP_KERNEL,
> -                                              NULL, cleanup_uevent_env, env);
> +                                              NULL, NULL, cleanup_uevent_env,
> +                                              env);
>               if (info) {
>                       retval = call_usermodehelper_exec(info, UMH_NO_WAIT);
>                       env = NULL;     /* freed by cleanup_uevent_env */
> diff --git a/security/keys/request_key.c b/security/keys/request_key.c
> index a29e355..087c11d 100644
> --- a/security/keys/request_key.c
> +++ b/security/keys/request_key.c
> @@ -78,8 +78,8 @@ static int call_usermodehelper_keys(char *path, char 
> **argv, char **envp,
>       struct subprocess_info *info;
>  
>       info = call_usermodehelper_setup(path, argv, envp, GFP_KERNEL,
> -                                       umh_keys_init, umh_keys_cleanup,
> -                                       session_keyring);
> +                                      NULL, umh_keys_init, umh_keys_cleanup,
> +                                      session_keyring);
>       if (!info)
>               return -ENOMEM;
>  
> -- 
> 1.8.5.1
> 
> 
> 

Reply via email to