* [PATCH 0/2] exec: refactor how call_usermodehelper works, and update the sense of the core_pipe recursion check (v4 rediff)
@ 2010-03-15 12:29 Neil Horman
2010-03-15 12:33 ` [PATCH 1/2] kmod: add init function to usermodehelper Neil Horman
` (2 more replies)
0 siblings, 3 replies; 18+ messages in thread
From: Neil Horman @ 2010-03-15 12:29 UTC (permalink / raw)
To: akpm; +Cc: linux-kernel, oleg, andi, nhorman
Hey, so I've rediffed and tested the user mode helper bits from my previous
patch set that got wrecked when the rcustring bits got pulled. I've got he
origional summary below, and the patches follwoing. Note, their just my
changes, rediffed to work without Andis patch set. The other umh call sites can
still use plain old call_usermodehelper as they did previously. I'll submit
patches as needed for other callers to make use of the init/cleanup functions as
I have the time/ability to test those changes properly. The changes below I've
been able to test and verify myself.
Hey all-
So, about 6 months ago, I made a set of changes to how the
core-dump-to-a-pipe feature in the kernel works. We had reports of several
races, including some reports of apps bypassing our recursion check so that a
process that was forked as part of a core_pattern setup could infinitely crash
and refork until the system crashed.
We fixes those by improving our recursion checks. The new check
basically refuses to fork a process if its core limit is zero, which works well.
Unfortunately, I've been getting grief from maintainer of user space
programs that are inserted as the forked process of core_pattern. They contend
that in order for their programs (such as abrt and apport) to work, all the
running processes in a system must have their core limits set to a non-zero
value, to which I say 'yes'. I did this by design, and think thats the right
way to do things.
But I've been asked to ease this burden on user space enough times that
I thought I would take a look at it. The first suggestion was to make the
recursion check fail on a non-zero 'special' number, like one. That way the
core collector process could set its core size ulimit to 1, and enable the
kernel's recursion detection. This isn't a bad idea on the surface, but I don't
like it since its opt-in, in that if a program like abrt or apport has a bug and
fails to set such a core limit, we're left with a recursively crashing system
again.
So I've come up with this. What I've done is modify the
call_usermodehelper api such that an extra parameter is added, a function
pointer which will be called by the user helper task, after it forks, but before
it exec's the required process. This will give the caller the opportunity to
get a call back in the processes context, allowing it to do whatever it needs to
to the process in the kernel prior to exec-ing the user space code. In the case
of do_coredump, this callback is ues to set the core ulimit of the helper
process to 1. This elimnates the opt-in problem that I had above, as it allows
the ulimit for core sizes to be set to the value of 1, which is what the
recursion check looks for in do_coredump.
This patch has been tested successfully by me.
Signed-off-by: Neil Horman <nhorman@tuxdriver.com>
CC: Oleg Nesterov <oleg@redhat.com>
CC: Andi Kleen <andi@firstfloor.org>
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH 1/2] kmod: add init function to usermodehelper
2010-03-15 12:29 [PATCH 0/2] exec: refactor how call_usermodehelper works, and update the sense of the core_pipe recursion check (v4 rediff) Neil Horman
@ 2010-03-15 12:33 ` Neil Horman
2010-03-15 17:34 ` Oleg Nesterov
2010-03-15 12:36 ` [PATCH 2/2] exec: replace call_usermodehelper_pipe with use of umh init function and resolve limit Neil Horman
2010-03-15 19:46 ` [PATCH 0/6] umh: keys, signals, misc Oleg Nesterov
2 siblings, 1 reply; 18+ messages in thread
From: Neil Horman @ 2010-03-15 12:33 UTC (permalink / raw)
To: akpm; +Cc: linux-kernel, oleg, andi
Rediff of the usermodehelper changes after all the wreckage. This patch
encompasses the patches that were formally in -mm as:
kmod-add-init-function-to-usermodehelper.patch
kmod-add-init-function-to-usermodehelper-fix.patch
I also rolled in changes from Oleg, moving the call of the init method down in
____call_usermodehelper, so the init function could override the nice level on
the process.
Create new function call_usermodehelper_fns() and allow it
to assign both an init and cleanup function, as we'll as arbitrary data.
The init function is called from the context of the forked process and
allows for customization of the helper process prior to calling exec. Its
return code gates the continuation of the process, or causes its exit.
Also add an arbitrary data pointer to the subprocess_info struct allowing
for data to be passed from the caller to the new process, and the
subsequent cleanup process
Also, use this patch to cleanup the cleanup function. It currently takes
an argp and envp pointer for freeing, which is ugly. Lets instead just
make the subprocess_info structure public, and pass that to the cleanup
and init routines
Signed-off-by: Neil Horman <nhorman@tuxdriver.com>
CC: Oleg Nesterov <oleg@redhat.com>
CC: Andi Kleen <andi@firstfloor.org>
include/linux/kmod.h | 51 +++++++++++++++++++++++++++++++++++++++++----------
kernel/kmod.c | 51 +++++++++++++++++++++++++++++----------------------
kernel/sys.c | 6 +++---
3 files changed, 73 insertions(+), 35 deletions(-)
diff --git a/include/linux/kmod.h b/include/linux/kmod.h
index facb27f..f9edf63 100644
--- a/include/linux/kmod.h
+++ b/include/linux/kmod.h
@@ -23,6 +23,7 @@
#include <linux/stddef.h>
#include <linux/errno.h>
#include <linux/compiler.h>
+#include <linux/workqueue.h>
#define KMOD_PATH_LEN 256
@@ -45,7 +46,27 @@ static inline int request_module_nowait(const char *name, ...) { return -ENOSYS;
struct key;
struct file;
-struct subprocess_info;
+
+enum umh_wait {
+ UMH_NO_WAIT = -1, /* don't wait at all */
+ UMH_WAIT_EXEC = 0, /* wait for the exec, but not the process */
+ UMH_WAIT_PROC = 1, /* wait for the process to complete */
+};
+
+struct subprocess_info {
+ struct work_struct work;
+ struct completion *complete;
+ struct cred *cred;
+ char *path;
+ char **argv;
+ char **envp;
+ enum umh_wait wait;
+ int retval;
+ struct file *stdin;
+ int (*init)(struct subprocess_info *info);
+ void (*cleanup)(struct subprocess_info *info);
+ void *data;
+};
/* Allocate a subprocess_info structure */
struct subprocess_info *call_usermodehelper_setup(char *path, char **argv,
@@ -56,14 +77,10 @@ void call_usermodehelper_setkeys(struct subprocess_info *info,
struct key *session_keyring);
int call_usermodehelper_stdinpipe(struct subprocess_info *sub_info,
struct file **filp);
-void call_usermodehelper_setcleanup(struct subprocess_info *info,
- void (*cleanup)(char **argv, char **envp));
-
-enum umh_wait {
- UMH_NO_WAIT = -1, /* don't wait at all */
- UMH_WAIT_EXEC = 0, /* wait for the exec, but not the process */
- UMH_WAIT_PROC = 1, /* wait for the process to complete */
-};
+void call_usermodehelper_setfns(struct subprocess_info *info,
+ int (*init)(struct subprocess_info *info),
+ void (*cleanup)(struct subprocess_info *info),
+ void *data);
/* Actually execute the sub-process */
int call_usermodehelper_exec(struct subprocess_info *info, enum umh_wait wait);
@@ -73,18 +90,32 @@ int call_usermodehelper_exec(struct subprocess_info *info, enum umh_wait wait);
void call_usermodehelper_freeinfo(struct subprocess_info *info);
static inline int
-call_usermodehelper(char *path, char **argv, char **envp, enum umh_wait wait)
+call_usermodehelper_fns(char *path, char **argv, char **envp,
+ enum umh_wait wait,
+ int (*init)(struct subprocess_info *info),
+ void (*cleanup)(struct subprocess_info *), void *data)
{
struct subprocess_info *info;
gfp_t gfp_mask = (wait == UMH_NO_WAIT) ? GFP_ATOMIC : GFP_KERNEL;
info = call_usermodehelper_setup(path, argv, envp, gfp_mask);
+
if (info == NULL)
return -ENOMEM;
+
+ call_usermodehelper_setfns(info, init, cleanup, data);
+
return call_usermodehelper_exec(info, wait);
}
static inline int
+call_usermodehelper(char *path, char **argv, char **envp, enum umh_wait wait)
+{
+ return call_usermodehelper_fns(path, argv, envp, wait,
+ NULL, NULL, NULL);
+}
+
+static inline int
call_usermodehelper_keys(char *path, char **argv, char **envp,
struct key *session_keyring, enum umh_wait wait)
{
diff --git a/kernel/kmod.c b/kernel/kmod.c
index bf0e231..531ef62 100644
--- a/kernel/kmod.c
+++ b/kernel/kmod.c
@@ -116,27 +116,16 @@ int __request_module(bool wait, const char *fmt, ...)
trace_module_request(module_name, wait, _RET_IP_);
- ret = call_usermodehelper(modprobe_path, argv, envp,
- wait ? UMH_WAIT_PROC : UMH_WAIT_EXEC);
+ ret = call_usermodehelper_fns(modprobe_path, argv, envp,
+ wait ? UMH_WAIT_PROC : UMH_WAIT_EXEC,
+ NULL, NULL, NULL);
+
atomic_dec(&kmod_concurrent);
return ret;
}
EXPORT_SYMBOL(__request_module);
#endif /* CONFIG_MODULES */
-struct subprocess_info {
- struct work_struct work;
- struct completion *complete;
- struct cred *cred;
- char *path;
- char **argv;
- char **envp;
- enum umh_wait wait;
- int retval;
- struct file *stdin;
- void (*cleanup)(char **argv, char **envp);
-};
-
/*
* This is the task which runs the usermode application
*/
@@ -184,9 +173,16 @@ static int ____call_usermodehelper(void *data)
*/
set_user_nice(current, 0);
+ if (sub_info->init) {
+ retval = sub_info->init(sub_info);
+ if (retval)
+ goto fail;
+ }
+
retval = kernel_execve(sub_info->path, sub_info->argv, sub_info->envp);
/* Exec failed? */
+fail:
sub_info->retval = retval;
do_exit(0);
}
@@ -194,7 +190,7 @@ static int ____call_usermodehelper(void *data)
void call_usermodehelper_freeinfo(struct subprocess_info *info)
{
if (info->cleanup)
- (*info->cleanup)(info->argv, info->envp);
+ (*info->cleanup)(info);
if (info->cred)
put_cred(info->cred);
kfree(info);
@@ -406,21 +402,31 @@ void call_usermodehelper_setkeys(struct subprocess_info *info,
EXPORT_SYMBOL(call_usermodehelper_setkeys);
/**
- * call_usermodehelper_setcleanup - set a cleanup function
+ * call_usermodehelper_setfns - set a cleanup/init function
* @info: a subprocess_info returned by call_usermodehelper_setup
* @cleanup: a cleanup function
+ * @init: an init function
+ * @data: arbitrary context sensitive data
+ *
+ * The init function is used to customize the helper process prior to
+ * exec. A non-zero return code causes the process to error out, exit,
+ * and return the failure to the calling process
*
- * The cleanup function is just befor ethe subprocess_info is about to
+ * The cleanup function is just before ethe subprocess_info is about to
* be freed. This can be used for freeing the argv and envp. The
* Function must be runnable in either a process context or the
* context in which call_usermodehelper_exec is called.
*/
-void call_usermodehelper_setcleanup(struct subprocess_info *info,
- void (*cleanup)(char **argv, char **envp))
+void call_usermodehelper_setfns(struct subprocess_info *info,
+ int (*init)(struct subprocess_info *info),
+ void (*cleanup)(struct subprocess_info *info),
+ void *data)
{
info->cleanup = cleanup;
+ info->init = init;
+ info->data = data;
}
-EXPORT_SYMBOL(call_usermodehelper_setcleanup);
+EXPORT_SYMBOL(call_usermodehelper_setfns);
/**
* call_usermodehelper_stdinpipe - set up a pipe to be used for stdin
@@ -515,7 +521,8 @@ int call_usermodehelper_pipe(char *path, char **argv, char **envp,
struct subprocess_info *sub_info;
int ret;
- sub_info = call_usermodehelper_setup(path, argv, envp, GFP_KERNEL);
+ sub_info = call_usermodehelper_setup(path, argv, envp,
+ GFP_KERNEL);
if (sub_info == NULL)
return -ENOMEM;
diff --git a/kernel/sys.c b/kernel/sys.c
index ee25411..66b9edc 100644
--- a/kernel/sys.c
+++ b/kernel/sys.c
@@ -1765,9 +1765,9 @@ SYSCALL_DEFINE3(getcpu, unsigned __user *, cpup, unsigned __user *, nodep,
char poweroff_cmd[POWEROFF_CMD_PATH_LEN] = "/sbin/poweroff";
-static void argv_cleanup(char **argv, char **envp)
+static void argv_cleanup(struct subprocess_info *info)
{
- argv_free(argv);
+ argv_free(info->argv);
}
/**
@@ -1801,7 +1801,7 @@ int orderly_poweroff(bool force)
goto out;
}
- call_usermodehelper_setcleanup(info, argv_cleanup);
+ call_usermodehelper_setfns(info, NULL, argv_cleanup, NULL);
ret = call_usermodehelper_exec(info, UMH_NO_WAIT);
^ permalink raw reply related [flat|nested] 18+ messages in thread
* Re: [PATCH 2/2] exec: replace call_usermodehelper_pipe with use of umh init function and resolve limit
2010-03-15 12:29 [PATCH 0/2] exec: refactor how call_usermodehelper works, and update the sense of the core_pipe recursion check (v4 rediff) Neil Horman
2010-03-15 12:33 ` [PATCH 1/2] kmod: add init function to usermodehelper Neil Horman
@ 2010-03-15 12:36 ` Neil Horman
2010-03-15 17:39 ` Oleg Nesterov
2010-03-15 19:46 ` [PATCH 0/6] umh: keys, signals, misc Oleg Nesterov
2 siblings, 1 reply; 18+ messages in thread
From: Neil Horman @ 2010-03-15 12:36 UTC (permalink / raw)
To: akpm; +Cc: linux-kernel, oleg, andi
Rediff of the usermodehelper changes after all the wreckage. This patch
encompasses the patches that were formally in -mm as:
kmod-replace-call_usermodehelper_pipe-with-use-of-umh-init-function-and-resolve-limit.patch
kmod-replace-call_usermodehelper_pipe-with-use-of-umh-init-function-and-resolve-limitcleanup.patch
kmod: replace call_usermodehelper_pipe() with use of umh init function and resolve limit
The first patch in this series introduced an init function to the
call_usermodehelper api so that processes could be customized by caller.
This patch takes advantage of that fact, by customizing the helper in
do_coredump to create the pipe and set its core limit to one (for our
recusrsion check). This lets us clean up the previous uglyness in the
usermodehelper internals and factor call_usermodehelper out entirely.
While I'm at it, we can also modify the helper setup to look for a core
limit value of 1 rather than zero for our recursion check
Signed-off-by: Neil Horman <nhorman@tuxdriver.com>
CC: Oleg Nesterov <oleg@redhat.com>
CC: Andi Kleen <andi@firstfloor.org>
fs/exec.c | 63 ++++++++++++++++++++++++++++++++++-----
include/linux/kmod.h | 7 ----
kernel/kmod.c | 82 ---------------------------------------------------
3 files changed, 56 insertions(+), 96 deletions(-)
diff --git a/fs/exec.c b/fs/exec.c
index f351cdb..64a50b4 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -1784,6 +1784,50 @@ static void wait_for_dump_helpers(struct file *file)
}
+/*
+ * uhm_pipe_setup
+ * helper function to customize the process used
+ * to collect the core in userspace. Specifically
+ * it sets up a pipe and installs it as fd 0 (stdin)
+ * for the process. Returns 0 on success, or
+ * PTR_ERR on failure.
+ * Note that it also sets the core limit to 1. This
+ * is a special value that we use to trap recursive
+ * core dumps
+ */
+static int umh_pipe_setup(struct subprocess_info *info)
+{
+ struct file *rp, *wp;
+ struct fdtable *fdt;
+ struct coredump_params *cp = (struct coredump_params *)info->data;
+ struct files_struct *cf = current->files;
+
+ wp = create_write_pipe(0);
+ if (IS_ERR(wp))
+ return PTR_ERR(wp);
+
+ rp = create_read_pipe(wp, 0);
+ if (IS_ERR(rp)) {
+ free_write_pipe(wp);
+ return PTR_ERR(rp);
+ }
+
+ cp->file = wp;
+
+ sys_close(0);
+ fd_install(0, rp);
+ spin_lock(&cf->file_lock);
+ fdt = files_fdtable(cf);
+ FD_SET(0, fdt->open_fds);
+ FD_CLR(0, fdt->close_on_exec);
+ spin_unlock(&cf->file_lock);
+
+ /* and disallow core files too */
+ current->signal->rlim[RLIMIT_CORE] = (struct rlimit){1, 1};
+
+ return 0;
+}
+
void do_coredump(long signr, int exit_code, struct pt_regs *regs)
{
struct core_state core_state;
@@ -1871,15 +1915,15 @@ void do_coredump(long signr, int exit_code, struct pt_regs *regs)
goto fail_unlock;
if (ispipe) {
- if (cprm.limit == 0) {
+ if (cprm.limit == 1) {
/*
* Normally core limits are irrelevant to pipes, since
* we're not writing to the file system, but we use
- * cprm.limit of 0 here as a speacial value. Any
- * non-zero limit gets set to RLIM_INFINITY below, but
+ * cprm.limit of 1 here as a speacial value. Any
+ * non-1 limit gets set to RLIM_INFINITY below, but
* a limit of 0 skips the dump. This is a consistent
* way to catch recursive crashes. We can still crash
- * if the core_pattern binary sets RLIM_CORE = !0
+ * if the core_pattern binary sets RLIM_CORE = !1
* but it runs as root, and can do lots of stupid things
* Note that we use task_tgid_vnr here to grab the pid
* of the process group leader. That way we get the
@@ -1887,7 +1931,7 @@ void do_coredump(long signr, int exit_code, struct pt_regs *regs)
* core_pattern process dies.
*/
printk(KERN_WARNING
- "Process %d(%s) has RLIMIT_CORE set to 0\n",
+ "Process %d(%s) has RLIMIT_CORE set to 1\n",
task_tgid_vnr(current), current->comm);
printk(KERN_WARNING "Aborting core\n");
goto fail_unlock;
@@ -1911,8 +1955,13 @@ void do_coredump(long signr, int exit_code, struct pt_regs *regs)
cprm.limit = RLIM_INFINITY;
/* SIGPIPE can happen, but it's just never processed */
- if (call_usermodehelper_pipe(helper_argv[0], helper_argv, NULL,
- &cprm.file)) {
+ cprm.file = NULL;
+ if (call_usermodehelper_fns(helper_argv[0], helper_argv, NULL,
+ UMH_WAIT_EXEC, umh_pipe_setup,
+ NULL, &cprm)) {
+ if (cprm.file)
+ filp_close(cprm.file, NULL);
+
printk(KERN_INFO "Core dump to %s pipe failed\n",
corename);
goto fail_dropcount;
diff --git a/include/linux/kmod.h b/include/linux/kmod.h
index f9edf63..5c05877 100644
--- a/include/linux/kmod.h
+++ b/include/linux/kmod.h
@@ -62,7 +62,6 @@ struct subprocess_info {
char **envp;
enum umh_wait wait;
int retval;
- struct file *stdin;
int (*init)(struct subprocess_info *info);
void (*cleanup)(struct subprocess_info *info);
void *data;
@@ -75,8 +74,6 @@ struct subprocess_info *call_usermodehelper_setup(char *path, char **argv,
/* Set various pieces of state into the subprocess_info structure */
void call_usermodehelper_setkeys(struct subprocess_info *info,
struct key *session_keyring);
-int call_usermodehelper_stdinpipe(struct subprocess_info *sub_info,
- struct file **filp);
void call_usermodehelper_setfns(struct subprocess_info *info,
int (*init)(struct subprocess_info *info),
void (*cleanup)(struct subprocess_info *info),
@@ -132,10 +129,6 @@ call_usermodehelper_keys(char *path, char **argv, char **envp,
extern void usermodehelper_init(void);
-struct file;
-extern int call_usermodehelper_pipe(char *path, char *argv[], char *envp[],
- struct file **filp);
-
extern int usermodehelper_disable(void);
extern void usermodehelper_enable(void);
diff --git a/kernel/kmod.c b/kernel/kmod.c
index 531ef62..d154454 100644
--- a/kernel/kmod.c
+++ b/kernel/kmod.c
@@ -147,23 +147,6 @@ static int ____call_usermodehelper(void *data)
commit_creds(sub_info->cred);
sub_info->cred = NULL;
- /* Install input pipe when needed */
- if (sub_info->stdin) {
- struct files_struct *f = current->files;
- struct fdtable *fdt;
- /* no races because files should be private here */
- sys_close(0);
- fd_install(0, sub_info->stdin);
- spin_lock(&f->file_lock);
- fdt = files_fdtable(f);
- FD_SET(0, fdt->open_fds);
- FD_CLR(0, fdt->close_on_exec);
- spin_unlock(&f->file_lock);
-
- /* and disallow core files too */
- current->signal->rlim[RLIMIT_CORE] = (struct rlimit){0, 0};
- }
-
/* We can run anywhere, unlike our parent keventd(). */
set_cpus_allowed_ptr(current, cpu_all_mask);
@@ -429,35 +412,6 @@ void call_usermodehelper_setfns(struct subprocess_info *info,
EXPORT_SYMBOL(call_usermodehelper_setfns);
/**
- * call_usermodehelper_stdinpipe - set up a pipe to be used for stdin
- * @sub_info: a subprocess_info returned by call_usermodehelper_setup
- * @filp: set to the write-end of a pipe
- *
- * This constructs a pipe, and sets the read end to be the stdin of the
- * subprocess, and returns the write-end in *@filp.
- */
-int call_usermodehelper_stdinpipe(struct subprocess_info *sub_info,
- struct file **filp)
-{
- struct file *f;
-
- f = create_write_pipe(0);
- if (IS_ERR(f))
- return PTR_ERR(f);
- *filp = f;
-
- f = create_read_pipe(f, 0);
- if (IS_ERR(f)) {
- free_write_pipe(*filp);
- return PTR_ERR(f);
- }
- sub_info->stdin = f;
-
- return 0;
-}
-EXPORT_SYMBOL(call_usermodehelper_stdinpipe);
-
-/**
* call_usermodehelper_exec - start a usermode application
* @sub_info: information about the subprocessa
* @wait: wait for the application to finish and return status.
@@ -504,42 +458,6 @@ unlock:
}
EXPORT_SYMBOL(call_usermodehelper_exec);
-/**
- * call_usermodehelper_pipe - call a usermode helper process with a pipe stdin
- * @path: path to usermode executable
- * @argv: arg vector for process
- * @envp: environment for process
- * @filp: set to the write-end of a pipe
- *
- * This is a simple wrapper which executes a usermode-helper function
- * with a pipe as stdin. It is implemented entirely in terms of
- * lower-level call_usermodehelper_* functions.
- */
-int call_usermodehelper_pipe(char *path, char **argv, char **envp,
- struct file **filp)
-{
- struct subprocess_info *sub_info;
- int ret;
-
- sub_info = call_usermodehelper_setup(path, argv, envp,
- GFP_KERNEL);
- if (sub_info == NULL)
- return -ENOMEM;
-
- ret = call_usermodehelper_stdinpipe(sub_info, filp);
- if (ret < 0) {
- call_usermodehelper_freeinfo(sub_info);
- return ret;
- }
-
- ret = call_usermodehelper_exec(sub_info, UMH_WAIT_EXEC);
- if (ret < 0) /* Failed to execute helper, close pipe */
- filp_close(*filp, NULL);
-
- return ret;
-}
-EXPORT_SYMBOL(call_usermodehelper_pipe);
-
void __init usermodehelper_init(void)
{
khelper_wq = create_singlethread_workqueue("khelper");
^ permalink raw reply related [flat|nested] 18+ messages in thread
* Re: [PATCH 1/2] kmod: add init function to usermodehelper
2010-03-15 12:33 ` [PATCH 1/2] kmod: add init function to usermodehelper Neil Horman
@ 2010-03-15 17:34 ` Oleg Nesterov
2010-03-15 17:56 ` Neil Horman
0 siblings, 1 reply; 18+ messages in thread
From: Oleg Nesterov @ 2010-03-15 17:34 UTC (permalink / raw)
To: Neil Horman; +Cc: akpm, linux-kernel, andi
On 03/15, Neil Horman wrote:
>
> --- a/kernel/kmod.c
> +++ b/kernel/kmod.c
> @@ -116,27 +116,16 @@ int __request_module(bool wait, const char *fmt, ...)
>
> trace_module_request(module_name, wait, _RET_IP_);
>
> - ret = call_usermodehelper(modprobe_path, argv, envp,
> - wait ? UMH_WAIT_PROC : UMH_WAIT_EXEC);
> + ret = call_usermodehelper_fns(modprobe_path, argv, envp,
> + wait ? UMH_WAIT_PROC : UMH_WAIT_EXEC,
> + NULL, NULL, NULL);
This change looks unnecessary, but doesn't hurt.
Reviewed-by: Oleg Nesterov <oleg@redhat.com>
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH 2/2] exec: replace call_usermodehelper_pipe with use of umh init function and resolve limit
2010-03-15 12:36 ` [PATCH 2/2] exec: replace call_usermodehelper_pipe with use of umh init function and resolve limit Neil Horman
@ 2010-03-15 17:39 ` Oleg Nesterov
0 siblings, 0 replies; 18+ messages in thread
From: Oleg Nesterov @ 2010-03-15 17:39 UTC (permalink / raw)
To: Neil Horman; +Cc: akpm, linux-kernel, andi
On 03/15, Neil Horman wrote:
>
> The first patch in this series introduced an init function to the
> call_usermodehelper api so that processes could be customized by caller.
> This patch takes advantage of that fact, by customizing the helper in
> do_coredump to create the pipe and set its core limit to one (for our
> recusrsion check). This lets us clean up the previous uglyness in the
> usermodehelper internals and factor call_usermodehelper out entirely.
> While I'm at it, we can also modify the helper setup to look for a core
> limit value of 1 rather than zero for our recursion check
>
> Signed-off-by: Neil Horman <nhorman@tuxdriver.com>
> CC: Oleg Nesterov <oleg@redhat.com>
> CC: Andi Kleen <andi@firstfloor.org>
Reviewed-by: Oleg Nesterov <oleg@redhat.com>
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH 1/2] kmod: add init function to usermodehelper
2010-03-15 17:34 ` Oleg Nesterov
@ 2010-03-15 17:56 ` Neil Horman
0 siblings, 0 replies; 18+ messages in thread
From: Neil Horman @ 2010-03-15 17:56 UTC (permalink / raw)
To: Oleg Nesterov; +Cc: akpm, linux-kernel, andi
On Mon, Mar 15, 2010 at 06:34:54PM +0100, Oleg Nesterov wrote:
> On 03/15, Neil Horman wrote:
> >
> > --- a/kernel/kmod.c
> > +++ b/kernel/kmod.c
> > @@ -116,27 +116,16 @@ int __request_module(bool wait, const char *fmt, ...)
> >
> > trace_module_request(module_name, wait, _RET_IP_);
> >
> > - ret = call_usermodehelper(modprobe_path, argv, envp,
> > - wait ? UMH_WAIT_PROC : UMH_WAIT_EXEC);
> > + ret = call_usermodehelper_fns(modprobe_path, argv, envp,
> > + wait ? UMH_WAIT_PROC : UMH_WAIT_EXEC,
> > + NULL, NULL, NULL);
>
> This change looks unnecessary, but doesn't hurt.
>
Yeah, I concur, it was part of the origional patch, when that
call_usermodehelper had already been replaced with call_usermodehelper_cleanup.
I made my change anyway, figuring part of the purpose of this longer term would
be to convert the call_usermodehelper functions to the init/cleanup enabled
variants, which andi's patchset will eventually do here
Neil
> Reviewed-by: Oleg Nesterov <oleg@redhat.com>
>
>
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH 0/6] umh: keys, signals, misc
2010-03-15 12:29 [PATCH 0/2] exec: refactor how call_usermodehelper works, and update the sense of the core_pipe recursion check (v4 rediff) Neil Horman
2010-03-15 12:33 ` [PATCH 1/2] kmod: add init function to usermodehelper Neil Horman
2010-03-15 12:36 ` [PATCH 2/2] exec: replace call_usermodehelper_pipe with use of umh init function and resolve limit Neil Horman
@ 2010-03-15 19:46 ` Oleg Nesterov
2010-03-15 19:46 ` [PATCH 1/6] umh: creds: convert call_usermodehelper_keys() to use subprocess_info->init() Oleg Nesterov
` (6 more replies)
2 siblings, 7 replies; 18+ messages in thread
From: Oleg Nesterov @ 2010-03-15 19:46 UTC (permalink / raw)
To: Andrew Morton
Cc: linux-kernel, andi, David Howells, Neil Horman, Roland McGrath
On 03/15, Neil Horman wrote:
>
> Hey, so I've rediffed and tested the user mode helper bits from my previous
> patch set that got wrecked when the rcustring bits got pulled.
More wrecked patches on top of Neil's refactoring.
None was changed, so I keeped the acks.
Oleg.
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH 1/6] umh: creds: convert call_usermodehelper_keys() to use subprocess_info->init()
2010-03-15 19:46 ` [PATCH 0/6] umh: keys, signals, misc Oleg Nesterov
@ 2010-03-15 19:46 ` Oleg Nesterov
2010-03-15 19:47 ` [PATCH 2/6] umh: creds: kill subprocess_info->cred logic Oleg Nesterov
` (5 subsequent siblings)
6 siblings, 0 replies; 18+ messages in thread
From: Oleg Nesterov @ 2010-03-15 19:46 UTC (permalink / raw)
To: Andrew Morton
Cc: linux-kernel, andi, David Howells, Neil Horman, Roland McGrath
call_usermodehelper_keys() uses call_usermodehelper_setkeys() to change
subprocess_info->cred in advance. Now that we have info->init() we can
change this code to set tgcred->session_keyring in context of execing
kernel thread.
Note: since currently call_usermodehelper_keys() is never called with
UMH_NO_WAIT, call_usermodehelper_keys()->key_get() and umh_keys_cleanup()
are not really needed, we could rely on install_session_keyring_to_cred()
which does key_get() on success.
Signed-off-by: Oleg Nesterov <oleg@redhat.com>
Acked-by: Neil Horman <nhorman@tuxdriver.com>
Acked-by: David Howells <dhowells@redhat.com>
---
include/linux/kmod.h | 17 -----------------
kernel/kmod.c | 18 ------------------
security/keys/internal.h | 1 +
security/keys/process_keys.c | 3 +--
security/keys/request_key.c | 32 ++++++++++++++++++++++++++++++++
5 files changed, 34 insertions(+), 37 deletions(-)
--- 34-rc1/include/linux/kmod.h~1_CONVERT_KEYS 2010-03-15 20:00:42.000000000 +0100
+++ 34-rc1/include/linux/kmod.h 2010-03-15 20:04:34.000000000 +0100
@@ -71,8 +71,6 @@ struct subprocess_info *call_usermodehel
char **envp, gfp_t gfp_mask);
/* Set various pieces of state into the subprocess_info structure */
-void call_usermodehelper_setkeys(struct subprocess_info *info,
- struct key *session_keyring);
void call_usermodehelper_setfns(struct subprocess_info *info,
int (*init)(struct subprocess_info *info),
void (*cleanup)(struct subprocess_info *info),
@@ -111,21 +109,6 @@ call_usermodehelper(char *path, char **a
NULL, NULL, NULL);
}
-static inline int
-call_usermodehelper_keys(char *path, char **argv, char **envp,
- struct key *session_keyring, enum umh_wait wait)
-{
- struct subprocess_info *info;
- gfp_t gfp_mask = (wait == UMH_NO_WAIT) ? GFP_ATOMIC : GFP_KERNEL;
-
- info = call_usermodehelper_setup(path, argv, envp, gfp_mask);
- if (info == NULL)
- return -ENOMEM;
-
- call_usermodehelper_setkeys(info, session_keyring);
- return call_usermodehelper_exec(info, wait);
-}
-
extern void usermodehelper_init(void);
extern int usermodehelper_disable(void);
--- 34-rc1/kernel/kmod.c~1_CONVERT_KEYS 2010-03-15 20:00:42.000000000 +0100
+++ 34-rc1/kernel/kmod.c 2010-03-15 20:04:34.000000000 +0100
@@ -367,24 +367,6 @@ struct subprocess_info *call_usermodehel
EXPORT_SYMBOL(call_usermodehelper_setup);
/**
- * call_usermodehelper_setkeys - set the session keys for usermode helper
- * @info: a subprocess_info returned by call_usermodehelper_setup
- * @session_keyring: the session keyring for the process
- */
-void call_usermodehelper_setkeys(struct subprocess_info *info,
- struct key *session_keyring)
-{
-#ifdef CONFIG_KEYS
- struct thread_group_cred *tgcred = info->cred->tgcred;
- key_put(tgcred->session_keyring);
- tgcred->session_keyring = key_get(session_keyring);
-#else
- BUG();
-#endif
-}
-EXPORT_SYMBOL(call_usermodehelper_setkeys);
-
-/**
* call_usermodehelper_setfns - set a cleanup/init function
* @info: a subprocess_info returned by call_usermodehelper_setup
* @cleanup: a cleanup function
--- 34-rc1/security/keys/internal.h~1_CONVERT_KEYS 2009-09-11 19:07:59.000000000 +0200
+++ 34-rc1/security/keys/internal.h 2010-03-15 20:04:34.000000000 +0100
@@ -115,6 +115,7 @@ extern struct key *find_keyring_by_name(
extern int install_user_keyrings(void);
extern int install_thread_keyring_to_cred(struct cred *);
extern int install_process_keyring_to_cred(struct cred *);
+extern int install_session_keyring_to_cred(struct cred *, struct key *);
extern struct key *request_key_and_link(struct key_type *type,
const char *description,
--- 34-rc1/security/keys/process_keys.c~1_CONVERT_KEYS 2009-09-11 19:07:59.000000000 +0200
+++ 34-rc1/security/keys/process_keys.c 2010-03-15 20:04:34.000000000 +0100
@@ -217,8 +217,7 @@ static int install_process_keyring(void)
/*
* install a session keyring directly to a credentials struct
*/
-static int install_session_keyring_to_cred(struct cred *cred,
- struct key *keyring)
+int install_session_keyring_to_cred(struct cred *cred, struct key *keyring)
{
unsigned long flags;
struct key *old;
--- 34-rc1/security/keys/request_key.c~1_CONVERT_KEYS 2009-04-13 17:05:52.000000000 +0200
+++ 34-rc1/security/keys/request_key.c 2010-03-15 20:04:34.000000000 +0100
@@ -58,6 +58,38 @@ void complete_request_key(struct key_con
}
EXPORT_SYMBOL(complete_request_key);
+static int umh_keys_init(struct subprocess_info *info)
+{
+ struct cred *cred = (struct cred*)current_cred();
+ struct key *keyring = info->data;
+ /*
+ * This is called in context of freshly forked kthread before
+ * kernel_execve(), we can just change our ->session_keyring.
+ */
+ return install_session_keyring_to_cred(cred, keyring);
+}
+
+static void umh_keys_cleanup(struct subprocess_info *info)
+{
+ struct key *keyring = info->data;
+ key_put(keyring);
+}
+
+static int call_usermodehelper_keys(char *path, char **argv, char **envp,
+ struct key *session_keyring, enum umh_wait wait)
+{
+ gfp_t gfp_mask = (wait == UMH_NO_WAIT) ? GFP_ATOMIC : GFP_KERNEL;
+ struct subprocess_info *info =
+ call_usermodehelper_setup(path, argv, envp, gfp_mask);
+
+ if (!info)
+ return -ENOMEM;
+
+ call_usermodehelper_setfns(info, umh_keys_init, umh_keys_cleanup,
+ key_get(session_keyring));
+ return call_usermodehelper_exec(info, wait);
+}
+
/*
* request userspace finish the construction of a key
* - execute "/sbin/request-key <op> <key> <uid> <gid> <keyring> <keyring> <keyring>"
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH 2/6] umh: creds: kill subprocess_info->cred logic
2010-03-15 19:46 ` [PATCH 0/6] umh: keys, signals, misc Oleg Nesterov
2010-03-15 19:46 ` [PATCH 1/6] umh: creds: convert call_usermodehelper_keys() to use subprocess_info->init() Oleg Nesterov
@ 2010-03-15 19:47 ` Oleg Nesterov
2010-03-15 19:47 ` [PATCH 3/6] call_usermodehelper: no need to unblock signals Oleg Nesterov
` (4 subsequent siblings)
6 siblings, 0 replies; 18+ messages in thread
From: Oleg Nesterov @ 2010-03-15 19:47 UTC (permalink / raw)
To: Andrew Morton
Cc: linux-kernel, andi, David Howells, Neil Horman, Roland McGrath
Now that nobody ever changes subprocess_info->cred we can kill this
member and related code. ____call_usermodehelper() always runs in the
context of freshly forked kernel thread, it has the proper ->cred
copied from its parent kthread, keventd.
Signed-off-by: Oleg Nesterov <oleg@redhat.com>
Acked-by: Neil Horman <nhorman@tuxdriver.com>
Acked-by: David Howells <dhowells@redhat.com>
---
include/linux/cred.h | 1
include/linux/kmod.h | 1
kernel/cred.c | 54 ---------------------------------------------------
kernel/kmod.c | 19 -----------------
4 files changed, 75 deletions(-)
--- 34-rc1/include/linux/cred.h~2_KILL_INFO_CRED 2010-03-11 13:11:50.000000000 +0100
+++ 34-rc1/include/linux/cred.h 2010-03-15 20:10:12.000000000 +0100
@@ -156,7 +156,6 @@ extern int copy_creds(struct task_struct
extern struct cred *cred_alloc_blank(void);
extern struct cred *prepare_creds(void);
extern struct cred *prepare_exec_creds(void);
-extern struct cred *prepare_usermodehelper_creds(void);
extern int commit_creds(struct cred *);
extern void abort_creds(struct cred *);
extern const struct cred *override_creds(const struct cred *);
--- 34-rc1/include/linux/kmod.h~2_KILL_INFO_CRED 2010-03-15 20:04:34.000000000 +0100
+++ 34-rc1/include/linux/kmod.h 2010-03-15 20:10:12.000000000 +0100
@@ -55,7 +55,6 @@ enum umh_wait {
struct subprocess_info {
struct work_struct work;
struct completion *complete;
- struct cred *cred;
char *path;
char **argv;
char **envp;
--- 34-rc1/kernel/cred.c~2_KILL_INFO_CRED 2010-02-15 11:15:21.000000000 +0100
+++ 34-rc1/kernel/cred.c 2010-03-15 20:10:12.000000000 +0100
@@ -347,60 +347,6 @@ struct cred *prepare_exec_creds(void)
}
/*
- * prepare new credentials for the usermode helper dispatcher
- */
-struct cred *prepare_usermodehelper_creds(void)
-{
-#ifdef CONFIG_KEYS
- struct thread_group_cred *tgcred = NULL;
-#endif
- struct cred *new;
-
-#ifdef CONFIG_KEYS
- tgcred = kzalloc(sizeof(*new->tgcred), GFP_ATOMIC);
- if (!tgcred)
- return NULL;
-#endif
-
- new = kmem_cache_alloc(cred_jar, GFP_ATOMIC);
- if (!new)
- return NULL;
-
- kdebug("prepare_usermodehelper_creds() alloc %p", new);
-
- memcpy(new, &init_cred, sizeof(struct cred));
-
- atomic_set(&new->usage, 1);
- set_cred_subscribers(new, 0);
- get_group_info(new->group_info);
- get_uid(new->user);
-
-#ifdef CONFIG_KEYS
- new->thread_keyring = NULL;
- new->request_key_auth = NULL;
- new->jit_keyring = KEY_REQKEY_DEFL_DEFAULT;
-
- atomic_set(&tgcred->usage, 1);
- spin_lock_init(&tgcred->lock);
- new->tgcred = tgcred;
-#endif
-
-#ifdef CONFIG_SECURITY
- new->security = NULL;
-#endif
- if (security_prepare_creds(new, &init_cred, GFP_ATOMIC) < 0)
- goto error;
- validate_creds(new);
-
- BUG_ON(atomic_read(&new->usage) != 1);
- return new;
-
-error:
- put_cred(new);
- return NULL;
-}
-
-/*
* Copy credentials for the new process created by fork()
*
* We share if we can, but under some circumstances we have to generate a new
--- 34-rc1/kernel/kmod.c~2_KILL_INFO_CRED 2010-03-15 20:04:34.000000000 +0100
+++ 34-rc1/kernel/kmod.c 2010-03-15 20:10:12.000000000 +0100
@@ -134,8 +134,6 @@ static int ____call_usermodehelper(void
struct subprocess_info *sub_info = data;
int retval;
- BUG_ON(atomic_read(&sub_info->cred->usage) != 1);
-
/* Unblock all signals */
spin_lock_irq(¤t->sighand->siglock);
flush_signal_handlers(current, 1);
@@ -143,10 +141,6 @@ static int ____call_usermodehelper(void
recalc_sigpending();
spin_unlock_irq(¤t->sighand->siglock);
- /* Install the credentials */
- commit_creds(sub_info->cred);
- sub_info->cred = NULL;
-
/* We can run anywhere, unlike our parent keventd(). */
set_cpus_allowed_ptr(current, cpu_all_mask);
@@ -174,8 +168,6 @@ void call_usermodehelper_freeinfo(struct
{
if (info->cleanup)
(*info->cleanup)(info);
- if (info->cred)
- put_cred(info->cred);
kfree(info);
}
EXPORT_SYMBOL(call_usermodehelper_freeinfo);
@@ -231,8 +223,6 @@ static void __call_usermodehelper(struct
pid_t pid;
enum umh_wait wait = sub_info->wait;
- BUG_ON(atomic_read(&sub_info->cred->usage) != 1);
-
/* CLONE_VFORK: wait until the usermode helper has execve'd
* successfully We need the data structures to stay around
* until that is done. */
@@ -355,12 +345,6 @@ struct subprocess_info *call_usermodehel
sub_info->path = path;
sub_info->argv = argv;
sub_info->envp = envp;
- sub_info->cred = prepare_usermodehelper_creds();
- if (!sub_info->cred) {
- kfree(sub_info);
- return NULL;
- }
-
out:
return sub_info;
}
@@ -411,9 +395,6 @@ int call_usermodehelper_exec(struct subp
DECLARE_COMPLETION_ONSTACK(done);
int retval = 0;
- BUG_ON(atomic_read(&sub_info->cred->usage) != 1);
- validate_creds(sub_info->cred);
-
helper_lock();
if (sub_info->path[0] == '\0')
goto out;
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH 3/6] call_usermodehelper: no need to unblock signals
2010-03-15 19:46 ` [PATCH 0/6] umh: keys, signals, misc Oleg Nesterov
2010-03-15 19:46 ` [PATCH 1/6] umh: creds: convert call_usermodehelper_keys() to use subprocess_info->init() Oleg Nesterov
2010-03-15 19:47 ` [PATCH 2/6] umh: creds: kill subprocess_info->cred logic Oleg Nesterov
@ 2010-03-15 19:47 ` Oleg Nesterov
2010-03-15 19:48 ` [PATCH 4/6] wait_for_helper: SIGCHLD from user-space can lead to use-after-free Oleg Nesterov
` (3 subsequent siblings)
6 siblings, 0 replies; 18+ messages in thread
From: Oleg Nesterov @ 2010-03-15 19:47 UTC (permalink / raw)
To: Andrew Morton
Cc: linux-kernel, andi, David Howells, Neil Horman, Roland McGrath
____call_usermodehelper() correctly calls flush_signal_handlers()
to set SIG_DFL, but sigemptyset(->blocked) and recalc_sigpending()
are not needed.
This kthread was forked by workqueue thread, all signals must be
unblocked and ignored, no pending signal is possible.
Signed-off-by: Oleg Nesterov <oleg@redhat.com>
---
kernel/kmod.c | 3 ---
1 file changed, 3 deletions(-)
--- 34-rc1/kernel/kmod.c~3_DONT_UNBLOCK 2010-03-15 20:10:12.000000000 +0100
+++ 34-rc1/kernel/kmod.c 2010-03-15 20:17:47.000000000 +0100
@@ -134,11 +134,8 @@ static int ____call_usermodehelper(void
struct subprocess_info *sub_info = data;
int retval;
- /* Unblock all signals */
spin_lock_irq(¤t->sighand->siglock);
flush_signal_handlers(current, 1);
- sigemptyset(¤t->blocked);
- recalc_sigpending();
spin_unlock_irq(¤t->sighand->siglock);
/* We can run anywhere, unlike our parent keventd(). */
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH 4/6] wait_for_helper: SIGCHLD from user-space can lead to use-after-free
2010-03-15 19:46 ` [PATCH 0/6] umh: keys, signals, misc Oleg Nesterov
` (2 preceding siblings ...)
2010-03-15 19:47 ` [PATCH 3/6] call_usermodehelper: no need to unblock signals Oleg Nesterov
@ 2010-03-15 19:48 ` Oleg Nesterov
2010-03-15 19:48 ` [PATCH 5/6] call_usermodehelper: simplify/fix UMH_NO_WAIT case Oleg Nesterov
` (2 subsequent siblings)
6 siblings, 0 replies; 18+ messages in thread
From: Oleg Nesterov @ 2010-03-15 19:48 UTC (permalink / raw)
To: Andrew Morton
Cc: linux-kernel, andi, David Howells, Neil Horman, Roland McGrath
1. wait_for_helper() calls allow_signal(SIGCHLD) to ensure the child
can't autoreap itself.
However, this means that a spurious SIGCHILD from user-space can
set TIF_SIGPENDING and:
- kernel_thread() or sys_wait4() can fail due to signal_pending()
- worse, wait4() can fail before ____call_usermodehelper() execs
or exits. In this case the caller may kfree(subprocess_info)
while the child still uses this memory.
Change the code to use SIG_DFL instead of magic "(void __user *)2"
set by allow_signal(). This means that SIGCHLD won't be delivered,
yet the child won't autoreap itsefl.
The problem is minor, only root can send a signal to this kthread.
2. If sys_wait4(&ret) fails it doesn't populate "ret", in this case
wait_for_helper() reports a random value from uninitialized var.
With this patch sys_wait4() should never fail, but still it makes
sense to initialize ret = -ECHILD so that the caller can notice
the problem.
Signed-off-by: Oleg Nesterov <oleg@redhat.com>
Acked-by: Neil Horman <nhorman@tuxdriver.com>
---
kernel/kmod.c | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
--- 34-rc1/kernel/kmod.c~4_SIGCHLD_RACE 2010-03-15 20:17:47.000000000 +0100
+++ 34-rc1/kernel/kmod.c 2010-03-15 20:28:31.000000000 +0100
@@ -175,16 +175,16 @@ static int wait_for_helper(void *data)
struct subprocess_info *sub_info = data;
pid_t pid;
- /* Install a handler: if SIGCLD isn't handled sys_wait4 won't
- * populate the status, but will return -ECHILD. */
- allow_signal(SIGCHLD);
+ /* If SIGCLD is ignored sys_wait4 won't populate the status. */
+ spin_lock_irq(¤t->sighand->siglock);
+ current->sighand->action[SIGCHLD-1].sa.sa_handler = SIG_DFL;
+ spin_unlock_irq(¤t->sighand->siglock);
pid = kernel_thread(____call_usermodehelper, sub_info, SIGCHLD);
if (pid < 0) {
sub_info->retval = pid;
} else {
- int ret;
-
+ int ret = -ECHILD;
/*
* Normally it is bogus to call wait4() from in-kernel because
* wait4() wants to write the exit code to a userspace address.
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH 5/6] call_usermodehelper: simplify/fix UMH_NO_WAIT case
2010-03-15 19:46 ` [PATCH 0/6] umh: keys, signals, misc Oleg Nesterov
` (3 preceding siblings ...)
2010-03-15 19:48 ` [PATCH 4/6] wait_for_helper: SIGCHLD from user-space can lead to use-after-free Oleg Nesterov
@ 2010-03-15 19:48 ` Oleg Nesterov
2010-03-15 19:49 ` [PATCH 6/6] call_usermodehelper: UMH_WAIT_EXEC ignores kernel_thread() failure Oleg Nesterov
2010-03-16 19:37 ` [PATCH 0/4] do_coredump: cleanups Oleg Nesterov
6 siblings, 0 replies; 18+ messages in thread
From: Oleg Nesterov @ 2010-03-15 19:48 UTC (permalink / raw)
To: Andrew Morton
Cc: linux-kernel, andi, David Howells, Neil Horman, Roland McGrath
__call_usermodehelper(UMH_NO_WAIT) has 2 problems:
- if kernel_thread() fails, call_usermodehelper_freeinfo()
is not called.
- for unknown reason UMH_NO_WAIT has UMH_WAIT_PROC logic,
we spawn yet another thread which waits until the user
mode application exits.
Change the UMH_NO_WAIT code to use ____call_usermodehelper() instead of
wait_for_helper(), and do call_usermodehelper_freeinfo() unconditionally.
We can rely on CLONE_VFORK, do_fork(CLONE_VFORK) until the child exits
or execs.
With or without this patch UMH_NO_WAIT does not report the error if
kernel_thread() fails, this is correct since the caller doesn't wait
for result.
Signed-off-by: Oleg Nesterov <oleg@redhat.com>
---
kernel/kmod.c | 10 ++++------
1 file changed, 4 insertions(+), 6 deletions(-)
--- 34-rc1/kernel/kmod.c~5_UMH_NO_WAIT 2010-03-15 20:28:31.000000000 +0100
+++ 34-rc1/kernel/kmod.c 2010-03-15 20:32:23.000000000 +0100
@@ -205,10 +205,7 @@ static int wait_for_helper(void *data)
sub_info->retval = ret;
}
- if (sub_info->wait == UMH_NO_WAIT)
- call_usermodehelper_freeinfo(sub_info);
- else
- complete(sub_info->complete);
+ complete(sub_info->complete);
return 0;
}
@@ -217,13 +214,13 @@ static void __call_usermodehelper(struct
{
struct subprocess_info *sub_info =
container_of(work, struct subprocess_info, work);
- pid_t pid;
enum umh_wait wait = sub_info->wait;
+ pid_t pid;
/* CLONE_VFORK: wait until the usermode helper has execve'd
* successfully We need the data structures to stay around
* until that is done. */
- if (wait == UMH_WAIT_PROC || wait == UMH_NO_WAIT)
+ if (wait == UMH_WAIT_PROC)
pid = kernel_thread(wait_for_helper, sub_info,
CLONE_FS | CLONE_FILES | SIGCHLD);
else
@@ -232,6 +229,7 @@ static void __call_usermodehelper(struct
switch (wait) {
case UMH_NO_WAIT:
+ call_usermodehelper_freeinfo(sub_info);
break;
case UMH_WAIT_PROC:
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH 6/6] call_usermodehelper: UMH_WAIT_EXEC ignores kernel_thread() failure
2010-03-15 19:46 ` [PATCH 0/6] umh: keys, signals, misc Oleg Nesterov
` (4 preceding siblings ...)
2010-03-15 19:48 ` [PATCH 5/6] call_usermodehelper: simplify/fix UMH_NO_WAIT case Oleg Nesterov
@ 2010-03-15 19:49 ` Oleg Nesterov
2010-03-16 19:37 ` [PATCH 0/4] do_coredump: cleanups Oleg Nesterov
6 siblings, 0 replies; 18+ messages in thread
From: Oleg Nesterov @ 2010-03-15 19:49 UTC (permalink / raw)
To: Andrew Morton
Cc: linux-kernel, andi, David Howells, Neil Horman, Roland McGrath
UMH_WAIT_EXEC should report the error if kernel_thread() fails, like
UMH_WAIT_PROC does.
Signed-off-by: Oleg Nesterov <oleg@redhat.com>
---
kernel/kmod.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
--- 34-rc1/kernel/kmod.c~6_UMH_WAIT_EXEC 2010-03-15 20:32:23.000000000 +0100
+++ 34-rc1/kernel/kmod.c 2010-03-15 20:37:07.000000000 +0100
@@ -235,10 +235,10 @@ static void __call_usermodehelper(struct
case UMH_WAIT_PROC:
if (pid > 0)
break;
- sub_info->retval = pid;
/* FALLTHROUGH */
-
case UMH_WAIT_EXEC:
+ if (pid < 0)
+ sub_info->retval = pid;
complete(sub_info->complete);
}
}
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH 0/4] do_coredump: cleanups
2010-03-15 19:46 ` [PATCH 0/6] umh: keys, signals, misc Oleg Nesterov
` (5 preceding siblings ...)
2010-03-15 19:49 ` [PATCH 6/6] call_usermodehelper: UMH_WAIT_EXEC ignores kernel_thread() failure Oleg Nesterov
@ 2010-03-16 19:37 ` Oleg Nesterov
2010-03-16 19:38 ` [PATCH 1/4] coredump: factor out the not-ispipe file checks Oleg Nesterov
` (3 more replies)
6 siblings, 4 replies; 18+ messages in thread
From: Oleg Nesterov @ 2010-03-16 19:37 UTC (permalink / raw)
To: Andrew Morton
Cc: linux-kernel, andi, David Howells, Neil Horman, Roland McGrath
On 03/15, Oleg Nesterov wrote:
>
> On 03/15, Neil Horman wrote:
> >
> > Hey, so I've rediffed and tested the user mode helper bits from my previous
> > patch set that got wrecked when the rcustring bits got pulled.
>
> More wrecked patches on top of Neil's refactoring.
And some more, this time in fs/exec.c.
do_coredump() is unreadable and needs cleanups.
Perhaps we should add the new helper which opens the file or pipe...
Oleg.
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH 1/4] coredump: factor out the not-ispipe file checks
2010-03-16 19:37 ` [PATCH 0/4] do_coredump: cleanups Oleg Nesterov
@ 2010-03-16 19:38 ` Oleg Nesterov
2010-03-16 19:38 ` [PATCH 2/4] coredump: cleanup "ispipe" code Oleg Nesterov
` (2 subsequent siblings)
3 siblings, 0 replies; 18+ messages in thread
From: Oleg Nesterov @ 2010-03-16 19:38 UTC (permalink / raw)
To: Andrew Morton
Cc: linux-kernel, andi, David Howells, Neil Horman, Roland McGrath
do_coredump() does a lot of file checks after it opens the file or
calls usermode helper. But all of these checks are only needed in
!ispipe case.
Move this code into the "else" branch and kill the ugly repetitive
ispipe checks.
Signed-off-by: Oleg Nesterov <oleg@redhat.com>
---
fs/exec.c | 61 ++++++++++++++++++++++++++++++-------------------------------
1 file changed, 30 insertions(+), 31 deletions(-)
--- 34-rc1/fs/exec.c~1_ISREG 2010-03-15 20:00:42.000000000 +0100
+++ 34-rc1/fs/exec.c 2010-03-16 18:06:12.000000000 +0100
@@ -1834,7 +1834,6 @@ void do_coredump(long signr, int exit_co
char corename[CORENAME_MAX_SIZE + 1];
struct mm_struct *mm = current->mm;
struct linux_binfmt * binfmt;
- struct inode * inode;
const struct cred *old_cred;
struct cred *cred;
int retval = 0;
@@ -1911,9 +1910,6 @@ void do_coredump(long signr, int exit_co
ispipe = format_corename(corename, signr);
unlock_kernel();
- if ((!ispipe) && (cprm.limit < binfmt->min_coredump))
- goto fail_unlock;
-
if (ispipe) {
if (cprm.limit == 1) {
/*
@@ -1966,39 +1962,42 @@ void do_coredump(long signr, int exit_co
corename);
goto fail_dropcount;
}
- } else
+ } else {
+ struct inode *inode;
+
+ if (cprm.limit < binfmt->min_coredump)
+ goto fail_unlock;
+
cprm.file = filp_open(corename,
O_CREAT | 2 | O_NOFOLLOW | O_LARGEFILE | flag,
0600);
- if (IS_ERR(cprm.file))
- goto fail_dropcount;
- inode = cprm.file->f_path.dentry->d_inode;
- if (inode->i_nlink > 1)
- goto close_fail; /* multiple links - don't dump */
- if (!ispipe && d_unhashed(cprm.file->f_path.dentry))
- goto close_fail;
+ if (IS_ERR(cprm.file))
+ goto fail_unlock;
- /* AK: actually i see no reason to not allow this for named pipes etc.,
- but keep the previous behaviour for now. */
- if (!ispipe && !S_ISREG(inode->i_mode))
- goto close_fail;
- /*
- * Dont allow local users get cute and trick others to coredump
- * into their pre-created files:
- * Note, this is not relevant for pipes
- */
- if (!ispipe && (inode->i_uid != current_fsuid()))
- goto close_fail;
- if (!cprm.file->f_op)
- goto close_fail;
- if (!cprm.file->f_op->write)
- goto close_fail;
- if (!ispipe &&
- do_truncate(cprm.file->f_path.dentry, 0, 0, cprm.file) != 0)
- goto close_fail;
+ inode = cprm.file->f_path.dentry->d_inode;
+ if (inode->i_nlink > 1)
+ goto close_fail;
+ if (d_unhashed(cprm.file->f_path.dentry))
+ goto close_fail;
+ /*
+ * AK: actually i see no reason to not allow this for named
+ * pipes etc, but keep the previous behaviour for now.
+ */
+ if (!S_ISREG(inode->i_mode))
+ goto close_fail;
+ /*
+ * Dont allow local users get cute and trick others to coredump
+ * into their pre-created files.
+ */
+ if (inode->i_uid != current_fsuid())
+ goto close_fail;
+ if (!cprm.file->f_op || !cprm.file->f_op->write)
+ goto close_fail;
+ if (do_truncate(cprm.file->f_path.dentry, 0, 0, cprm.file))
+ goto close_fail;
+ }
retval = binfmt->core_dump(&cprm);
-
if (retval)
current->signal->group_exit_code |= 0x80;
close_fail:
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH 2/4] coredump: cleanup "ispipe" code
2010-03-16 19:37 ` [PATCH 0/4] do_coredump: cleanups Oleg Nesterov
2010-03-16 19:38 ` [PATCH 1/4] coredump: factor out the not-ispipe file checks Oleg Nesterov
@ 2010-03-16 19:38 ` Oleg Nesterov
2010-03-16 19:39 ` [PATCH 3/4] coredump: factor out put_cred() calls Oleg Nesterov
2010-03-16 19:39 ` [PATCH 4/4] coredump: shift down_write(mmap_sem) into coredump_wait() Oleg Nesterov
3 siblings, 0 replies; 18+ messages in thread
From: Oleg Nesterov @ 2010-03-16 19:38 UTC (permalink / raw)
To: Andrew Morton
Cc: linux-kernel, andi, David Howells, Neil Horman, Roland McGrath
- kill "int dump_count", argv_split(argcp) accepts argcp == NULL.
- move "int dump_count" under " if (ispipe)" branch, fail_dropcount
can check ispipe.
- move "char **helper_argv" as well, change the code to do argv_free()
right after call_usermodehelper_fns().
- If call_usermodehelper_fns() fails goto close_fail label instead
of closing the file by hand.
Signed-off-by: Oleg Nesterov <oleg@redhat.com>
---
fs/exec.c | 39 +++++++++++++++++----------------------
1 file changed, 17 insertions(+), 22 deletions(-)
--- 34-rc1/fs/exec.c~2_IFIFO 2010-03-16 18:06:12.000000000 +0100
+++ 34-rc1/fs/exec.c 2010-03-16 18:09:13.000000000 +0100
@@ -1838,10 +1838,7 @@ void do_coredump(long signr, int exit_co
struct cred *cred;
int retval = 0;
int flag = 0;
- int ispipe = 0;
- char **helper_argv = NULL;
- int helper_argc = 0;
- int dump_count = 0;
+ int ispipe;
static atomic_t core_dump_count = ATOMIC_INIT(0);
struct coredump_params cprm = {
.signr = signr,
@@ -1911,6 +1908,9 @@ void do_coredump(long signr, int exit_co
unlock_kernel();
if (ispipe) {
+ int dump_count;
+ char **helper_argv;
+
if (cprm.limit == 1) {
/*
* Normally core limits are irrelevant to pipes, since
@@ -1932,6 +1932,7 @@ void do_coredump(long signr, int exit_co
printk(KERN_WARNING "Aborting core\n");
goto fail_unlock;
}
+ cprm.limit = RLIM_INFINITY;
dump_count = atomic_inc_return(&core_dump_count);
if (core_pipe_limit && (core_pipe_limit < dump_count)) {
@@ -1941,26 +1942,21 @@ void do_coredump(long signr, int exit_co
goto fail_dropcount;
}
- helper_argv = argv_split(GFP_KERNEL, corename+1, &helper_argc);
+ helper_argv = argv_split(GFP_KERNEL, corename+1, NULL);
if (!helper_argv) {
printk(KERN_WARNING "%s failed to allocate memory\n",
__func__);
goto fail_dropcount;
}
- cprm.limit = RLIM_INFINITY;
-
- /* SIGPIPE can happen, but it's just never processed */
- cprm.file = NULL;
- if (call_usermodehelper_fns(helper_argv[0], helper_argv, NULL,
- UMH_WAIT_EXEC, umh_pipe_setup,
- NULL, &cprm)) {
- if (cprm.file)
- filp_close(cprm.file, NULL);
-
+ retval = call_usermodehelper_fns(helper_argv[0], helper_argv,
+ NULL, UMH_WAIT_EXEC, umh_pipe_setup,
+ NULL, &cprm);
+ argv_free(helper_argv);
+ if (retval) {
printk(KERN_INFO "Core dump to %s pipe failed\n",
corename);
- goto fail_dropcount;
+ goto close_fail;
}
} else {
struct inode *inode;
@@ -2000,17 +1996,16 @@ void do_coredump(long signr, int exit_co
retval = binfmt->core_dump(&cprm);
if (retval)
current->signal->group_exit_code |= 0x80;
-close_fail:
+
if (ispipe && core_pipe_limit)
wait_for_dump_helpers(cprm.file);
- filp_close(cprm.file, NULL);
+close_fail:
+ if (cprm.file)
+ filp_close(cprm.file, NULL);
fail_dropcount:
- if (dump_count)
+ if (ispipe)
atomic_dec(&core_dump_count);
fail_unlock:
- if (helper_argv)
- argv_free(helper_argv);
-
revert_creds(old_cred);
put_cred(cred);
coredump_finish(mm);
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH 3/4] coredump: factor out put_cred() calls
2010-03-16 19:37 ` [PATCH 0/4] do_coredump: cleanups Oleg Nesterov
2010-03-16 19:38 ` [PATCH 1/4] coredump: factor out the not-ispipe file checks Oleg Nesterov
2010-03-16 19:38 ` [PATCH 2/4] coredump: cleanup "ispipe" code Oleg Nesterov
@ 2010-03-16 19:39 ` Oleg Nesterov
2010-03-16 19:39 ` [PATCH 4/4] coredump: shift down_write(mmap_sem) into coredump_wait() Oleg Nesterov
3 siblings, 0 replies; 18+ messages in thread
From: Oleg Nesterov @ 2010-03-16 19:39 UTC (permalink / raw)
To: Andrew Morton
Cc: linux-kernel, andi, David Howells, Neil Horman, Roland McGrath
Given that do_coredump() calls put_cred() on exit path, it is a bit ugly
to do put_cred() + "goto fail" twice, just add the new "fail_creds" label.
Signed-off-by: Oleg Nesterov <oleg@redhat.com>
---
fs/exec.c | 16 ++++++----------
1 file changed, 6 insertions(+), 10 deletions(-)
--- 34-rc1/fs/exec.c~3_CREDS 2010-03-16 18:09:13.000000000 +0100
+++ 34-rc1/fs/exec.c 2010-03-16 19:09:50.000000000 +0100
@@ -1859,10 +1859,8 @@ void do_coredump(long signr, int exit_co
goto fail;
cred = prepare_creds();
- if (!cred) {
- retval = -ENOMEM;
+ if (!cred)
goto fail;
- }
down_write(&mm->mmap_sem);
/*
@@ -1870,8 +1868,7 @@ void do_coredump(long signr, int exit_co
*/
if (mm->core_state || !__get_dumpable(cprm.mm_flags)) {
up_write(&mm->mmap_sem);
- put_cred(cred);
- goto fail;
+ goto fail_creds;
}
/*
@@ -1886,10 +1883,8 @@ void do_coredump(long signr, int exit_co
}
retval = coredump_wait(exit_code, &core_state);
- if (retval < 0) {
- put_cred(cred);
- goto fail;
- }
+ if (retval < 0)
+ goto fail_creds;
old_cred = override_creds(cred);
@@ -2006,9 +2001,10 @@ fail_dropcount:
if (ispipe)
atomic_dec(&core_dump_count);
fail_unlock:
+ coredump_finish(mm);
revert_creds(old_cred);
+fail_creds:
put_cred(cred);
- coredump_finish(mm);
fail:
return;
}
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH 4/4] coredump: shift down_write(mmap_sem) into coredump_wait()
2010-03-16 19:37 ` [PATCH 0/4] do_coredump: cleanups Oleg Nesterov
` (2 preceding siblings ...)
2010-03-16 19:39 ` [PATCH 3/4] coredump: factor out put_cred() calls Oleg Nesterov
@ 2010-03-16 19:39 ` Oleg Nesterov
3 siblings, 0 replies; 18+ messages in thread
From: Oleg Nesterov @ 2010-03-16 19:39 UTC (permalink / raw)
To: Andrew Morton
Cc: linux-kernel, andi, David Howells, Neil Horman, Roland McGrath
- move the cprm.mm_flags checks up, before we take mmap_sem
- move down_write(mmap_sem) and ->core_state check from do_coredump()
to coredump_wait()
This simplifies the code and makes the locking symmetrical.
Signed-off-by: Oleg Nesterov <oleg@redhat.com>
---
fs/exec.c | 19 +++++++------------
1 file changed, 7 insertions(+), 12 deletions(-)
--- 34-rc1/fs/exec.c~4_MMAP_SEM 2010-03-16 19:09:50.000000000 +0100
+++ 34-rc1/fs/exec.c 2010-03-16 19:28:23.000000000 +0100
@@ -1659,12 +1659,15 @@ static int coredump_wait(int exit_code,
struct task_struct *tsk = current;
struct mm_struct *mm = tsk->mm;
struct completion *vfork_done;
- int core_waiters;
+ int core_waiters = -EBUSY;
init_completion(&core_state->startup);
core_state->dumper.task = tsk;
core_state->dumper.next = NULL;
- core_waiters = zap_threads(tsk, mm, core_state, exit_code);
+
+ down_write(&mm->mmap_sem);
+ if (!mm->core_state)
+ core_waiters = zap_threads(tsk, mm, core_state, exit_code);
up_write(&mm->mmap_sem);
if (unlikely(core_waiters < 0))
@@ -1857,20 +1860,12 @@ void do_coredump(long signr, int exit_co
binfmt = mm->binfmt;
if (!binfmt || !binfmt->core_dump)
goto fail;
+ if (!__get_dumpable(cprm.mm_flags))
+ goto fail;
cred = prepare_creds();
if (!cred)
goto fail;
-
- down_write(&mm->mmap_sem);
- /*
- * If another thread got here first, or we are not dumpable, bail out.
- */
- if (mm->core_state || !__get_dumpable(cprm.mm_flags)) {
- up_write(&mm->mmap_sem);
- goto fail_creds;
- }
-
/*
* We cannot trust fsuid as being the "true" uid of the
* process nor do we know its entire history. We only know it
^ permalink raw reply [flat|nested] 18+ messages in thread
end of thread, other threads:[~2010-03-16 19:41 UTC | newest]
Thread overview: 18+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2010-03-15 12:29 [PATCH 0/2] exec: refactor how call_usermodehelper works, and update the sense of the core_pipe recursion check (v4 rediff) Neil Horman
2010-03-15 12:33 ` [PATCH 1/2] kmod: add init function to usermodehelper Neil Horman
2010-03-15 17:34 ` Oleg Nesterov
2010-03-15 17:56 ` Neil Horman
2010-03-15 12:36 ` [PATCH 2/2] exec: replace call_usermodehelper_pipe with use of umh init function and resolve limit Neil Horman
2010-03-15 17:39 ` Oleg Nesterov
2010-03-15 19:46 ` [PATCH 0/6] umh: keys, signals, misc Oleg Nesterov
2010-03-15 19:46 ` [PATCH 1/6] umh: creds: convert call_usermodehelper_keys() to use subprocess_info->init() Oleg Nesterov
2010-03-15 19:47 ` [PATCH 2/6] umh: creds: kill subprocess_info->cred logic Oleg Nesterov
2010-03-15 19:47 ` [PATCH 3/6] call_usermodehelper: no need to unblock signals Oleg Nesterov
2010-03-15 19:48 ` [PATCH 4/6] wait_for_helper: SIGCHLD from user-space can lead to use-after-free Oleg Nesterov
2010-03-15 19:48 ` [PATCH 5/6] call_usermodehelper: simplify/fix UMH_NO_WAIT case Oleg Nesterov
2010-03-15 19:49 ` [PATCH 6/6] call_usermodehelper: UMH_WAIT_EXEC ignores kernel_thread() failure Oleg Nesterov
2010-03-16 19:37 ` [PATCH 0/4] do_coredump: cleanups Oleg Nesterov
2010-03-16 19:38 ` [PATCH 1/4] coredump: factor out the not-ispipe file checks Oleg Nesterov
2010-03-16 19:38 ` [PATCH 2/4] coredump: cleanup "ispipe" code Oleg Nesterov
2010-03-16 19:39 ` [PATCH 3/4] coredump: factor out put_cred() calls Oleg Nesterov
2010-03-16 19:39 ` [PATCH 4/4] coredump: shift down_write(mmap_sem) into coredump_wait() Oleg Nesterov
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox