diff options
author | Neil Horman <nhorman@tuxdriver.com> | 2009-09-22 19:43:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-09-23 10:39:28 -0400 |
commit | c02e3f361c75da04ca3025b4d19e947e9cc62ed3 (patch) | |
tree | 92605809366dda20430fa9dc7d0b9b9f2f3c0bd9 /kernel | |
parent | 1fd7317d02ec03c6fdf072317841287933d06d24 (diff) |
kmod: fix race in usermodehelper code
The user mode helper code has a race in it. call_usermodehelper_exec()
takes an allocated subprocess_info structure, which it passes to a
workqueue, and then passes it to a kernel thread which it creates, after
which it calls complete to signal to the caller of
call_usermodehelper_exec() that it can free the subprocess_info struct.
But since we use that structure in the created thread, we can't call
complete from __call_usermodehelper(), which is where we create the kernel
thread. We need to call complete() from within the kernel thread and then
not use subprocess_info afterward in the case of UMH_WAIT_EXEC. Tested
successfully by me.
Signed-off-by: Neil Horman <nhorman@tuxdriver.com>
Cc: Rusty Russell <rusty@rustcorp.com.au>
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.c | 13 |
1 files changed, 8 insertions, 5 deletions
diff --git a/kernel/kmod.c b/kernel/kmod.c index 9fcb53a11f87..689d20f39305 100644 --- a/kernel/kmod.c +++ b/kernel/kmod.c | |||
@@ -143,6 +143,7 @@ struct subprocess_info { | |||
143 | static int ____call_usermodehelper(void *data) | 143 | static int ____call_usermodehelper(void *data) |
144 | { | 144 | { |
145 | struct subprocess_info *sub_info = data; | 145 | struct subprocess_info *sub_info = data; |
146 | enum umh_wait wait = sub_info->wait; | ||
146 | int retval; | 147 | int retval; |
147 | 148 | ||
148 | BUG_ON(atomic_read(&sub_info->cred->usage) != 1); | 149 | BUG_ON(atomic_read(&sub_info->cred->usage) != 1); |
@@ -184,10 +185,14 @@ static int ____call_usermodehelper(void *data) | |||
184 | */ | 185 | */ |
185 | set_user_nice(current, 0); | 186 | set_user_nice(current, 0); |
186 | 187 | ||
188 | if (wait == UMH_WAIT_EXEC) | ||
189 | complete(sub_info->complete); | ||
190 | |||
187 | retval = kernel_execve(sub_info->path, sub_info->argv, sub_info->envp); | 191 | retval = kernel_execve(sub_info->path, sub_info->argv, sub_info->envp); |
188 | 192 | ||
189 | /* Exec failed? */ | 193 | /* Exec failed? */ |
190 | sub_info->retval = retval; | 194 | if (wait != UMH_WAIT_EXEC) |
195 | sub_info->retval = retval; | ||
191 | do_exit(0); | 196 | do_exit(0); |
192 | } | 197 | } |
193 | 198 | ||
@@ -266,16 +271,14 @@ static void __call_usermodehelper(struct work_struct *work) | |||
266 | 271 | ||
267 | switch (wait) { | 272 | switch (wait) { |
268 | case UMH_NO_WAIT: | 273 | case UMH_NO_WAIT: |
274 | case UMH_WAIT_EXEC: | ||
269 | break; | 275 | break; |
270 | 276 | ||
271 | case UMH_WAIT_PROC: | 277 | case UMH_WAIT_PROC: |
272 | if (pid > 0) | 278 | if (pid > 0) |
273 | break; | 279 | break; |
274 | sub_info->retval = pid; | 280 | sub_info->retval = pid; |
275 | /* FALLTHROUGH */ | 281 | break; |
276 | |||
277 | case UMH_WAIT_EXEC: | ||
278 | complete(sub_info->complete); | ||
279 | } | 282 | } |
280 | } | 283 | } |
281 | 284 | ||