ocfs2/o2net: o2net_listen_data_ready should do nothing if socket state is not TCP_LISTEN
authorTariq Saeed <tariq.x.saeed@oracle.com>
Thu, 3 Apr 2014 21:47:11 +0000 (14:47 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Thu, 3 Apr 2014 23:20:56 +0000 (16:20 -0700)
Orabug: 17330860

When accepting an incomming connection o2net_accept_one clones a child
data socket from the parent listening socket.  It then proceeds to setup
the child with callback o2net_data_ready() and sk_user_data to NULL.  If
data arrives in this window, o2net_listen_data_ready will be called with
some non-deterministic value in sk_user_data (not inherited).  We panic
when we page fault on sk_user_data -- in parent it is
sock_def_readable().

The fix is to recognize that this is a data socket being set up by
looking at the socket state and do nothing.

Signed-off-by: Tariq Saseed <tariq.x.saeed@oracle.com>
Signed-off-by: Srinivas Eeda <srinivas.eeda@oracle.com>
Reviewed-by: Mark Fasheh <mfasheh@suse.com>
Cc: Joel Becker <jlbec@evilplan.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
fs/ocfs2/cluster/tcp.c

index b4ab371d46d9ca305230231dc6e08085414f151e..eb649d23a4de24e4cde6860389528e7b2ef919dc 100644 (file)
@@ -1964,18 +1964,30 @@ static void o2net_listen_data_ready(struct sock *sk, int bytes)
                goto out;
        }
 
-       /* ->sk_data_ready is also called for a newly established child socket
-        * before it has been accepted and the acceptor has set up their
-        * data_ready.. we only want to queue listen work for our listening
-        * socket */
+       /* This callback may called twice when a new connection
+        * is  being established as a child socket inherits everything
+        * from a parent LISTEN socket, including the data_ready cb of
+        * the parent. This leads to a hazard. In o2net_accept_one()
+        * we are still initializing the child socket but have not
+        * changed the inherited data_ready callback yet when
+        * data starts arriving.
+        * We avoid this hazard by checking the state.
+        * For the listening socket,  the state will be TCP_LISTEN; for the new
+        * socket, will be  TCP_ESTABLISHED. Also, in this case,
+        * sk->sk_user_data is not a valid function pointer.
+        */
+
        if (sk->sk_state == TCP_LISTEN) {
                mlog(ML_TCP, "bytes: %d\n", bytes);
                queue_work(o2net_wq, &o2net_listen_work);
+       } else {
+               ready = NULL;
        }
 
 out:
        read_unlock(&sk->sk_callback_lock);
-       ready(sk, bytes);
+       if (ready != NULL)
+               ready(sk, bytes);
 }
 
 static int o2net_open_listening_sock(__be32 addr, __be16 port)