cred_to_ucred(pid, cred, &scm->creds);
 }
 
+static __inline__ void scm_set_cred_noref(struct scm_cookie *scm,
+                                   struct pid *pid, const struct cred *cred)
+{
+       scm->pid  = pid;
+       scm->cred = cred;
+       cred_to_ucred(pid, cred, &scm->creds);
+}
+
 static __inline__ void scm_destroy_cred(struct scm_cookie *scm)
 {
        put_pid(scm->pid);
                __scm_destroy(scm);
 }
 
+static __inline__ void scm_release(struct scm_cookie *scm)
+{
+       /* keep ref on pid and cred */
+       scm->pid = NULL;
+       scm->cred = NULL;
+       if (scm->fp)
+               __scm_destroy(scm);
+}
+
 static __inline__ int scm_send(struct socket *sock, struct msghdr *msg,
                               struct scm_cookie *scm)
 {
        if (!msg->msg_control) {
                if (test_bit(SOCK_PASSCRED, &sock->flags) || scm->fp)
                        msg->msg_flags |= MSG_CTRUNC;
-               scm_destroy(scm);
+               if (scm && scm->fp)
+                       __scm_destroy(scm);
                return;
        }
 
        if (test_bit(SOCK_PASSCRED, &sock->flags))
                put_cmsg(msg, SOL_SOCKET, SCM_CREDENTIALS, sizeof(scm->creds), &scm->creds);
 
-       scm_destroy_cred(scm);
-
        scm_passec(sock, msg, scm);
 
        if (!scm->fp)
 
        return max_level;
 }
 
-static int unix_scm_to_skb(struct scm_cookie *scm, struct sk_buff *skb, bool send_fds)
+static int unix_scm_to_skb(struct scm_cookie *scm, struct sk_buff *skb,
+                          bool send_fds, bool ref)
 {
        int err = 0;
-       UNIXCB(skb).pid  = get_pid(scm->pid);
-       UNIXCB(skb).cred = get_cred(scm->cred);
+       if (ref) {
+               UNIXCB(skb).pid  = get_pid(scm->pid);
+               UNIXCB(skb).cred = get_cred(scm->cred);
+       } else {
+               UNIXCB(skb).pid  = scm->pid;
+               UNIXCB(skb).cred = scm->cred;
+       }
        UNIXCB(skb).fp = NULL;
        if (scm->fp && send_fds)
                err = unix_attach_fds(scm, skb);
        int namelen = 0; /* fake GCC */
        int err;
        unsigned hash;
-       struct sk_buff *skb;
+       struct sk_buff *skb = NULL;
        long timeo;
        struct scm_cookie tmp_scm;
        int max_level;
        if (skb == NULL)
                goto out;
 
-       err = unix_scm_to_skb(siocb->scm, skb, true);
+       err = unix_scm_to_skb(siocb->scm, skb, true, false);
        if (err < 0)
                goto out_free;
        max_level = err + 1;
        unix_state_unlock(other);
        other->sk_data_ready(other, len);
        sock_put(other);
-       scm_destroy(siocb->scm);
+       scm_release(siocb->scm);
        return len;
 
 out_unlock:
 out:
        if (other)
                sock_put(other);
-       scm_destroy(siocb->scm);
+       if (skb == NULL)
+               scm_destroy(siocb->scm);
        return err;
 }
 
        struct sock *sk = sock->sk;
        struct sock *other = NULL;
        int err, size;
-       struct sk_buff *skb;
+       struct sk_buff *skb = NULL;
        int sent = 0;
        struct scm_cookie tmp_scm;
        bool fds_sent = false;
                size = min_t(int, size, skb_tailroom(skb));
 
 
-               /* Only send the fds in the first buffer */
-               err = unix_scm_to_skb(siocb->scm, skb, !fds_sent);
+               /* Only send the fds and no ref to pid in the first buffer */
+               err = unix_scm_to_skb(siocb->scm, skb, !fds_sent, fds_sent);
                if (err < 0) {
                        kfree_skb(skb);
-                       goto out_err;
+                       goto out;
                }
                max_level = err + 1;
                fds_sent = true;
                err = memcpy_fromiovec(skb_put(skb, size), msg->msg_iov, size);
                if (err) {
                        kfree_skb(skb);
-                       goto out_err;
+                       goto out;
                }
 
                unix_state_lock(other);
                sent += size;
        }
 
-       scm_destroy(siocb->scm);
+       if (skb)
+               scm_release(siocb->scm);
+       else
+               scm_destroy(siocb->scm);
        siocb->scm = NULL;
 
        return sent;
                send_sig(SIGPIPE, current, 0);
        err = -EPIPE;
 out_err:
-       scm_destroy(siocb->scm);
+       if (skb == NULL)
+               scm_destroy(siocb->scm);
+out:
        siocb->scm = NULL;
        return sent ? : err;
 }
                siocb->scm = &tmp_scm;
                memset(&tmp_scm, 0, sizeof(tmp_scm));
        }
-       scm_set_cred(siocb->scm, UNIXCB(skb).pid, UNIXCB(skb).cred);
+       scm_set_cred_noref(siocb->scm, UNIXCB(skb).pid, UNIXCB(skb).cred);
        unix_set_secdata(siocb->scm, skb);
 
        if (!(flags & MSG_PEEK)) {
                        }
                } else {
                        /* Copy credentials */
-                       scm_set_cred(siocb->scm, UNIXCB(skb).pid, UNIXCB(skb).cred);
+                       scm_set_cred_noref(siocb->scm, UNIXCB(skb).pid,
+                                          UNIXCB(skb).cred);
                        check_creds = 1;
                }