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;
}