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