RDS:TCP: Synchronize rds_tcp_accept_one with rds_send_xmit when resetting t_sock
authorSowmini Varadhan <sowmini.varadhan@oracle.com>
Mon, 2 May 2016 18:24:51 +0000 (11:24 -0700)
committerDavid S. Miller <davem@davemloft.net>
Tue, 3 May 2016 20:03:44 +0000 (16:03 -0400)
commiteb192840266fab3e3da644018121eed30153355d
tree882f8ee60fe758e2e78275cdf00349cce1878000
parent42c8819b8d7245f54d5cfa6c2ec5a436818aeda9
RDS:TCP: Synchronize rds_tcp_accept_one with rds_send_xmit when resetting t_sock

There is a race condition between rds_send_xmit -> rds_tcp_xmit
and the code that deals with resolution of duelling syns added
by commit 241b271952eb ("RDS-TCP: Reset tcp callbacks if re-using an
outgoing socket in rds_tcp_accept_one()").

Specifically, we may end up derefencing a null pointer in rds_send_xmit
if we have the interleaving sequence:
           rds_tcp_accept_one                  rds_send_xmit

                                             conn is RDS_CONN_UP, so
      invoke rds_tcp_xmit

                                             tc = conn->c_transport_data
        rds_tcp_restore_callbacks
            /* reset t_sock */
      null ptr deref from tc->t_sock

The race condition can be avoided without adding the overhead of
additional locking in the xmit path: have rds_tcp_accept_one wait
for rds_tcp_xmit threads to complete before resetting callbacks.
The synchronization can be done in the same manner as rds_conn_shutdown().
First set the rds_conn_state to something other than RDS_CONN_UP
(so that new threads cannot get into rds_tcp_xmit()), then wait for
RDS_IN_XMIT to be cleared in the conn->c_flags indicating that any
threads in rds_tcp_xmit are done.

Fixes: 241b271952eb ("RDS-TCP: Reset tcp callbacks if re-using an
outgoing socket in rds_tcp_accept_one()")
Signed-off-by: Sowmini Varadhan <sowmini.varadhan@oracle.com>
Acked-by: Santosh Shilimkar <santosh.shilimkar@oracle.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
net/rds/tcp.c
net/rds/tcp_listen.c