diff options
Diffstat (limited to 'net/unix')
-rw-r--r-- | net/unix/af_unix.c | 37 | ||||
-rw-r--r-- | net/unix/garbage.c | 9 |
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 | |||
1346 | static int unix_attach_fds(struct scm_cookie *scm, struct sk_buff *skb) | 1348 | static 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 | ||
1364 | static int unix_scm_to_skb(struct scm_cookie *scm, struct sk_buff *skb, bool send_fds) | 1382 | 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, | |||
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); | |||
96 | unsigned int unix_tot_inflight; | 96 | unsigned int unix_tot_inflight; |
97 | 97 | ||
98 | 98 | ||
99 | static struct sock *unix_get_socket(struct file *filp) | 99 | struct 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 | ||
261 | static bool gc_in_progress = false; | 261 | static bool gc_in_progress = false; |
262 | #define UNIX_INFLIGHT_TRIGGER_GC 16000 | ||
262 | 263 | ||
263 | void wait_for_unix_gc(void) | 264 | void 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 | ||