From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S933014AbcH2MKg (ORCPT ); Mon, 29 Aug 2016 08:10:36 -0400 Received: from cn.fujitsu.com ([59.151.112.132]:59344 "EHLO heian.cn.fujitsu.com" rhost-flags-OK-FAIL-OK-FAIL) by vger.kernel.org with ESMTP id S932839AbcH2MKc (ORCPT ); Mon, 29 Aug 2016 08:10:32 -0400 X-IronPort-AV: E=Sophos;i="5.22,518,1449504000"; d="scan'208";a="10387929" From: Zhao Lei To: CC: , "Eric W. Biederman" , Mateusz Guzik , Kamezawa Hiroyuki , =?UTF-8?q?St=C3=A9phane=20Graber?= , Andrei Vagin , Zhao Lei Subject: [PATCH v3 1/3] Make call_usermodehelper_exec possible to set pid namespace Date: Mon, 29 Aug 2016 20:06:39 +0800 Message-ID: <94bf69d92a3204491862b8c72111f4e046bf5f9e.1472471586.git.zhaolei@cn.fujitsu.com> X-Mailer: git-send-email 1.8.5.1 In-Reply-To: References: MIME-Version: 1.0 Content-Type: text/plain X-yoursite-MailScanner-ID: 8F4BD4334C87.A9241 X-yoursite-MailScanner: Found to be clean X-yoursite-MailScanner-From: zhaolei@cn.fujitsu.com Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org 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. This function is helpful for coredump to run pipe_program in specific container environment. Signed-off-by: Zhao Lei --- 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(¤t->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