Commit dfe16dfa4ac178d9a10b489a73d535c6976e48d2
Committed by
Linus Torvalds
1 parent
b6e763f07f
Exists in
master
and in
39 other branches
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>
Showing 1 changed file with 23 additions and 26 deletions Side-by-side Diff
kernel/exit.c
... | ... | @@ -1645,32 +1645,6 @@ |
1645 | 1645 | end: |
1646 | 1646 | __set_current_state(TASK_RUNNING); |
1647 | 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 | 1648 | return retval; |
1675 | 1649 | } |
1676 | 1650 | |
... | ... | @@ -1715,6 +1689,29 @@ |
1715 | 1689 | wo.wo_stat = NULL; |
1716 | 1690 | wo.wo_rusage = ru; |
1717 | 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 | 1715 | put_pid(pid); |
1719 | 1716 | |
1720 | 1717 | /* avoid REGPARM breakage on x86: */ |