Commit dfe16dfa4ac178d9a10b489a73d535c6976e48d2

Authored by Vitaly Mayatskikh
Committed by Linus Torvalds
1 parent b6e763f07f

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

... ... @@ -1645,32 +1645,6 @@
1645 1645 end:
1646 1646 __set_current_state(TASK_RUNNING);
1647 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 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: */