History

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.

Christoph Hellwig committed 6 years ago

Commit 8043fc11 parent f48ff833 files · +21 −25Browse files

Unified · Split

3 files
  1. include/linux/sched/task.h +1 −0
  2. kernel/exit.c +16 −0
  3. kernel/umh.c +4 −25
include/linux/sched/task.h+1−0
@@ -88,6 +88,7 @@ struct task_struct *fork_idle(int); · 87 unmodified lines
8888 struct mm_struct *copy_init_mm(void);
8989 extern pid_t kernel_thread(int (*fn)(void *), void *arg, unsigned long flags);
9090 extern long kernel_wait4(pid_t, int __user *, int, struct rusage *);
91+int kernel_wait(pid_t pid, int *stat);
9192
9293 extern void free_task(struct task_struct *tsk);
9394
kernel/exit.c+16−0
@@ -1626,6 +1626,22 @@ long kernel_wait4(pid_t upid, int __user *stat_addr, int options, · 1625 unmodified lines
16261626 return ret;
16271627 }
16281628
1629+int kernel_wait(pid_t pid, int *stat)
1630+{
1631+ struct wait_opts wo = {
1632+ .wo_type = PIDTYPE_PID,
1633+ .wo_pid = find_get_pid(pid),
1634+ .wo_flags = WEXITED,
1635+ };
1636+ int ret;
1637+
1638+ ret = do_wait(&wo);
1639+ if (ret > 0 && wo.wo_stat)
1640+ *stat = wo.wo_stat;
1641+ put_pid(wo.wo_pid);
1642+ return ret;
1643+}
1644+
16291645 SYSCALL_DEFINE4(wait4, pid_t, upid, int __user *, stat_addr,
16301646 int, options, struct rusage __user *, ru)
16311647 {
kernel/umh.c+4−25
@@ -119,37 +119,16 @@ static void call_usermodehelper_exec_sync(struct subprocess_info *sub_info) · 118 unmodified lines
119119 {
120120 pid_t pid;
121121
122− /* If SIGCLD is ignored kernel_wait4 won't populate the status. */
122+ /* If SIGCLD is ignored do_wait won't populate the status. */
123123 kernel_sigaction(SIGCHLD, SIG_DFL);
124124 pid = kernel_thread(call_usermodehelper_exec_async, sub_info, SIGCHLD);
125− if (pid < 0) {
125+ if (pid < 0)
126126 sub_info->retval = pid;
127− } else {
128− int ret = -ECHILD;
129− /*
130− * Normally it is bogus to call wait4() from in-kernel because
131− * wait4() wants to write the exit code to a userspace address.
132− * But call_usermodehelper_exec_sync() always runs as kernel
133− * thread (workqueue) and put_user() to a kernel address works
134− * OK for kernel threads, due to their having an mm_segment_t
135− * which spans the entire address space.
136− *
137− * Thus the __user pointer cast is valid here.
138− */
139− kernel_wait4(pid, (int __user *)&ret, 0, NULL);
140−
141− /*
142− * If ret is 0, either call_usermodehelper_exec_async failed and
143− * the real error code is already in sub_info->retval or
144− * sub_info->retval is 0 anyway, so don't mess with it then.
145− */
146− if (ret)
147− sub_info->retval = ret;
148− }
127+ else
128+ kernel_wait(pid, &sub_info->retval);
149129
150130 /* Restore default kernel sig handler */
151131 kernel_sigaction(SIGCHLD, SIG_IGN);
152−
153132 umh_complete(sub_info);
154133 }
155134