diff options
Diffstat (limited to 'net/unix/af_unix.c')
| -rw-r--r-- | net/unix/af_unix.c | 37 | 
1 files changed, 32 insertions, 5 deletions
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index 3c95304a0817..2268e6798124 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -1343,9 +1343,25 @@ static void unix_destruct_scm(struct sk_buff *skb)  	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 @@ -1356,9 +1372,11 @@ static int unix_attach_fds(struct scm_cookie *scm, struct sk_buff *skb)  	if (!UNIXCB(skb).fp)  		return -ENOMEM; -	for (i = scm->fp->count-1; i >= 0; i--) -		unix_inflight(scm->fp->fp[i]); -	return 0; +	if (unix_sock_count) { +		for (i = scm->fp->count - 1; i >= 0; i--) +			unix_inflight(scm->fp->fp[i]); +	} +	return max_level;  }  static int unix_scm_to_skb(struct scm_cookie *scm, struct sk_buff *skb, bool send_fds) @@ -1393,6 +1411,7 @@ static int unix_dgram_sendmsg(struct kiocb *kiocb, struct socket *sock,  	struct sk_buff *skb;  	long timeo;  	struct scm_cookie tmp_scm; +	int max_level;  	if (NULL == siocb->scm)  		siocb->scm = &tmp_scm; @@ -1431,8 +1450,9 @@ static int unix_dgram_sendmsg(struct kiocb *kiocb, struct socket *sock,  		goto out;  	err = unix_scm_to_skb(siocb->scm, skb, true); -	if (err) +	if (err < 0)  		goto out_free; +	max_level = err + 1;  	unix_get_secdata(siocb->scm, skb);  	skb_reset_transport_header(skb); @@ -1514,6 +1534,8 @@ restart:  	if (sock_flag(other, SOCK_RCVTSTAMP))  		__net_timestamp(skb);  	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); @@ -1544,6 +1566,7 @@ static int unix_stream_sendmsg(struct kiocb *kiocb, struct socket *sock,  	int sent = 0;  	struct scm_cookie tmp_scm;  	bool fds_sent = false; +	int max_level;  	if (NULL == siocb->scm)  		siocb->scm = &tmp_scm; @@ -1607,10 +1630,11 @@ static int unix_stream_sendmsg(struct kiocb *kiocb, struct socket *sock,  		/* Only send the fds in the first buffer */  		err = unix_scm_to_skb(siocb->scm, skb, !fds_sent); -		if (err) { +		if (err < 0) {  			kfree_skb(skb);  			goto out_err;  		} +		max_level = err + 1;  		fds_sent = true;  		err = memcpy_fromiovec(skb_put(skb, size), msg->msg_iov, size); @@ -1626,6 +1650,8 @@ static int unix_stream_sendmsg(struct kiocb *kiocb, struct socket *sock,  			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; @@ -1845,6 +1871,7 @@ static int unix_stream_recvmsg(struct kiocb *iocb, struct socket *sock,  		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;  | 
