diff options
author | Vitaly Mayatskikh <v.mayatskih@gmail.com> | 2009-09-23 18:56:51 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-09-24 10:21:00 -0400 |
commit | dfe16dfa4ac178d9a10b489a73d535c6976e48d2 (patch) | |
tree | 0ebd7bddf7e99915baed4955faeed2af5bba4203 /kernel | |
parent | b6e763f07fba6243d2a553ed9a4f3e10a789932a (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.c | 49 |
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: | |||
1645 | end: | 1645 | end: |
1646 | __set_current_state(TASK_RUNNING); | 1646 | __set_current_state(TASK_RUNNING); |
1647 | remove_wait_queue(¤t->signal->wait_chldexit, &wo->child_wait); | 1647 | remove_wait_queue(¤t->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: */ |