net: unix: fix sending fds in multiple buffers
[safe/jmp/linux-2.6] / net / unix / af_unix.c
index 58db2a2..51ab497 100644 (file)
@@ -216,7 +216,7 @@ static int unix_mkname(struct sockaddr_un *sunaddr, int len, unsigned *hashp)
                return len;
        }
 
-       *hashp = unix_hash_fold(csum_partial((char *)sunaddr, len, 0));
+       *hashp = unix_hash_fold(csum_partial(sunaddr, len, 0));
        return len;
 }
 
@@ -315,7 +315,7 @@ static void unix_write_space(struct sock *sk)
 {
        read_lock(&sk->sk_callback_lock);
        if (unix_writable(sk)) {
-               if (sk->sk_sleep && waitqueue_active(sk->sk_sleep))
+               if (sk_has_sleeper(sk))
                        wake_up_interruptible_sync(sk->sk_sleep);
                sk_wake_async(sk, SOCK_WAKE_SPACE, POLL_OUT);
        }
@@ -353,7 +353,7 @@ static void unix_sock_destructor(struct sock *sk)
        WARN_ON(!sk_unhashed(sk));
        WARN_ON(sk->sk_socket);
        if (!sock_flag(sk, SOCK_DEAD)) {
-               printk(KERN_DEBUG "Attempt to release alive unix socket: %p\n", sk);
+               printk(KERN_INFO "Attempt to release alive unix socket: %p\n", sk);
                return;
        }
 
@@ -361,6 +361,9 @@ static void unix_sock_destructor(struct sock *sk)
                unix_release_addr(u->addr);
 
        atomic_dec(&unix_nr_socks);
+       local_bh_disable();
+       sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1);
+       local_bh_enable();
 #ifdef UNIX_REFCNT_DEBUG
        printk(KERN_DEBUG "UNIX %p is destroyed, %d are still alive.\n", sk,
                atomic_read(&unix_nr_socks));
@@ -467,8 +470,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:
@@ -566,9 +568,9 @@ static const struct proto_ops unix_seqpacket_ops = {
 };
 
 static struct proto unix_proto = {
-       .name     = "UNIX",
-       .owner    = THIS_MODULE,
-       .obj_size = sizeof(struct unix_sock),
+       .name                   = "UNIX",
+       .owner                  = THIS_MODULE,
+       .obj_size               = sizeof(struct unix_sock),
 };
 
 /*
@@ -611,6 +613,11 @@ static struct sock *unix_create1(struct net *net, struct socket *sock)
 out:
        if (sk == NULL)
                atomic_dec(&unix_nr_socks);
+       else {
+               local_bh_disable();
+               sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1);
+               local_bh_enable();
+       }
        return sk;
 }
 
@@ -681,7 +688,7 @@ static int unix_autobind(struct socket *sock)
 
 retry:
        addr->len = sprintf(addr->name->sun_path+1, "%05x", ordernum) + 1 + sizeof(short);
-       addr->hash = unix_hash_fold(csum_partial((void *)addr->name, addr->len, 0));
+       addr->hash = unix_hash_fold(csum_partial(addr->name, addr->len, 0));
 
        spin_lock(&unix_table_lock);
        ordernum = (ordernum+1)&0xFFFFF;
@@ -825,11 +832,15 @@ static int unix_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
                 * All right, let's create it.
                 */
                mode = S_IFSOCK |
-                      (SOCK_INODE(sock)->i_mode & ~current->fs->umask);
+                      (SOCK_INODE(sock)->i_mode & ~current_umask());
                err = mnt_want_write(nd.path.mnt);
                if (err)
                        goto out_mknod_dput;
+               err = security_path_mknod(&nd.path, dentry, mode, 0);
+               if (err)
+                       goto out_mknod_drop_write;
                err = vfs_mknod(nd.path.dentry->d_inode, dentry, mode, 0);
+out_mknod_drop_write:
                mnt_drop_write(nd.path.mnt);
                if (err)
                        goto out_mknod_dput;
@@ -1126,8 +1137,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);
@@ -1168,8 +1178,7 @@ out_unlock:
                unix_state_unlock(other);
 
 out:
-       if (skb)
-               kfree_skb(skb);
+       kfree_skb(skb);
        if (newsk)
                unix_release_sock(newsk, 0);
        if (other)
@@ -1187,8 +1196,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;
@@ -1343,6 +1353,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;
@@ -1490,9 +1501,11 @@ static int unix_stream_sendmsg(struct kiocb *kiocb, struct socket *sock,
        struct sk_buff *skb;
        int sent = 0;
        struct scm_cookie tmp_scm;
+       bool fds_sent = false;
 
        if (NULL == siocb->scm)
                siocb->scm = &tmp_scm;
+       wait_for_unix_gc();
        err = scm_send(sock, msg, siocb->scm);
        if (err < 0)
                return err;
@@ -1550,12 +1563,14 @@ 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) {
+               /* Only send the fds in the first buffer */
+               if (siocb->scm->fp && !fds_sent) {
                        err = unix_attach_fds(siocb->scm, skb);
                        if (err) {
                                kfree_skb(skb);
                                goto out_err;
                        }
+                       fds_sent = true;
                }
 
                err = memcpy_fromiovec(skb_put(skb, size), msg->msg_iov, size);
@@ -1934,7 +1949,7 @@ static int unix_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
 
        switch (cmd) {
        case SIOCOUTQ:
-               amount = atomic_read(&sk->sk_wmem_alloc);
+               amount = sk_wmem_alloc_get(sk);
                err = put_user(amount, (int __user *)arg);
                break;
        case SIOCINQ:
@@ -1973,7 +1988,7 @@ static unsigned int unix_poll(struct file *file, struct socket *sock, poll_table
        struct sock *sk = sock->sk;
        unsigned int mask;
 
-       poll_wait(file, sk->sk_sleep, wait);
+       sock_poll_wait(file, sk->sk_sleep, wait);
        mask = 0;
 
        /* exceptional events? */
@@ -2010,7 +2025,7 @@ static unsigned int unix_dgram_poll(struct file *file, struct socket *sock,
        struct sock *sk = sock->sk, *other;
        unsigned int mask, writable;
 
-       poll_wait(file, sk->sk_sleep, wait);
+       sock_poll_wait(file, sk->sk_sleep, wait);
        mask = 0;
 
        /* exceptional events? */
@@ -2041,7 +2056,7 @@ static unsigned int unix_dgram_poll(struct file *file, struct socket *sock,
                other = unix_peer_get(sk);
                if (other) {
                        if (unix_peer(other) != sk) {
-                               poll_wait(file, &unix_sk(other)->peer_wait,
+                               sock_poll_wait(file, &unix_sk(other)->peer_wait,
                                          wait);
                                if (unix_recvq_full(other))
                                        writable = 0;