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.
Showing the changes to kernel (2 of 3 files). Show all
Unified · Split
| @@ -1626,6 +1626,22 @@ long kernel_wait4(pid_t upid, int __user *stat_addr, int options, · 1625 unmodified lines | ||
| 1626 | 1626 | return ret; |
| 1627 | 1627 | } |
| 1628 | 1628 | |
| 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 | + | |
| 1629 | 1645 | SYSCALL_DEFINE4(wait4, pid_t, upid, int __user *, stat_addr, |
| 1630 | 1646 | int, options, struct rusage __user *, ru) |
| 1631 | 1647 | { |
| @@ -119,37 +119,16 @@ static void call_usermodehelper_exec_sync(struct subprocess_info *sub_info) · 118 unmodified lines | ||
| 119 | 119 | { |
| 120 | 120 | pid_t pid; |
| 121 | 121 | |
| 122 | − /* If SIGCLD is ignored kernel_wait4 won't populate the status. */ | |
| 122 | + /* If SIGCLD is ignored do_wait won't populate the status. */ | |
| 123 | 123 | kernel_sigaction(SIGCHLD, SIG_DFL); |
| 124 | 124 | pid = kernel_thread(call_usermodehelper_exec_async, sub_info, SIGCHLD); |
| 125 | − if (pid < 0) { | |
| 125 | + if (pid < 0) | |
| 126 | 126 | 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); | |
| 149 | 129 | |
| 150 | 130 | /* Restore default kernel sig handler */ |
| 151 | 131 | kernel_sigaction(SIGCHLD, SIG_IGN); |
| 152 | − | |
| 153 | 132 | umh_complete(sub_info); |
| 154 | 133 | } |
| 155 | 134 | |