aboutsummaryrefslogtreecommitdiffstats
path: root/net/unix
diff options
context:
space:
mode:
Diffstat (limited to 'net/unix')
-rw-r--r--net/unix/af_unix.c37
-rw-r--r--net/unix/garbage.c9
2 files changed, 40 insertions, 6 deletions
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
index 0b39b2451ea5..b4cfe207a6ac 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)
1343 sock_wfree(skb); 1343 sock_wfree(skb);
1344} 1344}
1345 1345
1346#define MAX_RECURSION_LEVEL 4
1347
1346static int unix_attach_fds(struct scm_cookie *scm, struct sk_buff *skb) 1348static int unix_attach_fds(struct scm_cookie *scm, struct sk_buff *skb)
1347{ 1349{
1348 int i; 1350 int i;
1351 unsigned char max_level = 0;
1352 int unix_sock_count = 0;
1353
1354 for (i = scm->fp->count - 1; i >= 0; i--) {
1355 struct sock *sk = unix_get_socket(scm->fp->fp[i]);
1356
1357 if (sk) {
1358 unix_sock_count++;
1359 max_level = max(max_level,
1360 unix_sk(sk)->recursion_level);
1361 }
1362 }
1363 if (unlikely(max_level > MAX_RECURSION_LEVEL))
1364 return -ETOOMANYREFS;
1349 1365
1350 /* 1366 /*
1351 * Need to duplicate file references for the sake of garbage 1367 * 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)
1356 if (!UNIXCB(skb).fp) 1372 if (!UNIXCB(skb).fp)
1357 return -ENOMEM; 1373 return -ENOMEM;
1358 1374
1359 for (i = scm->fp->count-1; i >= 0; i--) 1375 if (unix_sock_count) {
1360 unix_inflight(scm->fp->fp[i]); 1376 for (i = scm->fp->count - 1; i >= 0; i--)
1361 return 0; 1377 unix_inflight(scm->fp->fp[i]);
1378 }
1379 return max_level;
1362} 1380}
1363 1381
1364static int unix_scm_to_skb(struct scm_cookie *scm, struct sk_buff *skb, bool send_fds) 1382static 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,
1393 struct sk_buff *skb; 1411 struct sk_buff *skb;
1394 long timeo; 1412 long timeo;
1395 struct scm_cookie tmp_scm; 1413 struct scm_cookie tmp_scm;
1414 int max_level;
1396 1415
1397 if (NULL == siocb->scm) 1416 if (NULL == siocb->scm)
1398 siocb->scm = &tmp_scm; 1417 siocb->scm = &tmp_scm;
@@ -1431,8 +1450,9 @@ static int unix_dgram_sendmsg(struct kiocb *kiocb, struct socket *sock,
1431 goto out; 1450 goto out;
1432 1451
1433 err = unix_scm_to_skb(siocb->scm, skb, true); 1452 err = unix_scm_to_skb(siocb->scm, skb, true);
1434 if (err) 1453 if (err < 0)
1435 goto out_free; 1454 goto out_free;
1455 max_level = err + 1;
1436 unix_get_secdata(siocb->scm, skb); 1456 unix_get_secdata(siocb->scm, skb);
1437 1457
1438 skb_reset_transport_header(skb); 1458 skb_reset_transport_header(skb);
@@ -1512,6 +1532,8 @@ restart:
1512 } 1532 }
1513 1533
1514 skb_queue_tail(&other->sk_receive_queue, skb); 1534 skb_queue_tail(&other->sk_receive_queue, skb);
1535 if (max_level > unix_sk(other)->recursion_level)
1536 unix_sk(other)->recursion_level = max_level;
1515 unix_state_unlock(other); 1537 unix_state_unlock(other);
1516 other->sk_data_ready(other, len); 1538 other->sk_data_ready(other, len);
1517 sock_put(other); 1539 sock_put(other);
@@ -1542,6 +1564,7 @@ static int unix_stream_sendmsg(struct kiocb *kiocb, struct socket *sock,
1542 int sent = 0; 1564 int sent = 0;
1543 struct scm_cookie tmp_scm; 1565 struct scm_cookie tmp_scm;
1544 bool fds_sent = false; 1566 bool fds_sent = false;
1567 int max_level;
1545 1568
1546 if (NULL == siocb->scm) 1569 if (NULL == siocb->scm)
1547 siocb->scm = &tmp_scm; 1570 siocb->scm = &tmp_scm;
@@ -1605,10 +1628,11 @@ static int unix_stream_sendmsg(struct kiocb *kiocb, struct socket *sock,
1605 1628
1606 /* Only send the fds in the first buffer */ 1629 /* Only send the fds in the first buffer */
1607 err = unix_scm_to_skb(siocb->scm, skb, !fds_sent); 1630 err = unix_scm_to_skb(siocb->scm, skb, !fds_sent);
1608 if (err) { 1631 if (err < 0) {
1609 kfree_skb(skb); 1632 kfree_skb(skb);
1610 goto out_err; 1633 goto out_err;
1611 } 1634 }
1635 max_level = err + 1;
1612 fds_sent = true; 1636 fds_sent = true;
1613 1637
1614 err = memcpy_fromiovec(skb_put(skb, size), msg->msg_iov, size); 1638 err = memcpy_fromiovec(skb_put(skb, size), msg->msg_iov, size);
@@ -1624,6 +1648,8 @@ static int unix_stream_sendmsg(struct kiocb *kiocb, struct socket *sock,
1624 goto pipe_err_free; 1648 goto pipe_err_free;
1625 1649
1626 skb_queue_tail(&other->sk_receive_queue, skb); 1650 skb_queue_tail(&other->sk_receive_queue, skb);
1651 if (max_level > unix_sk(other)->recursion_level)
1652 unix_sk(other)->recursion_level = max_level;
1627 unix_state_unlock(other); 1653 unix_state_unlock(other);
1628 other->sk_data_ready(other, size); 1654 other->sk_data_ready(other, size);
1629 sent += size; 1655 sent += size;
@@ -1840,6 +1866,7 @@ static int unix_stream_recvmsg(struct kiocb *iocb, struct socket *sock,
1840 unix_state_lock(sk); 1866 unix_state_lock(sk);
1841 skb = skb_dequeue(&sk->sk_receive_queue); 1867 skb = skb_dequeue(&sk->sk_receive_queue);
1842 if (skb == NULL) { 1868 if (skb == NULL) {
1869 unix_sk(sk)->recursion_level = 0;
1843 if (copied >= target) 1870 if (copied >= target)
1844 goto unlock; 1871 goto unlock;
1845 1872
diff --git a/net/unix/garbage.c b/net/unix/garbage.c
index c8df6fda0b1f..f89f83bf828e 100644
--- a/net/unix/garbage.c
+++ b/net/unix/garbage.c
@@ -96,7 +96,7 @@ static DECLARE_WAIT_QUEUE_HEAD(unix_gc_wait);
96unsigned int unix_tot_inflight; 96unsigned int unix_tot_inflight;
97 97
98 98
99static struct sock *unix_get_socket(struct file *filp) 99struct sock *unix_get_socket(struct file *filp)
100{ 100{
101 struct sock *u_sock = NULL; 101 struct sock *u_sock = NULL;
102 struct inode *inode = filp->f_path.dentry->d_inode; 102 struct inode *inode = filp->f_path.dentry->d_inode;
@@ -259,9 +259,16 @@ static void inc_inflight_move_tail(struct unix_sock *u)
259} 259}
260 260
261static bool gc_in_progress = false; 261static bool gc_in_progress = false;
262#define UNIX_INFLIGHT_TRIGGER_GC 16000
262 263
263void wait_for_unix_gc(void) 264void wait_for_unix_gc(void)
264{ 265{
266 /*
267 * If number of inflight sockets is insane,
268 * force a garbage collect right now.
269 */
270 if (unix_tot_inflight > UNIX_INFLIGHT_TRIGGER_GC && !gc_in_progress)
271 unix_gc();
265 wait_event(unix_gc_wait, gc_in_progress == false); 272 wait_event(unix_gc_wait, gc_in_progress == false);
266} 273}
267 274