diff options
author | Trond Myklebust <Trond.Myklebust@netapp.com> | 2012-10-23 13:51:58 -0400 |
---|---|---|
committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2012-10-24 10:46:22 -0400 |
commit | a4ee8d978e47e79d536226dccb48991f70091168 (patch) | |
tree | 5c85033eae24348ec07dc9a0a3ddbb25141153bb /fs/lockd | |
parent | f878b657ce8e7d3673afe48110ec208a29e38c4a (diff) |
LOCKD: fix races in nsm_client_get
Commit e9406db20fecbfcab646bad157b4cfdc7cadddfb (lockd: per-net
NSM client creation and destruction helpers introduced) contains
a nasty race on initialisation of the per-net NSM client because
it doesn't check whether or not the client is set after grabbing
the nsm_create_mutex.
Reported-by: Nix <nix@esperi.org.uk>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
Cc: stable@vger.kernel.org
Diffstat (limited to 'fs/lockd')
-rw-r--r-- | fs/lockd/mon.c | 43 |
1 files changed, 26 insertions, 17 deletions
diff --git a/fs/lockd/mon.c b/fs/lockd/mon.c index e4fb3ba5a58a..fe695603e395 100644 --- a/fs/lockd/mon.c +++ b/fs/lockd/mon.c | |||
@@ -85,29 +85,38 @@ static struct rpc_clnt *nsm_create(struct net *net) | |||
85 | return rpc_create(&args); | 85 | return rpc_create(&args); |
86 | } | 86 | } |
87 | 87 | ||
88 | static struct rpc_clnt *nsm_client_set(struct lockd_net *ln, | ||
89 | struct rpc_clnt *clnt) | ||
90 | { | ||
91 | spin_lock(&ln->nsm_clnt_lock); | ||
92 | if (ln->nsm_users == 0) { | ||
93 | if (clnt == NULL) | ||
94 | goto out; | ||
95 | ln->nsm_clnt = clnt; | ||
96 | } | ||
97 | clnt = ln->nsm_clnt; | ||
98 | ln->nsm_users++; | ||
99 | out: | ||
100 | spin_unlock(&ln->nsm_clnt_lock); | ||
101 | return clnt; | ||
102 | } | ||
103 | |||
88 | static struct rpc_clnt *nsm_client_get(struct net *net) | 104 | static struct rpc_clnt *nsm_client_get(struct net *net) |
89 | { | 105 | { |
90 | static DEFINE_MUTEX(nsm_create_mutex); | 106 | struct rpc_clnt *clnt, *new; |
91 | struct rpc_clnt *clnt; | ||
92 | struct lockd_net *ln = net_generic(net, lockd_net_id); | 107 | struct lockd_net *ln = net_generic(net, lockd_net_id); |
93 | 108 | ||
94 | spin_lock(&ln->nsm_clnt_lock); | 109 | clnt = nsm_client_set(ln, NULL); |
95 | if (ln->nsm_users) { | 110 | if (clnt != NULL) |
96 | ln->nsm_users++; | ||
97 | clnt = ln->nsm_clnt; | ||
98 | spin_unlock(&ln->nsm_clnt_lock); | ||
99 | goto out; | 111 | goto out; |
100 | } | ||
101 | spin_unlock(&ln->nsm_clnt_lock); | ||
102 | 112 | ||
103 | mutex_lock(&nsm_create_mutex); | 113 | clnt = new = nsm_create(net); |
104 | clnt = nsm_create(net); | 114 | if (IS_ERR(clnt)) |
105 | if (!IS_ERR(clnt)) { | 115 | goto out; |
106 | ln->nsm_clnt = clnt; | 116 | |
107 | smp_wmb(); | 117 | clnt = nsm_client_set(ln, new); |
108 | ln->nsm_users = 1; | 118 | if (clnt != new) |
109 | } | 119 | rpc_shutdown_client(new); |
110 | mutex_unlock(&nsm_create_mutex); | ||
111 | out: | 120 | out: |
112 | return clnt; | 121 | return clnt; |
113 | } | 122 | } |