af_unix: Don't set err in unix_stream_read_generic unless there was an error
authorRainer Weikusat <rweikusat@mobileactivedefense.com>
Mon, 8 Feb 2016 18:47:19 +0000 (18:47 +0000)
committerBen Hutchings <ben@decadent.org.uk>
Fri, 1 Apr 2016 00:54:33 +0000 (01:54 +0100)
commit 1b92ee3d03af6643df395300ba7748f19ecdb0c5 upstream.

The present unix_stream_read_generic contains various code sequences of
the form

err = -EDISASTER;
if (<test>)
goto out;

This has the unfortunate side effect of possibly causing the error code
to bleed through to the final

out:
return copied ? : err;

and then to be wrongly returned if no data was copied because the caller
didn't supply a data buffer, as demonstrated by the program available at

http://pad.lv/1540731

Change it such that err is only set if an error condition was detected.

Fixes: 3822b5c2fc62 ("af_unix: Revert 'lock_interruptible' in stream receive code")
Reported-by: Joseph Salisbury <joseph.salisbury@canonical.com>
Signed-off-by: Rainer Weikusat <rweikusat@mobileactivedefense.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
[bwh: Backported to 3.2: adjust context]
Signed-off-by: Ben Hutchings <ben@decadent.org.uk>
net/unix/af_unix.c

index 390e079..54afcaa 100644 (file)
@@ -2059,13 +2059,15 @@ static int unix_stream_recvmsg(struct kiocb *iocb, struct socket *sock,
        int err = 0;
        long timeo;
 
        int err = 0;
        long timeo;
 
-       err = -EINVAL;
-       if (sk->sk_state != TCP_ESTABLISHED)
+       if (unlikely(sk->sk_state != TCP_ESTABLISHED)) {
+               err = -EINVAL;
                goto out;
                goto out;
+       }
 
 
-       err = -EOPNOTSUPP;
-       if (flags&MSG_OOB)
+       if (unlikely(flags & MSG_OOB)) {
+               err = -EOPNOTSUPP;
                goto out;
                goto out;
+       }
 
        target = sock_rcvlowat(sk, flags&MSG_WAITALL, size);
        timeo = sock_rcvtimeo(sk, noblock);
 
        target = sock_rcvlowat(sk, flags&MSG_WAITALL, size);
        timeo = sock_rcvtimeo(sk, noblock);
@@ -2107,9 +2109,11 @@ static int unix_stream_recvmsg(struct kiocb *iocb, struct socket *sock,
                                goto unlock;
 
                        unix_state_unlock(sk);
                                goto unlock;
 
                        unix_state_unlock(sk);
-                       err = -EAGAIN;
-                       if (!timeo)
+                       if (!timeo) {
+                               err = -EAGAIN;
                                break;
                                break;
+                       }
+
                        mutex_unlock(&u->readlock);
 
                        timeo = unix_stream_data_wait(sk, timeo);
                        mutex_unlock(&u->readlock);
 
                        timeo = unix_stream_data_wait(sk, timeo);