kernel: add a kernel_wait helper
Add a helper that waits for a pid and stores the status in the passed in kernel pointer. Use it to fix the usage of kernel_wait4 in call_usermodehelper_exec_sync that only happens to work due to the implicit set_fs(KERNEL_DS) for kernel threads. Signed-off-by: Christoph Hellwig <hch@lst.de> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Reviewed-by: Andrew Morton <akpm@linux-foundation.org> Acked-by: "Eric W. Biederman" <ebiederm@xmission.com> Cc: Luis Chamberlain <mcgrof@kernel.org> Link: http://lkml.kernel.org/r/20200721130449.5008-1-hch@lst.de Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
Родитель
f48ff83e9c
Коммит
8043fc147a
|
@ -88,6 +88,7 @@ struct task_struct *fork_idle(int);
|
|||
struct mm_struct *copy_init_mm(void);
|
||||
extern pid_t kernel_thread(int (*fn)(void *), void *arg, unsigned long flags);
|
||||
extern long kernel_wait4(pid_t, int __user *, int, struct rusage *);
|
||||
int kernel_wait(pid_t pid, int *stat);
|
||||
|
||||
extern void free_task(struct task_struct *tsk);
|
||||
|
||||
|
|
|
@ -1626,6 +1626,22 @@ long kernel_wait4(pid_t upid, int __user *stat_addr, int options,
|
|||
return ret;
|
||||
}
|
||||
|
||||
int kernel_wait(pid_t pid, int *stat)
|
||||
{
|
||||
struct wait_opts wo = {
|
||||
.wo_type = PIDTYPE_PID,
|
||||
.wo_pid = find_get_pid(pid),
|
||||
.wo_flags = WEXITED,
|
||||
};
|
||||
int ret;
|
||||
|
||||
ret = do_wait(&wo);
|
||||
if (ret > 0 && wo.wo_stat)
|
||||
*stat = wo.wo_stat;
|
||||
put_pid(wo.wo_pid);
|
||||
return ret;
|
||||
}
|
||||
|
||||
SYSCALL_DEFINE4(wait4, pid_t, upid, int __user *, stat_addr,
|
||||
int, options, struct rusage __user *, ru)
|
||||
{
|
||||
|
|
29
kernel/umh.c
29
kernel/umh.c
|
@ -119,37 +119,16 @@ static void call_usermodehelper_exec_sync(struct subprocess_info *sub_info)
|
|||
{
|
||||
pid_t pid;
|
||||
|
||||
/* If SIGCLD is ignored kernel_wait4 won't populate the status. */
|
||||
/* If SIGCLD is ignored do_wait won't populate the status. */
|
||||
kernel_sigaction(SIGCHLD, SIG_DFL);
|
||||
pid = kernel_thread(call_usermodehelper_exec_async, sub_info, SIGCHLD);
|
||||
if (pid < 0) {
|
||||
if (pid < 0)
|
||||
sub_info->retval = pid;
|
||||
} else {
|
||||
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.
|
||||
* But call_usermodehelper_exec_sync() always runs as kernel
|
||||
* thread (workqueue) and put_user() to a kernel address works
|
||||
* OK for kernel threads, due to their having an mm_segment_t
|
||||
* which spans the entire address space.
|
||||
*
|
||||
* Thus the __user pointer cast is valid here.
|
||||
*/
|
||||
kernel_wait4(pid, (int __user *)&ret, 0, NULL);
|
||||
|
||||
/*
|
||||
* If ret is 0, either call_usermodehelper_exec_async failed and
|
||||
* the real error code is already in sub_info->retval or
|
||||
* sub_info->retval is 0 anyway, so don't mess with it then.
|
||||
*/
|
||||
if (ret)
|
||||
sub_info->retval = ret;
|
||||
}
|
||||
else
|
||||
kernel_wait(pid, &sub_info->retval);
|
||||
|
||||
/* Restore default kernel sig handler */
|
||||
kernel_sigaction(SIGCHLD, SIG_IGN);
|
||||
|
||||
umh_complete(sub_info);
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче