Merge branch 'tracing-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel...
[cascardo/linux.git] / net / unix / af_unix.c
index 4d3c607..b152e2b 100644 (file)
@@ -467,8 +467,7 @@ static int unix_listen(struct socket *sock, int backlog)
        sk->sk_state            = TCP_LISTEN;
        /* set credentials so connect can copy them */
        sk->sk_peercred.pid     = task_tgid_vnr(current);
-       sk->sk_peercred.uid     = current->euid;
-       sk->sk_peercred.gid     = current->egid;
+       current_euid_egid(&sk->sk_peercred.uid, &sk->sk_peercred.gid);
        err = 0;
 
 out_unlock:
@@ -1126,8 +1125,7 @@ restart:
        newsk->sk_state         = TCP_ESTABLISHED;
        newsk->sk_type          = sk->sk_type;
        newsk->sk_peercred.pid  = task_tgid_vnr(current);
-       newsk->sk_peercred.uid  = current->euid;
-       newsk->sk_peercred.gid  = current->egid;
+       current_euid_egid(&newsk->sk_peercred.uid, &newsk->sk_peercred.gid);
        newu = unix_sk(newsk);
        newsk->sk_sleep         = &newu->peer_wait;
        otheru = unix_sk(other);
@@ -1187,8 +1185,9 @@ static int unix_socketpair(struct socket *socka, struct socket *sockb)
        unix_peer(ska)=skb;
        unix_peer(skb)=ska;
        ska->sk_peercred.pid = skb->sk_peercred.pid = task_tgid_vnr(current);
-       ska->sk_peercred.uid = skb->sk_peercred.uid = current->euid;
-       ska->sk_peercred.gid = skb->sk_peercred.gid = current->egid;
+       current_euid_egid(&skb->sk_peercred.uid, &skb->sk_peercred.gid);
+       ska->sk_peercred.uid = skb->sk_peercred.uid;
+       ska->sk_peercred.gid = skb->sk_peercred.gid;
 
        if (ska->sk_type != SOCK_DGRAM) {
                ska->sk_state = TCP_ESTABLISHED;
@@ -1302,14 +1301,23 @@ static void unix_destruct_fds(struct sk_buff *skb)
        sock_wfree(skb);
 }
 
-static void unix_attach_fds(struct scm_cookie *scm, struct sk_buff *skb)
+static int unix_attach_fds(struct scm_cookie *scm, struct sk_buff *skb)
 {
        int i;
+
+       /*
+        * Need to duplicate file references for the sake of garbage
+        * collection.  Otherwise a socket in the fps might become a
+        * candidate for GC while the skb is not yet queued.
+        */
+       UNIXCB(skb).fp = scm_fp_dup(scm->fp);
+       if (!UNIXCB(skb).fp)
+               return -ENOMEM;
+
        for (i=scm->fp->count-1; i>=0; i--)
                unix_inflight(scm->fp->fp[i]);
-       UNIXCB(skb).fp = scm->fp;
        skb->destructor = unix_destruct_fds;
-       scm->fp = NULL;
+       return 0;
 }
 
 /*
@@ -1334,6 +1342,7 @@ static int unix_dgram_sendmsg(struct kiocb *kiocb, struct socket *sock,
 
        if (NULL == siocb->scm)
                siocb->scm = &tmp_scm;
+       wait_for_unix_gc();
        err = scm_send(sock, msg, siocb->scm);
        if (err < 0)
                return err;
@@ -1368,8 +1377,11 @@ static int unix_dgram_sendmsg(struct kiocb *kiocb, struct socket *sock,
                goto out;
 
        memcpy(UNIXCREDS(skb), &siocb->scm->creds, sizeof(struct ucred));
-       if (siocb->scm->fp)
-               unix_attach_fds(siocb->scm, skb);
+       if (siocb->scm->fp) {
+               err = unix_attach_fds(siocb->scm, skb);
+               if (err)
+                       goto out_free;
+       }
        unix_get_secdata(siocb->scm, skb);
 
        skb_reset_transport_header(skb);
@@ -1481,6 +1493,7 @@ static int unix_stream_sendmsg(struct kiocb *kiocb, struct socket *sock,
 
        if (NULL == siocb->scm)
                siocb->scm = &tmp_scm;
+       wait_for_unix_gc();
        err = scm_send(sock, msg, siocb->scm);
        if (err < 0)
                return err;
@@ -1538,8 +1551,13 @@ static int unix_stream_sendmsg(struct kiocb *kiocb, struct socket *sock,
                size = min_t(int, size, skb_tailroom(skb));
 
                memcpy(UNIXCREDS(skb), &siocb->scm->creds, sizeof(struct ucred));
-               if (siocb->scm->fp)
-                       unix_attach_fds(siocb->scm, skb);
+               if (siocb->scm->fp) {
+                       err = unix_attach_fds(siocb->scm, skb);
+                       if (err) {
+                               kfree_skb(skb);
+                               goto out_err;
+                       }
+               }
 
                if ((err = memcpy_fromiovec(skb_put(skb,size), msg->msg_iov, size)) != 0) {
                        kfree_skb(skb);