aboutsummaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
authorVitaly Mayatskikh <v.mayatskih@gmail.com>2009-09-23 18:56:51 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2009-09-24 10:21:00 -0400
commitdfe16dfa4ac178d9a10b489a73d535c6976e48d2 (patch)
tree0ebd7bddf7e99915baed4955faeed2af5bba4203 /kernel
parentb6e763f07fba6243d2a553ed9a4f3e10a789932a (diff)
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>
Diffstat (limited to 'kernel')
-rw-r--r--kernel/exit.c49
1 files changed, 23 insertions, 26 deletions
diff --git a/kernel/exit.c b/kernel/exit.c
index 1daa7f46bccd..2cc69eb8db2a 100644
--- a/kernel/exit.c
+++ b/kernel/exit.c
@@ -1645,32 +1645,6 @@ notask:
1645end: 1645end:
1646 __set_current_state(TASK_RUNNING); 1646 __set_current_state(TASK_RUNNING);
1647 remove_wait_queue(&current->signal->wait_chldexit, &wo->child_wait); 1647 remove_wait_queue(&current->signal->wait_chldexit, &wo->child_wait);
1648
1649 if (wo->wo_info) {
1650 struct siginfo __user *infop = wo->wo_info;
1651
1652 if (retval > 0)
1653 retval = 0;
1654 else {
1655 /*
1656 * For a WNOHANG return, clear out all the fields
1657 * we would set so the user can easily tell the
1658 * difference.
1659 */
1660 if (!retval)
1661 retval = put_user(0, &infop->si_signo);
1662 if (!retval)
1663 retval = put_user(0, &infop->si_errno);
1664 if (!retval)
1665 retval = put_user(0, &infop->si_code);
1666 if (!retval)
1667 retval = put_user(0, &infop->si_pid);
1668 if (!retval)
1669 retval = put_user(0, &infop->si_uid);
1670 if (!retval)
1671 retval = put_user(0, &infop->si_status);
1672 }
1673 }
1674 return retval; 1648 return retval;
1675} 1649}
1676 1650
@@ -1715,6 +1689,29 @@ SYSCALL_DEFINE5(waitid, int, which, pid_t, upid, struct siginfo __user *,
1715 wo.wo_stat = NULL; 1689 wo.wo_stat = NULL;
1716 wo.wo_rusage = ru; 1690 wo.wo_rusage = ru;
1717 ret = do_wait(&wo); 1691 ret = do_wait(&wo);
1692
1693 if (ret > 0) {
1694 ret = 0;
1695 } else if (infop) {
1696 /*
1697 * For a WNOHANG return, clear out all the fields
1698 * we would set so the user can easily tell the
1699 * difference.
1700 */
1701 if (!ret)
1702 ret = put_user(0, &infop->si_signo);
1703 if (!ret)
1704 ret = put_user(0, &infop->si_errno);
1705 if (!ret)
1706 ret = put_user(0, &infop->si_code);
1707 if (!ret)
1708 ret = put_user(0, &infop->si_pid);
1709 if (!ret)
1710 ret = put_user(0, &infop->si_uid);
1711 if (!ret)
1712 ret = put_user(0, &infop->si_status);
1713 }
1714
1718 put_pid(pid); 1715 put_pid(pid);
1719 1716
1720 /* avoid REGPARM breakage on x86: */ 1717 /* avoid REGPARM breakage on x86: */