All of lore.kernel.org
 help / color / mirror / Atom feed
From: Olaf Kirch <okir@suse.de>
To: nfs@lists.sourceforge.net
Subject: Race condition in xprt_disconnect
Date: Fri, 2 Apr 2004 10:32:36 +0200	[thread overview]
Message-ID: <20040402083236.GE11477@suse.de> (raw)

[-- Attachment #1: Type: text/plain, Size: 737 bytes --]

Hi,

it seems bad things can happen if two RPC processes call xprt_disconnect
at the same time. I have two bug reports of ppc machines oopsing
somewhere deep in some SELinux functions because inode->i_security
is NULL. inode->i_security is allocated when the inode is created,
and cleared when the inode is destroyed.

The stack looks something like 
xprt_disconnect->sock_release->...->selinux_foofah

The only way I can see this happening is if sock_release is called twice,
and indeed we do not seem to protect against this. Please take a look
at the attached patch. It should prevent these oopses, but I'm not
entirely sure it's safe.

Thanks,
Olaf
-- 
Olaf Kirch     |  The Hardware Gods hate me.
okir@suse.de   |
---------------+ 

[-- Attachment #2: sunrpc-disconnect-race --]
[-- Type: text/plain, Size: 2849 bytes --]

--- linux-2.6.4/net/sunrpc/xprt.c.fix	2004-04-01 18:51:50.000000000 +0200
+++ linux-2.6.4/net/sunrpc/xprt.c	2004-04-01 18:51:03.000000000 +0200
@@ -87,7 +87,7 @@
 static struct rpc_xprt * xprt_setup(int proto, struct sockaddr_in *ap,
 						struct rpc_timeout *to);
 static struct socket *xprt_create_socket(struct rpc_xprt *, int, int);
-static void	xprt_bind_socket(struct rpc_xprt *, struct socket *);
+static int	xprt_bind_socket(struct rpc_xprt *, struct socket *);
 static int      __xprt_get_cong(struct rpc_xprt *, struct rpc_task *);
 
 #ifdef RPC_DEBUG_DATA
@@ -389,26 +389,33 @@
 static void
 xprt_close(struct rpc_xprt *xprt, int reconnecting)
 {
-	struct socket	*sock = xprt->sock;
-	struct sock	*sk = xprt->inet;
-
-	if (!sk)
-		return;
-
-	write_lock_bh(&sk->sk_callback_lock);
+	struct socket	*sock;
+	struct sock	*sk;
+	
+	spin_lock_bh(&xprt->sock_lock);
+	if ((sk = xprt->inet) != NULL)
+		sock_hold(sk);
 	xprt->inet = NULL;
+
+	sock = xprt->sock;
 	xprt->sock = NULL;
+	spin_unlock_bh(&xprt->sock_lock);
 
-	sk->sk_user_data    = NULL;
-	sk->sk_data_ready   = xprt->old_data_ready;
-	sk->sk_state_change = xprt->old_state_change;
-	sk->sk_write_space  = xprt->old_write_space;
-	write_unlock_bh(&sk->sk_callback_lock);
+	if (sk != NULL) {
+		write_lock_bh(&sk->sk_callback_lock);
+		sk->sk_user_data    = NULL;
+		sk->sk_data_ready   = xprt->old_data_ready;
+		sk->sk_state_change = xprt->old_state_change;
+		sk->sk_write_space  = xprt->old_write_space;
+		sk->sk_no_check	 = 0;
+		write_unlock_bh(&sk->sk_callback_lock);
+		sock_put(sk);
+	}
 
 	xprt_disconnect(xprt, reconnecting);
-	sk->sk_no_check	 = 0;
 
-	sock_release(sock);
+	if (sock)
+		sock_release(sock);
 }
 
 static void
@@ -481,7 +488,10 @@
 		goto out_err;
 		return;
 	}
-	xprt_bind_socket(xprt, sock);
+	if (xprt_bind_socket(xprt, sock) < 0) {
+		sock_release(sock);
+		goto out_err;
+	}
 	xprt_sock_setbufsize(xprt);
 
 	if (!xprt->stream)
@@ -1520,13 +1530,17 @@
 	return err;
 }
 
-static void
+static int
 xprt_bind_socket(struct rpc_xprt *xprt, struct socket *sock)
 {
 	struct sock	*sk = sock->sk;
+	int		error = -EBUSY;
 
+	/* Can it happen that xprt->inet is non-null when we
+	 * get here? If so we should indicate an error so
+	 * that the caller can drop the newly created socket */
 	if (xprt->inet)
-		return;
+		goto out;
 
 	write_lock_bh(&sk->sk_callback_lock);
 	sk->sk_user_data = xprt;
@@ -1545,13 +1559,19 @@
 		xprt_clear_connected(xprt);
 	}
 	sk->sk_write_space = xprt_write_space;
+	write_unlock_bh(&sk->sk_callback_lock);
 
 	/* Reset to new socket */
-	xprt->sock = sock;
-	xprt->inet = sk;
-	write_unlock_bh(&sk->sk_callback_lock);
+	spin_lock_bh(&xprt->sock_lock);
+	if (xprt->inet == NULL) {
+		xprt->sock = sock;
+		xprt->inet = sk;
+		error = 0;
+	}
+	spin_unlock_bh(&xprt->sock_lock);
 
-	return;
+out:
+	return error;
 }
 
 /*

             reply	other threads:[~2004-04-02  8:32 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2004-04-02  8:32 Olaf Kirch [this message]
  -- strict thread matches above, loose matches on Subject: below --
2004-04-05 20:39 Race condition in xprt_disconnect Trond Myklebust

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20040402083236.GE11477@suse.de \
    --to=okir@suse.de \
    --cc=nfs@lists.sourceforge.net \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.