From da2a4fa0dbb0e6603abea4868d67579bebc7d769 Mon Sep 17 00:00:00 2001 From: Nick Alcock Date: Mon, 22 Jul 2013 19:16:07 +0100 Subject: [PATCH] wait: fix loss of error code from waitid() when info is provided One of the review comments on the original waitid patches suggested using the pattern ret = __put_user(...); ret |= __put_user(...); ... rather than if (!ret) ret = __put_user(...); if (!ret) ret |= __put_user(...); This turns out to be a bad idea if ret is used for anything else first, e.g. if it is used to store errnos from waitid(), since it overwrites any nonzero error with the return value of __put_user(), even if that is zero. The solution is to use a temporary error variable instead, and not assign it to the actual return value if the temporary error variable is still zero after doing all the __put_user()s (as is the common case). Signed-off-by: Nick Alcock --- kernel/exit.c | 38 ++++++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/kernel/exit.c b/kernel/exit.c index 9ef11c30c41e..4149f391c08f 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -949,16 +949,17 @@ static int wait_noreap_copyout(struct wait_opts *wo, struct task_struct *p, put_task_struct(p); infop = wo->wo_info; if (infop) { - retval = put_user(SIGCHLD, &infop->si_signo); - retval |= put_user(0, &infop->si_errno); - if (!retval) - retval = put_user((short)why, &infop->si_code); - if (!retval) - retval = put_user(pid, &infop->si_pid); - if (!retval) - retval = put_user(uid, &infop->si_uid); - if (!retval) - retval = put_user(status, &infop->si_status); + int put_user_retval; + + put_user_retval = put_user(SIGCHLD, &infop->si_signo); + put_user_retval |= put_user(0, &infop->si_errno); + put_user_retval |= put_user((short)why, &infop->si_code); + put_user_retval |= put_user(pid, &infop->si_pid); + put_user_retval |= put_user(uid, &infop->si_uid); + put_user_retval |= put_user(status, &infop->si_status); + + if (put_user_retval != 0) + retval = put_user_retval; } if (!retval) retval = pid; @@ -1581,17 +1582,22 @@ SYSCALL_DEFINE5(waitid, int, which, pid_t, upid, struct siginfo __user *, if (ret > 0) { ret = 0; } else if (infop) { + int put_user_ret; + /* * For a WNOHANG return, clear out all the fields * we would set so the user can easily tell the * difference. */ - ret = __put_user(0, &infop->si_signo); - ret |= __put_user(0, &infop->si_errno); - ret |= __put_user(0, &infop->si_code); - ret |= __put_user(0, &infop->si_pid); - ret |= __put_user(0, &infop->si_uid); - ret |= __put_user(0, &infop->si_status); + put_user_ret = __put_user(0, &infop->si_signo); + put_user_ret |= __put_user(0, &infop->si_errno); + put_user_ret |= __put_user(0, &infop->si_code); + put_user_ret |= __put_user(0, &infop->si_pid); + put_user_ret |= __put_user(0, &infop->si_uid); + put_user_ret |= __put_user(0, &infop->si_status); + + if (put_user_ret != 0) + ret = put_user_ret; } return ret; -- 2.50.1