[PATCH v3 1/3] Make call_usermodehelper_exec possible to set pid namespace

From: Zhao Lei
Date: Mon Aug 29 2016 - 08:10:42 EST


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 <zhaolei@xxxxxxxxxxxxxx>
---
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