--- 065dc6677e90e606205112d0aa846b549c4e2284 +++ db8d51a974b4fff10ab964aee0692ed1f291c418 @@ -503,6 +503,8 @@ static int unix_dgram_connect(struct soc int, int); static int unix_seqpacket_sendmsg(struct kiocb *, struct socket *, struct msghdr *, size_t); +static int unix_seqpacket_recvmsg(struct kiocb *, struct socket *, + struct msghdr *, size_t, int); static const struct proto_ops unix_stream_ops = { .family = PF_UNIX, @@ -562,7 +564,7 @@ static const struct proto_ops unix_seqpa .setsockopt = sock_no_setsockopt, .getsockopt = sock_no_getsockopt, .sendmsg = unix_seqpacket_sendmsg, - .recvmsg = unix_dgram_recvmsg, + .recvmsg = unix_seqpacket_recvmsg, .mmap = sock_no_mmap, .sendpage = sock_no_sendpage, }; @@ -1323,9 +1325,25 @@ static void unix_destruct_fds(struct sk_ sock_wfree(skb); } +#define MAX_RECURSION_LEVEL 4 + static int unix_attach_fds(struct scm_cookie *scm, struct sk_buff *skb) { int i; + unsigned char max_level = 0; + int unix_sock_count = 0; + + for (i = scm->fp->count - 1; i >= 0; i--) { + struct sock *sk = unix_get_socket(scm->fp->fp[i]); + + if (sk) { + unix_sock_count++; + max_level = max(max_level, + unix_sk(sk)->recursion_level); + } + } + if (unlikely(max_level > MAX_RECURSION_LEVEL)) + return -ETOOMANYREFS; /* * Need to duplicate file references for the sake of garbage @@ -1336,10 +1354,12 @@ static int unix_attach_fds(struct scm_co if (!UNIXCB(skb).fp) return -ENOMEM; - for (i = scm->fp->count-1; i >= 0; i--) - unix_inflight(scm->fp->fp[i]); + if (unix_sock_count) { + for (i = scm->fp->count - 1; i >= 0; i--) + unix_inflight(scm->fp->fp[i]); + } skb->destructor = unix_destruct_fds; - return 0; + return max_level; } /* @@ -1361,6 +1381,7 @@ static int unix_dgram_sendmsg(struct kio struct sk_buff *skb; long timeo; struct scm_cookie tmp_scm; + int max_level = 0; if (NULL == siocb->scm) siocb->scm = &tmp_scm; @@ -1401,8 +1422,9 @@ static int unix_dgram_sendmsg(struct kio memcpy(UNIXCREDS(skb), &siocb->scm->creds, sizeof(struct ucred)); if (siocb->scm->fp) { err = unix_attach_fds(siocb->scm, skb); - if (err) + if (err < 0) goto out_free; + max_level = err + 1; } unix_get_secdata(siocb->scm, skb); @@ -1483,6 +1505,8 @@ restart: } skb_queue_tail(&other->sk_receive_queue, skb); + if (max_level > unix_sk(other)->recursion_level) + unix_sk(other)->recursion_level = max_level; unix_state_unlock(other); other->sk_data_ready(other, len); sock_put(other); @@ -1513,6 +1537,7 @@ static int unix_stream_sendmsg(struct ki int sent = 0; struct scm_cookie tmp_scm; bool fds_sent = false; + int max_level = 0; if (NULL == siocb->scm) siocb->scm = &tmp_scm; @@ -1577,10 +1602,11 @@ static int unix_stream_sendmsg(struct ki /* Only send the fds in the first buffer */ if (siocb->scm->fp && !fds_sent) { err = unix_attach_fds(siocb->scm, skb); - if (err) { + if (err < 0) { kfree_skb(skb); goto out_err; } + max_level = err + 1; fds_sent = true; } @@ -1597,6 +1623,8 @@ static int unix_stream_sendmsg(struct ki goto pipe_err_free; skb_queue_tail(&other->sk_receive_queue, skb); + if (max_level > unix_sk(other)->recursion_level) + unix_sk(other)->recursion_level = max_level; unix_state_unlock(other); other->sk_data_ready(other, size); sent += size; @@ -1639,6 +1667,18 @@ static int unix_seqpacket_sendmsg(struct return unix_dgram_sendmsg(kiocb, sock, msg, len); } +static int unix_seqpacket_recvmsg(struct kiocb *iocb, struct socket *sock, + struct msghdr *msg, size_t size, + int flags) +{ + struct sock *sk = sock->sk; + + if (sk->sk_state != TCP_ESTABLISHED) + return -ENOTCONN; + + return unix_dgram_recvmsg(iocb, sock, msg, size, flags); +} + static void unix_copy_addr(struct msghdr *msg, struct sock *sk) { struct unix_sock *u = unix_sk(sk); @@ -1813,6 +1853,7 @@ static int unix_stream_recvmsg(struct ki unix_state_lock(sk); skb = skb_dequeue(&sk->sk_receive_queue); if (skb == NULL) { + unix_sk(sk)->recursion_level = 0; if (copied >= target) goto unlock;