diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2010-11-29 17:36:33 -0500 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2010-11-29 17:36:33 -0500 |
| commit | a01af8e4a4ee1135598f157051959982418c38f8 (patch) | |
| tree | 76c78a7cbd02204afbe7404880dfbf312ebd51a5 /net/unix | |
| parent | a9735c81a43054a7e8cb8771c8e04c01fcacde10 (diff) | |
| parent | 25888e30319f8896fc656fc68643e6a078263060 (diff) | |
Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-2.6
* git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-2.6: (27 commits)
af_unix: limit recursion level
pch_gbe driver: The wrong of initializer entry
pch_gbe dreiver: chang author
ucc_geth: fix ucc halt problem in half duplex mode
inet: Fix __inet_inherit_port() to correctly increment bsockets and num_owners
ehea: Add some info messages and fix an issue
hso: fix disable_net
NET: wan/x25_asy, move lapb_unregister to x25_asy_close_tty
cxgb4vf: fix setting unicast/multicast addresses ...
net, ppp: Report correct error code if unit allocation failed
DECnet: don't leak uninitialized stack byte
au1000_eth: fix invalid address accessing the MAC enable register
dccp: fix error in updating the GAR
tcp: restrict net.ipv4.tcp_adv_win_scale (#20312)
netns: Don't leak others' openreq-s in proc
Net: ceph: Makefile: Remove unnessary code
vhost/net: fix rcu check usage
econet: fix CVE-2010-3848
econet: fix CVE-2010-3850
econet: disallow NULL remote addr for sendmsg(), fixes CVE-2010-3849
...
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 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) | |||
| 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); |
| @@ -1514,6 +1534,8 @@ restart: | |||
| 1514 | if (sock_flag(other, SOCK_RCVTSTAMP)) | 1534 | if (sock_flag(other, SOCK_RCVTSTAMP)) |
| 1515 | __net_timestamp(skb); | 1535 | __net_timestamp(skb); |
| 1516 | skb_queue_tail(&other->sk_receive_queue, skb); | 1536 | skb_queue_tail(&other->sk_receive_queue, skb); |
| 1537 | if (max_level > unix_sk(other)->recursion_level) | ||
| 1538 | unix_sk(other)->recursion_level = max_level; | ||
| 1517 | unix_state_unlock(other); | 1539 | unix_state_unlock(other); |
| 1518 | other->sk_data_ready(other, len); | 1540 | other->sk_data_ready(other, len); |
| 1519 | sock_put(other); | 1541 | sock_put(other); |
| @@ -1544,6 +1566,7 @@ static int unix_stream_sendmsg(struct kiocb *kiocb, struct socket *sock, | |||
| 1544 | int sent = 0; | 1566 | int sent = 0; |
| 1545 | struct scm_cookie tmp_scm; | 1567 | struct scm_cookie tmp_scm; |
| 1546 | bool fds_sent = false; | 1568 | bool fds_sent = false; |
| 1569 | int max_level; | ||
| 1547 | 1570 | ||
| 1548 | if (NULL == siocb->scm) | 1571 | if (NULL == siocb->scm) |
| 1549 | siocb->scm = &tmp_scm; | 1572 | siocb->scm = &tmp_scm; |
| @@ -1607,10 +1630,11 @@ static int unix_stream_sendmsg(struct kiocb *kiocb, struct socket *sock, | |||
| 1607 | 1630 | ||
| 1608 | /* Only send the fds in the first buffer */ | 1631 | /* Only send the fds in the first buffer */ |
| 1609 | err = unix_scm_to_skb(siocb->scm, skb, !fds_sent); | 1632 | err = unix_scm_to_skb(siocb->scm, skb, !fds_sent); |
| 1610 | if (err) { | 1633 | if (err < 0) { |
| 1611 | kfree_skb(skb); | 1634 | kfree_skb(skb); |
| 1612 | goto out_err; | 1635 | goto out_err; |
| 1613 | } | 1636 | } |
| 1637 | max_level = err + 1; | ||
| 1614 | fds_sent = true; | 1638 | fds_sent = true; |
| 1615 | 1639 | ||
| 1616 | err = memcpy_fromiovec(skb_put(skb, size), msg->msg_iov, size); | 1640 | 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, | |||
| 1626 | goto pipe_err_free; | 1650 | goto pipe_err_free; |
| 1627 | 1651 | ||
| 1628 | skb_queue_tail(&other->sk_receive_queue, skb); | 1652 | skb_queue_tail(&other->sk_receive_queue, skb); |
| 1653 | if (max_level > unix_sk(other)->recursion_level) | ||
| 1654 | unix_sk(other)->recursion_level = max_level; | ||
| 1629 | unix_state_unlock(other); | 1655 | unix_state_unlock(other); |
| 1630 | other->sk_data_ready(other, size); | 1656 | other->sk_data_ready(other, size); |
| 1631 | sent += size; | 1657 | sent += size; |
| @@ -1845,6 +1871,7 @@ static int unix_stream_recvmsg(struct kiocb *iocb, struct socket *sock, | |||
| 1845 | unix_state_lock(sk); | 1871 | unix_state_lock(sk); |
| 1846 | skb = skb_dequeue(&sk->sk_receive_queue); | 1872 | skb = skb_dequeue(&sk->sk_receive_queue); |
| 1847 | if (skb == NULL) { | 1873 | if (skb == NULL) { |
| 1874 | unix_sk(sk)->recursion_level = 0; | ||
| 1848 | if (copied >= target) | 1875 | if (copied >= target) |
| 1849 | goto unlock; | 1876 | goto unlock; |
| 1850 | 1877 | ||
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 | ||
