aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorAllan Stephens <allan.stephens@windriver.com>2008-07-15 01:43:32 -0400
committerDavid S. Miller <davem@davemloft.net>2008-07-15 01:43:32 -0400
commit2da59918e26837f305131cfac9c0f1b3b42bb8ae (patch)
treea6c183dfbb0d6f929dbf09167d8bbc3d92897e53 /net
parent8642bd9e04f51980b2b6293c66acf7e388c9a6e7 (diff)
tipc: Fix race condition that could cause accept() to fail
This patch ensurs that accept() returns successfully even when the newly created socket is immediately disconnected by its peer. Previously, accept() would fail if it was unable to pass back the optional address info for the socket's peer before the socket became disconnected; TIPC now allows accept() to gather peer address information after disconnection. As a bonus, the revised code accesses the socket's port more efficiently, without the overhead incurred by a reference table lookup. Signed-off-by: Allan Stephens <allan.stephens@windriver.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r--net/tipc/socket.c42
1 files changed, 22 insertions, 20 deletions
diff --git a/net/tipc/socket.c b/net/tipc/socket.c
index ddcb2a753aa6..1848693ebb82 100644
--- a/net/tipc/socket.c
+++ b/net/tipc/socket.c
@@ -63,6 +63,7 @@
63struct tipc_sock { 63struct tipc_sock {
64 struct sock sk; 64 struct sock sk;
65 struct tipc_port *p; 65 struct tipc_port *p;
66 struct tipc_portid peer_name;
66}; 67};
67 68
68#define tipc_sk(sk) ((struct tipc_sock *)(sk)) 69#define tipc_sk(sk) ((struct tipc_sock *)(sk))
@@ -377,27 +378,29 @@ static int bind(struct socket *sock, struct sockaddr *uaddr, int uaddr_len)
377 * @sock: socket structure 378 * @sock: socket structure
378 * @uaddr: area for returned socket address 379 * @uaddr: area for returned socket address
379 * @uaddr_len: area for returned length of socket address 380 * @uaddr_len: area for returned length of socket address
380 * @peer: 0 to obtain socket name, 1 to obtain peer socket name 381 * @peer: 0 = own ID, 1 = current peer ID, 2 = current/former peer ID
381 * 382 *
382 * Returns 0 on success, errno otherwise 383 * Returns 0 on success, errno otherwise
383 * 384 *
384 * NOTE: This routine doesn't need to take the socket lock since it doesn't 385 * NOTE: This routine doesn't need to take the socket lock since it only
385 * access any non-constant socket information. 386 * accesses socket information that is unchanging (or which changes in
387 * a completely predictable manner).
386 */ 388 */
387 389
388static int get_name(struct socket *sock, struct sockaddr *uaddr, 390static int get_name(struct socket *sock, struct sockaddr *uaddr,
389 int *uaddr_len, int peer) 391 int *uaddr_len, int peer)
390{ 392{
391 struct sockaddr_tipc *addr = (struct sockaddr_tipc *)uaddr; 393 struct sockaddr_tipc *addr = (struct sockaddr_tipc *)uaddr;
392 u32 portref = tipc_sk_port(sock->sk)->ref; 394 struct tipc_sock *tsock = tipc_sk(sock->sk);
393 u32 res;
394 395
395 if (peer) { 396 if (peer) {
396 res = tipc_peer(portref, &addr->addr.id); 397 if ((sock->state != SS_CONNECTED) &&
397 if (res) 398 ((peer != 2) || (sock->state != SS_DISCONNECTING)))
398 return res; 399 return -ENOTCONN;
400 addr->addr.id.ref = tsock->peer_name.ref;
401 addr->addr.id.node = tsock->peer_name.node;
399 } else { 402 } else {
400 tipc_ownidentity(portref, &addr->addr.id); 403 tipc_ownidentity(tsock->p->ref, &addr->addr.id);
401 } 404 }
402 405
403 *uaddr_len = sizeof(*addr); 406 *uaddr_len = sizeof(*addr);
@@ -766,18 +769,17 @@ exit:
766 769
767static int auto_connect(struct socket *sock, struct tipc_msg *msg) 770static int auto_connect(struct socket *sock, struct tipc_msg *msg)
768{ 771{
769 struct tipc_port *tport = tipc_sk_port(sock->sk); 772 struct tipc_sock *tsock = tipc_sk(sock->sk);
770 struct tipc_portid peer;
771 773
772 if (msg_errcode(msg)) { 774 if (msg_errcode(msg)) {
773 sock->state = SS_DISCONNECTING; 775 sock->state = SS_DISCONNECTING;
774 return -ECONNREFUSED; 776 return -ECONNREFUSED;
775 } 777 }
776 778
777 peer.ref = msg_origport(msg); 779 tsock->peer_name.ref = msg_origport(msg);
778 peer.node = msg_orignode(msg); 780 tsock->peer_name.node = msg_orignode(msg);
779 tipc_connect2port(tport->ref, &peer); 781 tipc_connect2port(tsock->p->ref, &tsock->peer_name);
780 tipc_set_portimportance(tport->ref, msg_importance(msg)); 782 tipc_set_portimportance(tsock->p->ref, msg_importance(msg));
781 sock->state = SS_CONNECTED; 783 sock->state = SS_CONNECTED;
782 return 0; 784 return 0;
783} 785}
@@ -1529,9 +1531,9 @@ static int accept(struct socket *sock, struct socket *new_sock, int flags)
1529 res = tipc_create(sock_net(sock->sk), new_sock, 0); 1531 res = tipc_create(sock_net(sock->sk), new_sock, 0);
1530 if (!res) { 1532 if (!res) {
1531 struct sock *new_sk = new_sock->sk; 1533 struct sock *new_sk = new_sock->sk;
1532 struct tipc_port *new_tport = tipc_sk_port(new_sk); 1534 struct tipc_sock *new_tsock = tipc_sk(new_sk);
1535 struct tipc_port *new_tport = new_tsock->p;
1533 u32 new_ref = new_tport->ref; 1536 u32 new_ref = new_tport->ref;
1534 struct tipc_portid id;
1535 struct tipc_msg *msg = buf_msg(buf); 1537 struct tipc_msg *msg = buf_msg(buf);
1536 1538
1537 lock_sock(new_sk); 1539 lock_sock(new_sk);
@@ -1545,9 +1547,9 @@ static int accept(struct socket *sock, struct socket *new_sock, int flags)
1545 1547
1546 /* Connect new socket to it's peer */ 1548 /* Connect new socket to it's peer */
1547 1549
1548 id.ref = msg_origport(msg); 1550 new_tsock->peer_name.ref = msg_origport(msg);
1549 id.node = msg_orignode(msg); 1551 new_tsock->peer_name.node = msg_orignode(msg);
1550 tipc_connect2port(new_ref, &id); 1552 tipc_connect2port(new_ref, &new_tsock->peer_name);
1551 new_sock->state = SS_CONNECTED; 1553 new_sock->state = SS_CONNECTED;
1552 1554
1553 tipc_set_portimportance(new_ref, msg_importance(msg)); 1555 tipc_set_portimportance(new_ref, msg_importance(msg));