aboutsummaryrefslogtreecommitdiffstats
path: root/net/unix/af_unix.c
diff options
context:
space:
mode:
authorMiklos Szeredi <mszeredi@suse.cz>2007-06-05 16:10:29 -0400
committerDavid S. Miller <davem@sunset.davemloft.net>2007-06-07 16:40:44 -0400
commit3c0d2f3780fc94746c4842e965bd2570e2119bb6 (patch)
tree993e21657b05a2e299e699006aa53a0c0a380e4c /net/unix/af_unix.c
parentc764c9ade6d9b710bad2b9c631ede9864333b98c (diff)
[AF_UNIX]: Fix stream recvmsg() race.
A recv() on an AF_UNIX, SOCK_STREAM socket can race with a send()+close() on the peer, causing recv() to return zero, even though the sent data should be received. This happens if the send() and the close() is performed between skb_dequeue() and checking sk->sk_shutdown in unix_stream_recvmsg(): process A skb_dequeue() returns NULL, there's no data in the socket queue process B new data is inserted onto the queue by unix_stream_sendmsg() process B sk->sk_shutdown is set to SHUTDOWN_MASK by unix_release_sock() process A sk->sk_shutdown is checked, unix_release_sock() returns zero I'm surprised nobody noticed this, it's not hard to trigger. Maybe it's just (un)luck with the timing. It's possible to work around this bug in userspace, by retrying the recv() once in case of a zero return value. Signed-off-by: Miklos Szeredi <mszeredi@suse.cz> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/unix/af_unix.c')
-rw-r--r--net/unix/af_unix.c13
1 files changed, 10 insertions, 3 deletions
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
index 87c794d8fa2d..d70fa30d4294 100644
--- a/net/unix/af_unix.c
+++ b/net/unix/af_unix.c
@@ -1744,20 +1744,23 @@ static int unix_stream_recvmsg(struct kiocb *iocb, struct socket *sock,
1744 int chunk; 1744 int chunk;
1745 struct sk_buff *skb; 1745 struct sk_buff *skb;
1746 1746
1747 unix_state_lock(sk);
1747 skb = skb_dequeue(&sk->sk_receive_queue); 1748 skb = skb_dequeue(&sk->sk_receive_queue);
1748 if (skb==NULL) 1749 if (skb==NULL)
1749 { 1750 {
1750 if (copied >= target) 1751 if (copied >= target)
1751 break; 1752 goto unlock;
1752 1753
1753 /* 1754 /*
1754 * POSIX 1003.1g mandates this order. 1755 * POSIX 1003.1g mandates this order.
1755 */ 1756 */
1756 1757
1757 if ((err = sock_error(sk)) != 0) 1758 if ((err = sock_error(sk)) != 0)
1758 break; 1759 goto unlock;
1759 if (sk->sk_shutdown & RCV_SHUTDOWN) 1760 if (sk->sk_shutdown & RCV_SHUTDOWN)
1760 break; 1761 goto unlock;
1762
1763 unix_state_unlock(sk);
1761 err = -EAGAIN; 1764 err = -EAGAIN;
1762 if (!timeo) 1765 if (!timeo)
1763 break; 1766 break;
@@ -1771,7 +1774,11 @@ static int unix_stream_recvmsg(struct kiocb *iocb, struct socket *sock,
1771 } 1774 }
1772 mutex_lock(&u->readlock); 1775 mutex_lock(&u->readlock);
1773 continue; 1776 continue;
1777 unlock:
1778 unix_state_unlock(sk);
1779 break;
1774 } 1780 }
1781 unix_state_unlock(sk);
1775 1782
1776 if (check_creds) { 1783 if (check_creds) {
1777 /* Never glue messages from different writers */ 1784 /* Never glue messages from different writers */