]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
wait: fix loss of error code from waitid() when info is provided
authorNick Alcock <nick.alcock@oracle.com>
Mon, 22 Jul 2013 18:16:07 +0000 (19:16 +0100)
committerNick Alcock <nick.alcock@oracle.com>
Tue, 21 Jul 2015 14:28:59 +0000 (15:28 +0100)
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 <nick.alcock@oracle.com>
kernel/exit.c

index 9ef11c30c41ef6c4a4987810b1ac8a5a5d806465..4149f391c08f68e1ffc4bccb2e09ba4a5b910c80 100644 (file)
@@ -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;