aboutsummaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
authorNeil Horman <nhorman@tuxdriver.com>2010-05-26 17:42:58 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2010-05-27 12:12:44 -0400
commita06a4dc3a08201ff6a8a958f935b3cbf7744115f (patch)
treeb338edde5edb31f51e0b7eb86e6c4ce8063c703f /kernel
parent065add3941bdca54fe04ed3471a96bce9af88793 (diff)
kmod: add init function to usermodehelper
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 fixed 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: 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> Reviewed-by: Oleg Nesterov <oleg@redhat.com> Cc: Andi Kleen <andi@firstfloor.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'kernel')
-rw-r--r--kernel/kmod.c51
-rw-r--r--kernel/sys.c6
2 files changed, 32 insertions, 25 deletions
diff --git a/kernel/kmod.c b/kernel/kmod.c
index bf0e231d9702..531ef62cf536 100644
--- a/kernel/kmod.c
+++ b/kernel/kmod.c
@@ -116,27 +116,16 @@ int __request_module(bool wait, const char *fmt, ...)
116 116
117 trace_module_request(module_name, wait, _RET_IP_); 117 trace_module_request(module_name, wait, _RET_IP_);
118 118
119 ret = call_usermodehelper(modprobe_path, argv, envp, 119 ret = call_usermodehelper_fns(modprobe_path, argv, envp,
120 wait ? UMH_WAIT_PROC : UMH_WAIT_EXEC); 120 wait ? UMH_WAIT_PROC : UMH_WAIT_EXEC,
121 NULL, NULL, NULL);
122
121 atomic_dec(&kmod_concurrent); 123 atomic_dec(&kmod_concurrent);
122 return ret; 124 return ret;
123} 125}
124EXPORT_SYMBOL(__request_module); 126EXPORT_SYMBOL(__request_module);
125#endif /* CONFIG_MODULES */ 127#endif /* CONFIG_MODULES */
126 128
127struct subprocess_info {
128 struct work_struct work;
129 struct completion *complete;
130 struct cred *cred;
131 char *path;
132 char **argv;
133 char **envp;
134 enum umh_wait wait;
135 int retval;
136 struct file *stdin;
137 void (*cleanup)(char **argv, char **envp);
138};
139
140/* 129/*
141 * This is the task which runs the usermode application 130 * This is the task which runs the usermode application
142 */ 131 */
@@ -184,9 +173,16 @@ static int ____call_usermodehelper(void *data)
184 */ 173 */
185 set_user_nice(current, 0); 174 set_user_nice(current, 0);
186 175
176 if (sub_info->init) {
177 retval = sub_info->init(sub_info);
178 if (retval)
179 goto fail;
180 }
181
187 retval = kernel_execve(sub_info->path, sub_info->argv, sub_info->envp); 182 retval = kernel_execve(sub_info->path, sub_info->argv, sub_info->envp);
188 183
189 /* Exec failed? */ 184 /* Exec failed? */
185fail:
190 sub_info->retval = retval; 186 sub_info->retval = retval;
191 do_exit(0); 187 do_exit(0);
192} 188}
@@ -194,7 +190,7 @@ static int ____call_usermodehelper(void *data)
194void call_usermodehelper_freeinfo(struct subprocess_info *info) 190void call_usermodehelper_freeinfo(struct subprocess_info *info)
195{ 191{
196 if (info->cleanup) 192 if (info->cleanup)
197 (*info->cleanup)(info->argv, info->envp); 193 (*info->cleanup)(info);
198 if (info->cred) 194 if (info->cred)
199 put_cred(info->cred); 195 put_cred(info->cred);
200 kfree(info); 196 kfree(info);
@@ -406,21 +402,31 @@ void call_usermodehelper_setkeys(struct subprocess_info *info,
406EXPORT_SYMBOL(call_usermodehelper_setkeys); 402EXPORT_SYMBOL(call_usermodehelper_setkeys);
407 403
408/** 404/**
409 * call_usermodehelper_setcleanup - set a cleanup function 405 * call_usermodehelper_setfns - set a cleanup/init function
410 * @info: a subprocess_info returned by call_usermodehelper_setup 406 * @info: a subprocess_info returned by call_usermodehelper_setup
411 * @cleanup: a cleanup function 407 * @cleanup: a cleanup function
408 * @init: an init function
409 * @data: arbitrary context sensitive data
410 *
411 * The init function is used to customize the helper process prior to
412 * exec. A non-zero return code causes the process to error out, exit,
413 * and return the failure to the calling process
412 * 414 *
413 * The cleanup function is just befor ethe subprocess_info is about to 415 * The cleanup function is just before ethe subprocess_info is about to
414 * be freed. This can be used for freeing the argv and envp. The 416 * be freed. This can be used for freeing the argv and envp. The
415 * Function must be runnable in either a process context or the 417 * Function must be runnable in either a process context or the
416 * context in which call_usermodehelper_exec is called. 418 * context in which call_usermodehelper_exec is called.
417 */ 419 */
418void call_usermodehelper_setcleanup(struct subprocess_info *info, 420void call_usermodehelper_setfns(struct subprocess_info *info,
419 void (*cleanup)(char **argv, char **envp)) 421 int (*init)(struct subprocess_info *info),
422 void (*cleanup)(struct subprocess_info *info),
423 void *data)
420{ 424{
421 info->cleanup = cleanup; 425 info->cleanup = cleanup;
426 info->init = init;
427 info->data = data;
422} 428}
423EXPORT_SYMBOL(call_usermodehelper_setcleanup); 429EXPORT_SYMBOL(call_usermodehelper_setfns);
424 430
425/** 431/**
426 * call_usermodehelper_stdinpipe - set up a pipe to be used for stdin 432 * 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,
515 struct subprocess_info *sub_info; 521 struct subprocess_info *sub_info;
516 int ret; 522 int ret;
517 523
518 sub_info = call_usermodehelper_setup(path, argv, envp, GFP_KERNEL); 524 sub_info = call_usermodehelper_setup(path, argv, envp,
525 GFP_KERNEL);
519 if (sub_info == NULL) 526 if (sub_info == NULL)
520 return -ENOMEM; 527 return -ENOMEM;
521 528
diff --git a/kernel/sys.c b/kernel/sys.c
index 0d36d889c74d..e83ddbbaf89d 100644
--- a/kernel/sys.c
+++ b/kernel/sys.c
@@ -1632,9 +1632,9 @@ SYSCALL_DEFINE3(getcpu, unsigned __user *, cpup, unsigned __user *, nodep,
1632 1632
1633char poweroff_cmd[POWEROFF_CMD_PATH_LEN] = "/sbin/poweroff"; 1633char poweroff_cmd[POWEROFF_CMD_PATH_LEN] = "/sbin/poweroff";
1634 1634
1635static void argv_cleanup(char **argv, char **envp) 1635static void argv_cleanup(struct subprocess_info *info)
1636{ 1636{
1637 argv_free(argv); 1637 argv_free(info->argv);
1638} 1638}
1639 1639
1640/** 1640/**
@@ -1668,7 +1668,7 @@ int orderly_poweroff(bool force)
1668 goto out; 1668 goto out;
1669 } 1669 }
1670 1670
1671 call_usermodehelper_setcleanup(info, argv_cleanup); 1671 call_usermodehelper_setfns(info, NULL, argv_cleanup, NULL);
1672 1672
1673 ret = call_usermodehelper_exec(info, UMH_NO_WAIT); 1673 ret = call_usermodehelper_exec(info, UMH_NO_WAIT);
1674 1674