do_wait: fix sys_waitid()-specific behaviour
do_wait() checks ->wo_info to figure out who is the caller. If it's not NULL the caller should be sys_waitid(), in that case do_wait() fixes up the retval or zeros ->wo_info, depending on retval from underlying function. This is bug: user can pass ->wo_info == NULL and sys_waitid() will return incorrect value. man 2 waitid says: waitid(): returns 0 on success Test-case: int main(void) { if (fork()) assert(waitid(P_ALL, 0, NULL, WEXITED) == 0); return 0; } Result: Assertion `waitid(P_ALL, 0, ((void *)0), 4) == 0' failed. Move that code to sys_waitid(). User-visible change: sys_waitid() will return 0 on success, either infop is set or not. Note, there's another bug in wait_noreap_copyout() which affects return value of sys_waitid(). It will be fixed in next patch. Signed-off-by: Vitaly Mayatskikh <v.mayatskih@gmail.com> Reviewed-by: Oleg Nesterov <oleg@redhat.com> Cc: Roland McGrath <roland@redhat.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
parent
b6e763f07f
commit
dfe16dfa4a
|
@ -1645,32 +1645,6 @@ notask:
|
|||
end:
|
||||
__set_current_state(TASK_RUNNING);
|
||||
remove_wait_queue(¤t->signal->wait_chldexit, &wo->child_wait);
|
||||
|
||||
if (wo->wo_info) {
|
||||
struct siginfo __user *infop = wo->wo_info;
|
||||
|
||||
if (retval > 0)
|
||||
retval = 0;
|
||||
else {
|
||||
/*
|
||||
* For a WNOHANG return, clear out all the fields
|
||||
* we would set so the user can easily tell the
|
||||
* difference.
|
||||
*/
|
||||
if (!retval)
|
||||
retval = put_user(0, &infop->si_signo);
|
||||
if (!retval)
|
||||
retval = put_user(0, &infop->si_errno);
|
||||
if (!retval)
|
||||
retval = put_user(0, &infop->si_code);
|
||||
if (!retval)
|
||||
retval = put_user(0, &infop->si_pid);
|
||||
if (!retval)
|
||||
retval = put_user(0, &infop->si_uid);
|
||||
if (!retval)
|
||||
retval = put_user(0, &infop->si_status);
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
@ -1715,6 +1689,29 @@ SYSCALL_DEFINE5(waitid, int, which, pid_t, upid, struct siginfo __user *,
|
|||
wo.wo_stat = NULL;
|
||||
wo.wo_rusage = ru;
|
||||
ret = do_wait(&wo);
|
||||
|
||||
if (ret > 0) {
|
||||
ret = 0;
|
||||
} else if (infop) {
|
||||
/*
|
||||
* For a WNOHANG return, clear out all the fields
|
||||
* we would set so the user can easily tell the
|
||||
* difference.
|
||||
*/
|
||||
if (!ret)
|
||||
ret = put_user(0, &infop->si_signo);
|
||||
if (!ret)
|
||||
ret = put_user(0, &infop->si_errno);
|
||||
if (!ret)
|
||||
ret = put_user(0, &infop->si_code);
|
||||
if (!ret)
|
||||
ret = put_user(0, &infop->si_pid);
|
||||
if (!ret)
|
||||
ret = put_user(0, &infop->si_uid);
|
||||
if (!ret)
|
||||
ret = put_user(0, &infop->si_status);
|
||||
}
|
||||
|
||||
put_pid(pid);
|
||||
|
||||
/* avoid REGPARM breakage on x86: */
|
||||
|
|
Loading…
Reference in New Issue