Messages in this thread | | | From | (Eric W. Biederman) | Date | Tue, 21 Jul 2020 08:35:00 -0500 | Subject | Re: [PATCH] kernel: add a kernel_wait helper |
| |
Christoph Hellwig <hch@lst.de> writes:
> 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.
Acked-by: "Eric W. Biederman" <ebiederm@xmission.com>
> Signed-off-by: Christoph Hellwig <hch@lst.de> > --- > include/linux/sched/task.h | 1 + > kernel/exit.c | 16 ++++++++++++++++ > kernel/umh.c | 29 ++++------------------------- > 3 files changed, 21 insertions(+), 25 deletions(-) > > diff --git a/include/linux/sched/task.h b/include/linux/sched/task.h > index 38359071236ad7..a80007df396e95 100644 > --- a/include/linux/sched/task.h > +++ b/include/linux/sched/task.h > @@ -102,6 +102,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); > > diff --git a/kernel/exit.c b/kernel/exit.c > index 727150f2810338..fd598846df0b17 100644 > --- a/kernel/exit.c > +++ b/kernel/exit.c > @@ -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) > { > diff --git a/kernel/umh.c b/kernel/umh.c > index 79f139a7ca03c6..733430921f47d7 100644 > --- a/kernel/umh.c > +++ b/kernel/umh.c > @@ -130,37 +130,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); > }
| |