Hold a local reference to new_sock->sk before installing callbacks
in rds_tcp_accept_one. After rds_tcp_set_callbacks() or
rds_tcp_reset_callbacks(), tc->t_sock is set to new_sock which
may race with the shutdown path.  A concurrent
rds_tcp_conn_path_shutdown() may call sock_release(), which sets
new_sock->sk = NULL and frees sk.

Subsequent accesses to new_sock->sk->sk_state dereference NULL,
causing the null dereference. So a local sock reference with
sock_hold() before installing callbacks will prevent the race.

Fixes: 826c1004d4ae ("net/rds: rds_tcp_conn_path_shutdown must not discard 
messages")
Reported-by: [email protected]
Closes: https://syzkaller.appspot.com/bug?extid=96046021045ffe6d7709
Signed-off-by: Allison Henderson <[email protected]>
---
 net/rds/tcp_listen.c | 17 ++++++++++++++---
 1 file changed, 14 insertions(+), 3 deletions(-)

diff --git a/net/rds/tcp_listen.c b/net/rds/tcp_listen.c
index 6fb5c928b8fd..cdc86473a1ba 100644
--- a/net/rds/tcp_listen.c
+++ b/net/rds/tcp_listen.c
@@ -177,6 +177,7 @@ int rds_tcp_accept_one(struct rds_tcp_net *rtn)
        struct rds_tcp_connection *rs_tcp = NULL;
        int conn_state;
        struct rds_conn_path *cp;
+       struct sock *sk;
        struct in6_addr *my_addr, *peer_addr;
 #if !IS_ENABLED(CONFIG_IPV6)
        struct in6_addr saddr, daddr;
@@ -298,6 +299,14 @@ int rds_tcp_accept_one(struct rds_tcp_net *rtn)
                rds_conn_path_drop(cp, 0);
                goto rst_nsk;
        }
+       /* Hold a local reference to sk before setting callbacks. Once callbacks
+        * are set, it is possible for a concurrent rds_tcp_conn_path_shutdown
+        * call to release the new_sock->sk and set it to NULL.  So we use
+        * a local sk here to avoid racing with callbacks
+        */
+       sk = new_sock->sk;
+       sock_hold(sk);
+
        if (rs_tcp->t_sock) {
                /* Duelling SYN has been handled in rds_tcp_accept_one() */
                rds_tcp_reset_callbacks(new_sock, cp);
@@ -316,13 +325,15 @@ int rds_tcp_accept_one(struct rds_tcp_net *rtn)
         * knowing that "rds_tcp_conn_path_shutdown" will
         * dequeue pending messages.
         */
-       if (new_sock->sk->sk_state == TCP_CLOSE_WAIT ||
-           new_sock->sk->sk_state == TCP_LAST_ACK ||
-           new_sock->sk->sk_state == TCP_CLOSE)
+       if (READ_ONCE(sk->sk_state) == TCP_CLOSE_WAIT ||
+           READ_ONCE(sk->sk_state) == TCP_LAST_ACK ||
+           READ_ONCE(sk->sk_state) == TCP_CLOSE)
                rds_conn_path_drop(cp, 0);
        else
                queue_delayed_work(cp->cp_wq, &cp->cp_recv_w, 0);
 
+       sock_put(sk);
+
        new_sock = NULL;
        ret = 0;
        if (conn->c_npaths == 0)
-- 
2.43.0


Reply via email to