aboutsummaryrefslogtreecommitdiffstats
path: root/net/unix
diff options
context:
space:
mode:
authorMiklos Szeredi <mszeredi@suse.cz>2009-09-11 14:31:45 -0400
committerDavid S. Miller <davem@davemloft.net>2009-09-11 14:31:45 -0400
commit8ba69ba6a324b13e1190fc31e41954d190fd4f1d (patch)
tree3794f633c57ca8257242abccc891c8be7d58cdf0 /net/unix
parent9a0da0d19c573e01aded6ac17747d2efc5b1115f (diff)
net: unix: fix sending fds in multiple buffers
Kalle Olavi Niemitalo reported that: "..., when one process calls sendmsg once to send 43804 bytes of data and one file descriptor, and another process then calls recvmsg three times to receive the 16032+16032+11740 bytes, each of those recvmsg calls returns the file descriptor in the ancillary data. I confirmed this with strace. The behaviour differs from Linux 2.6.26, where reportedly only one of those recvmsg calls (I think the first one) returned the file descriptor." This bug was introduced by a patch from me titled "net: unix: fix inflight counting bug in garbage collector", commit 6209344f5. And the reason is, quoting Kalle: "Before your patch, unix_attach_fds() would set scm->fp = NULL, so that if the loop in unix_stream_sendmsg() ran multiple iterations, it could not call unix_attach_fds() again. But now, unix_attach_fds() leaves scm->fp unchanged, and I think this causes it to be called multiple times and duplicate the same file descriptors to each struct sk_buff." Fix this by introducing a flag that is cleared at the start and set when the fds attached to the first buffer. The resulting code should work equivalently to the one on 2.6.26. Reported-by: Kalle Olavi Niemitalo <kon@iki.fi> Signed-off-by: Miklos Szeredi <mszeredi@suse.cz> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/unix')
-rw-r--r--net/unix/af_unix.c5
1 files changed, 4 insertions, 1 deletions
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
index fc3ebb906911..51ab497115eb 100644
--- a/net/unix/af_unix.c
+++ b/net/unix/af_unix.c
@@ -1501,6 +1501,7 @@ static int unix_stream_sendmsg(struct kiocb *kiocb, struct socket *sock,
1501 struct sk_buff *skb; 1501 struct sk_buff *skb;
1502 int sent = 0; 1502 int sent = 0;
1503 struct scm_cookie tmp_scm; 1503 struct scm_cookie tmp_scm;
1504 bool fds_sent = false;
1504 1505
1505 if (NULL == siocb->scm) 1506 if (NULL == siocb->scm)
1506 siocb->scm = &tmp_scm; 1507 siocb->scm = &tmp_scm;
@@ -1562,12 +1563,14 @@ static int unix_stream_sendmsg(struct kiocb *kiocb, struct socket *sock,
1562 size = min_t(int, size, skb_tailroom(skb)); 1563 size = min_t(int, size, skb_tailroom(skb));
1563 1564
1564 memcpy(UNIXCREDS(skb), &siocb->scm->creds, sizeof(struct ucred)); 1565 memcpy(UNIXCREDS(skb), &siocb->scm->creds, sizeof(struct ucred));
1565 if (siocb->scm->fp) { 1566 /* Only send the fds in the first buffer */
1567 if (siocb->scm->fp && !fds_sent) {
1566 err = unix_attach_fds(siocb->scm, skb); 1568 err = unix_attach_fds(siocb->scm, skb);
1567 if (err) { 1569 if (err) {
1568 kfree_skb(skb); 1570 kfree_skb(skb);
1569 goto out_err; 1571 goto out_err;
1570 } 1572 }
1573 fds_sent = true;
1571 } 1574 }
1572 1575
1573 err = memcpy_fromiovec(skb_put(skb, size), msg->msg_iov, size); 1576 err = memcpy_fromiovec(skb_put(skb, size), msg->msg_iov, size);