diff options
Diffstat (limited to 'kernel/kmod.c')
-rw-r--r-- | kernel/kmod.c | 193 |
1 files changed, 38 insertions, 155 deletions
diff --git a/kernel/kmod.c b/kernel/kmod.c index bf0e231d9702..6e9b19667a8d 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 | } |
124 | EXPORT_SYMBOL(__request_module); | 126 | EXPORT_SYMBOL(__request_module); |
125 | #endif /* CONFIG_MODULES */ | 127 | #endif /* CONFIG_MODULES */ |
126 | 128 | ||
127 | struct 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 | */ |
@@ -145,36 +134,10 @@ static int ____call_usermodehelper(void *data) | |||
145 | struct subprocess_info *sub_info = data; | 134 | struct subprocess_info *sub_info = data; |
146 | int retval; | 135 | int retval; |
147 | 136 | ||
148 | BUG_ON(atomic_read(&sub_info->cred->usage) != 1); | ||
149 | |||
150 | /* Unblock all signals */ | ||
151 | spin_lock_irq(¤t->sighand->siglock); | 137 | spin_lock_irq(¤t->sighand->siglock); |
152 | flush_signal_handlers(current, 1); | 138 | flush_signal_handlers(current, 1); |
153 | sigemptyset(¤t->blocked); | ||
154 | recalc_sigpending(); | ||
155 | spin_unlock_irq(¤t->sighand->siglock); | 139 | spin_unlock_irq(¤t->sighand->siglock); |
156 | 140 | ||
157 | /* Install the credentials */ | ||
158 | commit_creds(sub_info->cred); | ||
159 | sub_info->cred = NULL; | ||
160 | |||
161 | /* Install input pipe when needed */ | ||
162 | if (sub_info->stdin) { | ||
163 | struct files_struct *f = current->files; | ||
164 | struct fdtable *fdt; | ||
165 | /* no races because files should be private here */ | ||
166 | sys_close(0); | ||
167 | fd_install(0, sub_info->stdin); | ||
168 | spin_lock(&f->file_lock); | ||
169 | fdt = files_fdtable(f); | ||
170 | FD_SET(0, fdt->open_fds); | ||
171 | FD_CLR(0, fdt->close_on_exec); | ||
172 | spin_unlock(&f->file_lock); | ||
173 | |||
174 | /* and disallow core files too */ | ||
175 | current->signal->rlim[RLIMIT_CORE] = (struct rlimit){0, 0}; | ||
176 | } | ||
177 | |||
178 | /* We can run anywhere, unlike our parent keventd(). */ | 141 | /* We can run anywhere, unlike our parent keventd(). */ |
179 | set_cpus_allowed_ptr(current, cpu_all_mask); | 142 | set_cpus_allowed_ptr(current, cpu_all_mask); |
180 | 143 | ||
@@ -184,9 +147,16 @@ static int ____call_usermodehelper(void *data) | |||
184 | */ | 147 | */ |
185 | set_user_nice(current, 0); | 148 | set_user_nice(current, 0); |
186 | 149 | ||
150 | if (sub_info->init) { | ||
151 | retval = sub_info->init(sub_info); | ||
152 | if (retval) | ||
153 | goto fail; | ||
154 | } | ||
155 | |||
187 | retval = kernel_execve(sub_info->path, sub_info->argv, sub_info->envp); | 156 | retval = kernel_execve(sub_info->path, sub_info->argv, sub_info->envp); |
188 | 157 | ||
189 | /* Exec failed? */ | 158 | /* Exec failed? */ |
159 | fail: | ||
190 | sub_info->retval = retval; | 160 | sub_info->retval = retval; |
191 | do_exit(0); | 161 | do_exit(0); |
192 | } | 162 | } |
@@ -194,9 +164,7 @@ static int ____call_usermodehelper(void *data) | |||
194 | void call_usermodehelper_freeinfo(struct subprocess_info *info) | 164 | void call_usermodehelper_freeinfo(struct subprocess_info *info) |
195 | { | 165 | { |
196 | if (info->cleanup) | 166 | if (info->cleanup) |
197 | (*info->cleanup)(info->argv, info->envp); | 167 | (*info->cleanup)(info); |
198 | if (info->cred) | ||
199 | put_cred(info->cred); | ||
200 | kfree(info); | 168 | kfree(info); |
201 | } | 169 | } |
202 | EXPORT_SYMBOL(call_usermodehelper_freeinfo); | 170 | EXPORT_SYMBOL(call_usermodehelper_freeinfo); |
@@ -207,16 +175,16 @@ static int wait_for_helper(void *data) | |||
207 | struct subprocess_info *sub_info = data; | 175 | struct subprocess_info *sub_info = data; |
208 | pid_t pid; | 176 | pid_t pid; |
209 | 177 | ||
210 | /* Install a handler: if SIGCLD isn't handled sys_wait4 won't | 178 | /* If SIGCLD is ignored sys_wait4 won't populate the status. */ |
211 | * populate the status, but will return -ECHILD. */ | 179 | spin_lock_irq(¤t->sighand->siglock); |
212 | allow_signal(SIGCHLD); | 180 | current->sighand->action[SIGCHLD-1].sa.sa_handler = SIG_DFL; |
181 | spin_unlock_irq(¤t->sighand->siglock); | ||
213 | 182 | ||
214 | pid = kernel_thread(____call_usermodehelper, sub_info, SIGCHLD); | 183 | pid = kernel_thread(____call_usermodehelper, sub_info, SIGCHLD); |
215 | if (pid < 0) { | 184 | if (pid < 0) { |
216 | sub_info->retval = pid; | 185 | sub_info->retval = pid; |
217 | } else { | 186 | } else { |
218 | int ret; | 187 | int ret = -ECHILD; |
219 | |||
220 | /* | 188 | /* |
221 | * Normally it is bogus to call wait4() from in-kernel because | 189 | * Normally it is bogus to call wait4() from in-kernel because |
222 | * wait4() wants to write the exit code to a userspace address. | 190 | * wait4() wants to write the exit code to a userspace address. |
@@ -237,10 +205,7 @@ static int wait_for_helper(void *data) | |||
237 | sub_info->retval = ret; | 205 | sub_info->retval = ret; |
238 | } | 206 | } |
239 | 207 | ||
240 | if (sub_info->wait == UMH_NO_WAIT) | 208 | complete(sub_info->complete); |
241 | call_usermodehelper_freeinfo(sub_info); | ||
242 | else | ||
243 | complete(sub_info->complete); | ||
244 | return 0; | 209 | return 0; |
245 | } | 210 | } |
246 | 211 | ||
@@ -249,15 +214,13 @@ static void __call_usermodehelper(struct work_struct *work) | |||
249 | { | 214 | { |
250 | struct subprocess_info *sub_info = | 215 | struct subprocess_info *sub_info = |
251 | container_of(work, struct subprocess_info, work); | 216 | container_of(work, struct subprocess_info, work); |
252 | pid_t pid; | ||
253 | enum umh_wait wait = sub_info->wait; | 217 | enum umh_wait wait = sub_info->wait; |
254 | 218 | pid_t pid; | |
255 | BUG_ON(atomic_read(&sub_info->cred->usage) != 1); | ||
256 | 219 | ||
257 | /* CLONE_VFORK: wait until the usermode helper has execve'd | 220 | /* CLONE_VFORK: wait until the usermode helper has execve'd |
258 | * successfully We need the data structures to stay around | 221 | * successfully We need the data structures to stay around |
259 | * until that is done. */ | 222 | * until that is done. */ |
260 | if (wait == UMH_WAIT_PROC || wait == UMH_NO_WAIT) | 223 | if (wait == UMH_WAIT_PROC) |
261 | pid = kernel_thread(wait_for_helper, sub_info, | 224 | pid = kernel_thread(wait_for_helper, sub_info, |
262 | CLONE_FS | CLONE_FILES | SIGCHLD); | 225 | CLONE_FS | CLONE_FILES | SIGCHLD); |
263 | else | 226 | else |
@@ -266,15 +229,16 @@ static void __call_usermodehelper(struct work_struct *work) | |||
266 | 229 | ||
267 | switch (wait) { | 230 | switch (wait) { |
268 | case UMH_NO_WAIT: | 231 | case UMH_NO_WAIT: |
232 | call_usermodehelper_freeinfo(sub_info); | ||
269 | break; | 233 | break; |
270 | 234 | ||
271 | case UMH_WAIT_PROC: | 235 | case UMH_WAIT_PROC: |
272 | if (pid > 0) | 236 | if (pid > 0) |
273 | break; | 237 | break; |
274 | sub_info->retval = pid; | ||
275 | /* FALLTHROUGH */ | 238 | /* FALLTHROUGH */ |
276 | |||
277 | case UMH_WAIT_EXEC: | 239 | case UMH_WAIT_EXEC: |
240 | if (pid < 0) | ||
241 | sub_info->retval = pid; | ||
278 | complete(sub_info->complete); | 242 | complete(sub_info->complete); |
279 | } | 243 | } |
280 | } | 244 | } |
@@ -376,80 +340,37 @@ struct subprocess_info *call_usermodehelper_setup(char *path, char **argv, | |||
376 | sub_info->path = path; | 340 | sub_info->path = path; |
377 | sub_info->argv = argv; | 341 | sub_info->argv = argv; |
378 | sub_info->envp = envp; | 342 | sub_info->envp = envp; |
379 | sub_info->cred = prepare_usermodehelper_creds(); | ||
380 | if (!sub_info->cred) { | ||
381 | kfree(sub_info); | ||
382 | return NULL; | ||
383 | } | ||
384 | |||
385 | out: | 343 | out: |
386 | return sub_info; | 344 | return sub_info; |
387 | } | 345 | } |
388 | EXPORT_SYMBOL(call_usermodehelper_setup); | 346 | EXPORT_SYMBOL(call_usermodehelper_setup); |
389 | 347 | ||
390 | /** | 348 | /** |
391 | * call_usermodehelper_setkeys - set the session keys for usermode helper | 349 | * call_usermodehelper_setfns - set a cleanup/init function |
392 | * @info: a subprocess_info returned by call_usermodehelper_setup | ||
393 | * @session_keyring: the session keyring for the process | ||
394 | */ | ||
395 | void call_usermodehelper_setkeys(struct subprocess_info *info, | ||
396 | struct key *session_keyring) | ||
397 | { | ||
398 | #ifdef CONFIG_KEYS | ||
399 | struct thread_group_cred *tgcred = info->cred->tgcred; | ||
400 | key_put(tgcred->session_keyring); | ||
401 | tgcred->session_keyring = key_get(session_keyring); | ||
402 | #else | ||
403 | BUG(); | ||
404 | #endif | ||
405 | } | ||
406 | EXPORT_SYMBOL(call_usermodehelper_setkeys); | ||
407 | |||
408 | /** | ||
409 | * call_usermodehelper_setcleanup - set a cleanup function | ||
410 | * @info: a subprocess_info returned by call_usermodehelper_setup | 350 | * @info: a subprocess_info returned by call_usermodehelper_setup |
411 | * @cleanup: a cleanup function | 351 | * @cleanup: a cleanup function |
352 | * @init: an init function | ||
353 | * @data: arbitrary context sensitive data | ||
412 | * | 354 | * |
413 | * The cleanup function is just befor ethe subprocess_info is about to | 355 | * The init function is used to customize the helper process prior to |
356 | * exec. A non-zero return code causes the process to error out, exit, | ||
357 | * and return the failure to the calling process | ||
358 | * | ||
359 | * 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 | 360 | * 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 | 361 | * Function must be runnable in either a process context or the |
416 | * context in which call_usermodehelper_exec is called. | 362 | * context in which call_usermodehelper_exec is called. |
417 | */ | 363 | */ |
418 | void call_usermodehelper_setcleanup(struct subprocess_info *info, | 364 | void call_usermodehelper_setfns(struct subprocess_info *info, |
419 | void (*cleanup)(char **argv, char **envp)) | 365 | int (*init)(struct subprocess_info *info), |
366 | void (*cleanup)(struct subprocess_info *info), | ||
367 | void *data) | ||
420 | { | 368 | { |
421 | info->cleanup = cleanup; | 369 | info->cleanup = cleanup; |
370 | info->init = init; | ||
371 | info->data = data; | ||
422 | } | 372 | } |
423 | EXPORT_SYMBOL(call_usermodehelper_setcleanup); | 373 | EXPORT_SYMBOL(call_usermodehelper_setfns); |
424 | |||
425 | /** | ||
426 | * call_usermodehelper_stdinpipe - set up a pipe to be used for stdin | ||
427 | * @sub_info: a subprocess_info returned by call_usermodehelper_setup | ||
428 | * @filp: set to the write-end of a pipe | ||
429 | * | ||
430 | * This constructs a pipe, and sets the read end to be the stdin of the | ||
431 | * subprocess, and returns the write-end in *@filp. | ||
432 | */ | ||
433 | int call_usermodehelper_stdinpipe(struct subprocess_info *sub_info, | ||
434 | struct file **filp) | ||
435 | { | ||
436 | struct file *f; | ||
437 | |||
438 | f = create_write_pipe(0); | ||
439 | if (IS_ERR(f)) | ||
440 | return PTR_ERR(f); | ||
441 | *filp = f; | ||
442 | |||
443 | f = create_read_pipe(f, 0); | ||
444 | if (IS_ERR(f)) { | ||
445 | free_write_pipe(*filp); | ||
446 | return PTR_ERR(f); | ||
447 | } | ||
448 | sub_info->stdin = f; | ||
449 | |||
450 | return 0; | ||
451 | } | ||
452 | EXPORT_SYMBOL(call_usermodehelper_stdinpipe); | ||
453 | 374 | ||
454 | /** | 375 | /** |
455 | * call_usermodehelper_exec - start a usermode application | 376 | * call_usermodehelper_exec - start a usermode application |
@@ -469,9 +390,6 @@ int call_usermodehelper_exec(struct subprocess_info *sub_info, | |||
469 | DECLARE_COMPLETION_ONSTACK(done); | 390 | DECLARE_COMPLETION_ONSTACK(done); |
470 | int retval = 0; | 391 | int retval = 0; |
471 | 392 | ||
472 | BUG_ON(atomic_read(&sub_info->cred->usage) != 1); | ||
473 | validate_creds(sub_info->cred); | ||
474 | |||
475 | helper_lock(); | 393 | helper_lock(); |
476 | if (sub_info->path[0] == '\0') | 394 | if (sub_info->path[0] == '\0') |
477 | goto out; | 395 | goto out; |
@@ -498,41 +416,6 @@ unlock: | |||
498 | } | 416 | } |
499 | EXPORT_SYMBOL(call_usermodehelper_exec); | 417 | EXPORT_SYMBOL(call_usermodehelper_exec); |
500 | 418 | ||
501 | /** | ||
502 | * call_usermodehelper_pipe - call a usermode helper process with a pipe stdin | ||
503 | * @path: path to usermode executable | ||
504 | * @argv: arg vector for process | ||
505 | * @envp: environment for process | ||
506 | * @filp: set to the write-end of a pipe | ||
507 | * | ||
508 | * This is a simple wrapper which executes a usermode-helper function | ||
509 | * with a pipe as stdin. It is implemented entirely in terms of | ||
510 | * lower-level call_usermodehelper_* functions. | ||
511 | */ | ||
512 | int call_usermodehelper_pipe(char *path, char **argv, char **envp, | ||
513 | struct file **filp) | ||
514 | { | ||
515 | struct subprocess_info *sub_info; | ||
516 | int ret; | ||
517 | |||
518 | sub_info = call_usermodehelper_setup(path, argv, envp, GFP_KERNEL); | ||
519 | if (sub_info == NULL) | ||
520 | return -ENOMEM; | ||
521 | |||
522 | ret = call_usermodehelper_stdinpipe(sub_info, filp); | ||
523 | if (ret < 0) { | ||
524 | call_usermodehelper_freeinfo(sub_info); | ||
525 | return ret; | ||
526 | } | ||
527 | |||
528 | ret = call_usermodehelper_exec(sub_info, UMH_WAIT_EXEC); | ||
529 | if (ret < 0) /* Failed to execute helper, close pipe */ | ||
530 | filp_close(*filp, NULL); | ||
531 | |||
532 | return ret; | ||
533 | } | ||
534 | EXPORT_SYMBOL(call_usermodehelper_pipe); | ||
535 | |||
536 | void __init usermodehelper_init(void) | 419 | void __init usermodehelper_init(void) |
537 | { | 420 | { |
538 | khelper_wq = create_singlethread_workqueue("khelper"); | 421 | khelper_wq = create_singlethread_workqueue("khelper"); |