aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJon Paul Maloy <jon.maloy@ericsson.com>2014-05-14 05:39:09 -0400
committerDavid S. Miller <davem@davemloft.net>2014-05-14 15:19:47 -0400
commit4f4482dcd9a0606a30541ff165ddaca64748299b (patch)
tree8fbc7936da4cba170abd49388bf1255011c543ea
parent6163a194e02ab6cab2758b277a0ae082378dd4e6 (diff)
tipc: compensate for double accounting in socket rcv buffer
The function net/core/sock.c::__release_sock() runs a tight loop to move buffers from the socket backlog queue to the receive queue. As a security measure, sk_backlog.len of the receiving socket is not set to zero until after the loop is finished, i.e., until the whole backlog queue has been transferred to the receive queue. During this transfer, the data that has already been moved is counted both in the backlog queue and the receive queue, hence giving an incorrect picture of the available queue space for new arriving buffers. This leads to unnecessary rejection of buffers by sk_add_backlog(), which in TIPC leads to unnecessarily broken connections. In this commit, we compensate for this double accounting by adding a counter that keeps track of it. The function socket.c::backlog_rcv() receives buffers one by one from __release_sock(), and adds them to the socket receive queue. If the transfer is successful, it increases a new atomic counter 'tipc_sock::dupl_rcvcnt' with 'truesize' of the transferred buffer. If a new buffer arrives during this transfer and finds the socket busy (owned), we attempt to add it to the backlog. However, when sk_add_backlog() is called, we adjust the 'limit' parameter with the value of the new counter, so that the risk of inadvertent rejection is eliminated. It should be noted that this change does not invalidate the original purpose of zeroing 'sk_backlog.len' after the full transfer. We set an upper limit for dupl_rcvcnt, so that if a 'wild' sender (i.e., one that doesn't respect the send window) keeps pumping in buffers to sk_add_backlog(), he will eventually reach an upper limit, (2 x TIPC_CONN_OVERLOAD_LIMIT). After that, no messages can be added to the backlog, and the connection will be broken. Ordinary, well- behaved senders will never reach this buffer limit at all. Signed-off-by: Jon Maloy <jon.maloy@ericsson.com> Reviewed-by: Ying Xue <ying.xue@windriver.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--net/tipc/socket.c28
-rw-r--r--net/tipc/socket.h2
2 files changed, 21 insertions, 9 deletions
diff --git a/net/tipc/socket.c b/net/tipc/socket.c
index 8685daf060f9..249500614568 100644
--- a/net/tipc/socket.c
+++ b/net/tipc/socket.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * net/tipc/socket.c: TIPC socket API 2* net/tipc/socket.c: TIPC socket API
3 * 3 *
4 * Copyright (c) 2001-2007, 2012-2014, Ericsson AB 4 * Copyright (c) 2001-2007, 2012-2014, Ericsson AB
5 * Copyright (c) 2004-2008, 2010-2013, Wind River Systems 5 * Copyright (c) 2004-2008, 2010-2013, Wind River Systems
@@ -45,7 +45,7 @@
45 45
46#define CONN_TIMEOUT_DEFAULT 8000 /* default connect timeout = 8s */ 46#define CONN_TIMEOUT_DEFAULT 8000 /* default connect timeout = 8s */
47 47
48static int backlog_rcv(struct sock *sk, struct sk_buff *skb); 48static int tipc_backlog_rcv(struct sock *sk, struct sk_buff *skb);
49static void tipc_data_ready(struct sock *sk); 49static void tipc_data_ready(struct sock *sk);
50static void tipc_write_space(struct sock *sk); 50static void tipc_write_space(struct sock *sk);
51static int tipc_release(struct socket *sock); 51static int tipc_release(struct socket *sock);
@@ -196,11 +196,12 @@ static int tipc_sk_create(struct net *net, struct socket *sock,
196 sock->state = state; 196 sock->state = state;
197 197
198 sock_init_data(sock, sk); 198 sock_init_data(sock, sk);
199 sk->sk_backlog_rcv = backlog_rcv; 199 sk->sk_backlog_rcv = tipc_backlog_rcv;
200 sk->sk_rcvbuf = sysctl_tipc_rmem[1]; 200 sk->sk_rcvbuf = sysctl_tipc_rmem[1];
201 sk->sk_data_ready = tipc_data_ready; 201 sk->sk_data_ready = tipc_data_ready;
202 sk->sk_write_space = tipc_write_space; 202 sk->sk_write_space = tipc_write_space;
203 tipc_sk(sk)->conn_timeout = CONN_TIMEOUT_DEFAULT; 203 tsk->conn_timeout = CONN_TIMEOUT_DEFAULT;
204 atomic_set(&tsk->dupl_rcvcnt, 0);
204 tipc_port_unlock(port); 205 tipc_port_unlock(port);
205 206
206 if (sock->state == SS_READY) { 207 if (sock->state == SS_READY) {
@@ -1416,7 +1417,7 @@ static u32 filter_rcv(struct sock *sk, struct sk_buff *buf)
1416} 1417}
1417 1418
1418/** 1419/**
1419 * backlog_rcv - handle incoming message from backlog queue 1420 * tipc_backlog_rcv - handle incoming message from backlog queue
1420 * @sk: socket 1421 * @sk: socket
1421 * @buf: message 1422 * @buf: message
1422 * 1423 *
@@ -1424,13 +1425,18 @@ static u32 filter_rcv(struct sock *sk, struct sk_buff *buf)
1424 * 1425 *
1425 * Returns 0 1426 * Returns 0
1426 */ 1427 */
1427static int backlog_rcv(struct sock *sk, struct sk_buff *buf) 1428static int tipc_backlog_rcv(struct sock *sk, struct sk_buff *buf)
1428{ 1429{
1429 u32 res; 1430 u32 res;
1431 struct tipc_sock *tsk = tipc_sk(sk);
1430 1432
1431 res = filter_rcv(sk, buf); 1433 res = filter_rcv(sk, buf);
1432 if (res) 1434 if (unlikely(res))
1433 tipc_reject_msg(buf, res); 1435 tipc_reject_msg(buf, res);
1436
1437 if (atomic_read(&tsk->dupl_rcvcnt) < TIPC_CONN_OVERLOAD_LIMIT)
1438 atomic_add(buf->truesize, &tsk->dupl_rcvcnt);
1439
1434 return 0; 1440 return 0;
1435} 1441}
1436 1442
@@ -1445,8 +1451,9 @@ static int backlog_rcv(struct sock *sk, struct sk_buff *buf)
1445 */ 1451 */
1446u32 tipc_sk_rcv(struct sock *sk, struct sk_buff *buf) 1452u32 tipc_sk_rcv(struct sock *sk, struct sk_buff *buf)
1447{ 1453{
1454 struct tipc_sock *tsk = tipc_sk(sk);
1448 u32 res; 1455 u32 res;
1449 1456 uint limit;
1450 /* 1457 /*
1451 * Process message if socket is unlocked; otherwise add to backlog queue 1458 * Process message if socket is unlocked; otherwise add to backlog queue
1452 * 1459 *
@@ -1457,7 +1464,10 @@ u32 tipc_sk_rcv(struct sock *sk, struct sk_buff *buf)
1457 if (!sock_owned_by_user(sk)) { 1464 if (!sock_owned_by_user(sk)) {
1458 res = filter_rcv(sk, buf); 1465 res = filter_rcv(sk, buf);
1459 } else { 1466 } else {
1460 if (sk_add_backlog(sk, buf, rcvbuf_limit(sk, buf))) 1467 if (sk->sk_backlog.len == 0)
1468 atomic_set(&tsk->dupl_rcvcnt, 0);
1469 limit = rcvbuf_limit(sk, buf) + atomic_read(&tsk->dupl_rcvcnt);
1470 if (sk_add_backlog(sk, buf, limit))
1461 res = TIPC_ERR_OVERLOAD; 1471 res = TIPC_ERR_OVERLOAD;
1462 else 1472 else
1463 res = TIPC_OK; 1473 res = TIPC_OK;
diff --git a/net/tipc/socket.h b/net/tipc/socket.h
index 74e5c7f195a6..86c27cc51e33 100644
--- a/net/tipc/socket.h
+++ b/net/tipc/socket.h
@@ -44,12 +44,14 @@
44 * @port: port - interacts with 'sk' and with the rest of the TIPC stack 44 * @port: port - interacts with 'sk' and with the rest of the TIPC stack
45 * @peer_name: the peer of the connection, if any 45 * @peer_name: the peer of the connection, if any
46 * @conn_timeout: the time we can wait for an unresponded setup request 46 * @conn_timeout: the time we can wait for an unresponded setup request
47 * @dupl_rcvcnt: number of bytes counted twice, in both backlog and rcv queue
47 */ 48 */
48 49
49struct tipc_sock { 50struct tipc_sock {
50 struct sock sk; 51 struct sock sk;
51 struct tipc_port port; 52 struct tipc_port port;
52 unsigned int conn_timeout; 53 unsigned int conn_timeout;
54 atomic_t dupl_rcvcnt;
53}; 55};
54 56
55static inline struct tipc_sock *tipc_sk(const struct sock *sk) 57static inline struct tipc_sock *tipc_sk(const struct sock *sk)