aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndrey Ryabinin <aryabinin@virtuozzo.com>2015-09-23 08:49:29 -0400
committerJ. Bruce Fields <bfields@redhat.com>2015-10-12 17:31:05 -0400
commit0ad95472bf169a3501991f8f33f5147f792a8116 (patch)
tree3c2303ba0350c2dcd54db07c9883954ed9092c49
parentaaf91ec148910e0c2bfd135ea19f870e7196e64f (diff)
lockd: create NSM handles per net namespace
Commit cb7323fffa85 ("lockd: create and use per-net NSM RPC clients on MON/UNMON requests") introduced per-net NSM RPC clients. Unfortunately this doesn't make any sense without per-net nsm_handle. E.g. the following scenario could happen Two hosts (X and Y) in different namespaces (A and B) share the same nsm struct. 1. nsm_monitor(host_X) called => NSM rpc client created, nsm->sm_monitored bit set. 2. nsm_mointor(host-Y) called => nsm->sm_monitored already set, we just exit. Thus in namespace B ln->nsm_clnt == NULL. 3. host X destroyed => nsm->sm_count decremented to 1 4. host Y destroyed => nsm_unmonitor() => nsm_mon_unmon() => NULL-ptr dereference of *ln->nsm_clnt So this could be fixed by making per-net nsm_handles list, instead of global. Thus different net namespaces will not be able share the same nsm_handle. Signed-off-by: Andrey Ryabinin <aryabinin@virtuozzo.com> Cc: <stable@vger.kernel.org> Signed-off-by: J. Bruce Fields <bfields@redhat.com>
-rw-r--r--fs/lockd/host.c7
-rw-r--r--fs/lockd/mon.c36
-rw-r--r--fs/lockd/netns.h1
-rw-r--r--fs/lockd/svc.c1
-rw-r--r--fs/lockd/svc4proc.c2
-rw-r--r--fs/lockd/svcproc.c2
-rw-r--r--include/linux/lockd/lockd.h9
7 files changed, 36 insertions, 22 deletions
diff --git a/fs/lockd/host.c b/fs/lockd/host.c
index 969d589c848d..b5f3c3ab0d5f 100644
--- a/fs/lockd/host.c
+++ b/fs/lockd/host.c
@@ -116,7 +116,7 @@ static struct nlm_host *nlm_alloc_host(struct nlm_lookup_host_info *ni,
116 atomic_inc(&nsm->sm_count); 116 atomic_inc(&nsm->sm_count);
117 else { 117 else {
118 host = NULL; 118 host = NULL;
119 nsm = nsm_get_handle(ni->sap, ni->salen, 119 nsm = nsm_get_handle(ni->net, ni->sap, ni->salen,
120 ni->hostname, ni->hostname_len); 120 ni->hostname, ni->hostname_len);
121 if (unlikely(nsm == NULL)) { 121 if (unlikely(nsm == NULL)) {
122 dprintk("lockd: %s failed; no nsm handle\n", 122 dprintk("lockd: %s failed; no nsm handle\n",
@@ -534,17 +534,18 @@ static struct nlm_host *next_host_state(struct hlist_head *cache,
534 534
535/** 535/**
536 * nlm_host_rebooted - Release all resources held by rebooted host 536 * nlm_host_rebooted - Release all resources held by rebooted host
537 * @net: network namespace
537 * @info: pointer to decoded results of NLM_SM_NOTIFY call 538 * @info: pointer to decoded results of NLM_SM_NOTIFY call
538 * 539 *
539 * We were notified that the specified host has rebooted. Release 540 * We were notified that the specified host has rebooted. Release
540 * all resources held by that peer. 541 * all resources held by that peer.
541 */ 542 */
542void nlm_host_rebooted(const struct nlm_reboot *info) 543void nlm_host_rebooted(const struct net *net, const struct nlm_reboot *info)
543{ 544{
544 struct nsm_handle *nsm; 545 struct nsm_handle *nsm;
545 struct nlm_host *host; 546 struct nlm_host *host;
546 547
547 nsm = nsm_reboot_lookup(info); 548 nsm = nsm_reboot_lookup(net, info);
548 if (unlikely(nsm == NULL)) 549 if (unlikely(nsm == NULL))
549 return; 550 return;
550 551
diff --git a/fs/lockd/mon.c b/fs/lockd/mon.c
index 47a32b6d9b90..6c05cd17e520 100644
--- a/fs/lockd/mon.c
+++ b/fs/lockd/mon.c
@@ -51,7 +51,6 @@ struct nsm_res {
51}; 51};
52 52
53static const struct rpc_program nsm_program; 53static const struct rpc_program nsm_program;
54static LIST_HEAD(nsm_handles);
55static DEFINE_SPINLOCK(nsm_lock); 54static DEFINE_SPINLOCK(nsm_lock);
56 55
57/* 56/*
@@ -264,33 +263,35 @@ void nsm_unmonitor(const struct nlm_host *host)
264 } 263 }
265} 264}
266 265
267static struct nsm_handle *nsm_lookup_hostname(const char *hostname, 266static struct nsm_handle *nsm_lookup_hostname(const struct list_head *nsm_handles,
268 const size_t len) 267 const char *hostname, const size_t len)
269{ 268{
270 struct nsm_handle *nsm; 269 struct nsm_handle *nsm;
271 270
272 list_for_each_entry(nsm, &nsm_handles, sm_link) 271 list_for_each_entry(nsm, nsm_handles, sm_link)
273 if (strlen(nsm->sm_name) == len && 272 if (strlen(nsm->sm_name) == len &&
274 memcmp(nsm->sm_name, hostname, len) == 0) 273 memcmp(nsm->sm_name, hostname, len) == 0)
275 return nsm; 274 return nsm;
276 return NULL; 275 return NULL;
277} 276}
278 277
279static struct nsm_handle *nsm_lookup_addr(const struct sockaddr *sap) 278static struct nsm_handle *nsm_lookup_addr(const struct list_head *nsm_handles,
279 const struct sockaddr *sap)
280{ 280{
281 struct nsm_handle *nsm; 281 struct nsm_handle *nsm;
282 282
283 list_for_each_entry(nsm, &nsm_handles, sm_link) 283 list_for_each_entry(nsm, nsm_handles, sm_link)
284 if (rpc_cmp_addr(nsm_addr(nsm), sap)) 284 if (rpc_cmp_addr(nsm_addr(nsm), sap))
285 return nsm; 285 return nsm;
286 return NULL; 286 return NULL;
287} 287}
288 288
289static struct nsm_handle *nsm_lookup_priv(const struct nsm_private *priv) 289static struct nsm_handle *nsm_lookup_priv(const struct list_head *nsm_handles,
290 const struct nsm_private *priv)
290{ 291{
291 struct nsm_handle *nsm; 292 struct nsm_handle *nsm;
292 293
293 list_for_each_entry(nsm, &nsm_handles, sm_link) 294 list_for_each_entry(nsm, nsm_handles, sm_link)
294 if (memcmp(nsm->sm_priv.data, priv->data, 295 if (memcmp(nsm->sm_priv.data, priv->data,
295 sizeof(priv->data)) == 0) 296 sizeof(priv->data)) == 0)
296 return nsm; 297 return nsm;
@@ -353,6 +354,7 @@ static struct nsm_handle *nsm_create_handle(const struct sockaddr *sap,
353 354
354/** 355/**
355 * nsm_get_handle - Find or create a cached nsm_handle 356 * nsm_get_handle - Find or create a cached nsm_handle
357 * @net: network namespace
356 * @sap: pointer to socket address of handle to find 358 * @sap: pointer to socket address of handle to find
357 * @salen: length of socket address 359 * @salen: length of socket address
358 * @hostname: pointer to C string containing hostname to find 360 * @hostname: pointer to C string containing hostname to find
@@ -365,11 +367,13 @@ static struct nsm_handle *nsm_create_handle(const struct sockaddr *sap,
365 * @hostname cannot be found in the handle cache. Returns NULL if 367 * @hostname cannot be found in the handle cache. Returns NULL if
366 * an error occurs. 368 * an error occurs.
367 */ 369 */
368struct nsm_handle *nsm_get_handle(const struct sockaddr *sap, 370struct nsm_handle *nsm_get_handle(const struct net *net,
371 const struct sockaddr *sap,
369 const size_t salen, const char *hostname, 372 const size_t salen, const char *hostname,
370 const size_t hostname_len) 373 const size_t hostname_len)
371{ 374{
372 struct nsm_handle *cached, *new = NULL; 375 struct nsm_handle *cached, *new = NULL;
376 struct lockd_net *ln = net_generic(net, lockd_net_id);
373 377
374 if (hostname && memchr(hostname, '/', hostname_len) != NULL) { 378 if (hostname && memchr(hostname, '/', hostname_len) != NULL) {
375 if (printk_ratelimit()) { 379 if (printk_ratelimit()) {
@@ -384,9 +388,10 @@ retry:
384 spin_lock(&nsm_lock); 388 spin_lock(&nsm_lock);
385 389
386 if (nsm_use_hostnames && hostname != NULL) 390 if (nsm_use_hostnames && hostname != NULL)
387 cached = nsm_lookup_hostname(hostname, hostname_len); 391 cached = nsm_lookup_hostname(&ln->nsm_handles,
392 hostname, hostname_len);
388 else 393 else
389 cached = nsm_lookup_addr(sap); 394 cached = nsm_lookup_addr(&ln->nsm_handles, sap);
390 395
391 if (cached != NULL) { 396 if (cached != NULL) {
392 atomic_inc(&cached->sm_count); 397 atomic_inc(&cached->sm_count);
@@ -400,7 +405,7 @@ retry:
400 } 405 }
401 406
402 if (new != NULL) { 407 if (new != NULL) {
403 list_add(&new->sm_link, &nsm_handles); 408 list_add(&new->sm_link, &ln->nsm_handles);
404 spin_unlock(&nsm_lock); 409 spin_unlock(&nsm_lock);
405 dprintk("lockd: created nsm_handle for %s (%s)\n", 410 dprintk("lockd: created nsm_handle for %s (%s)\n",
406 new->sm_name, new->sm_addrbuf); 411 new->sm_name, new->sm_addrbuf);
@@ -417,19 +422,22 @@ retry:
417 422
418/** 423/**
419 * nsm_reboot_lookup - match NLMPROC_SM_NOTIFY arguments to an nsm_handle 424 * nsm_reboot_lookup - match NLMPROC_SM_NOTIFY arguments to an nsm_handle
425 * @net: network namespace
420 * @info: pointer to NLMPROC_SM_NOTIFY arguments 426 * @info: pointer to NLMPROC_SM_NOTIFY arguments
421 * 427 *
422 * Returns a matching nsm_handle if found in the nsm cache. The returned 428 * Returns a matching nsm_handle if found in the nsm cache. The returned
423 * nsm_handle's reference count is bumped. Otherwise returns NULL if some 429 * nsm_handle's reference count is bumped. Otherwise returns NULL if some
424 * error occurred. 430 * error occurred.
425 */ 431 */
426struct nsm_handle *nsm_reboot_lookup(const struct nlm_reboot *info) 432struct nsm_handle *nsm_reboot_lookup(const struct net *net,
433 const struct nlm_reboot *info)
427{ 434{
428 struct nsm_handle *cached; 435 struct nsm_handle *cached;
436 struct lockd_net *ln = net_generic(net, lockd_net_id);
429 437
430 spin_lock(&nsm_lock); 438 spin_lock(&nsm_lock);
431 439
432 cached = nsm_lookup_priv(&info->priv); 440 cached = nsm_lookup_priv(&ln->nsm_handles, &info->priv);
433 if (unlikely(cached == NULL)) { 441 if (unlikely(cached == NULL)) {
434 spin_unlock(&nsm_lock); 442 spin_unlock(&nsm_lock);
435 dprintk("lockd: never saw rebooted peer '%.*s' before\n", 443 dprintk("lockd: never saw rebooted peer '%.*s' before\n",
diff --git a/fs/lockd/netns.h b/fs/lockd/netns.h
index 097bfa3adb1c..89fe011b1335 100644
--- a/fs/lockd/netns.h
+++ b/fs/lockd/netns.h
@@ -15,6 +15,7 @@ struct lockd_net {
15 spinlock_t nsm_clnt_lock; 15 spinlock_t nsm_clnt_lock;
16 unsigned int nsm_users; 16 unsigned int nsm_users;
17 struct rpc_clnt *nsm_clnt; 17 struct rpc_clnt *nsm_clnt;
18 struct list_head nsm_handles;
18}; 19};
19 20
20extern int lockd_net_id; 21extern int lockd_net_id;
diff --git a/fs/lockd/svc.c b/fs/lockd/svc.c
index d678bcc3cbcb..0dff13f41808 100644
--- a/fs/lockd/svc.c
+++ b/fs/lockd/svc.c
@@ -593,6 +593,7 @@ static int lockd_init_net(struct net *net)
593 INIT_LIST_HEAD(&ln->lockd_manager.list); 593 INIT_LIST_HEAD(&ln->lockd_manager.list);
594 ln->lockd_manager.block_opens = false; 594 ln->lockd_manager.block_opens = false;
595 spin_lock_init(&ln->nsm_clnt_lock); 595 spin_lock_init(&ln->nsm_clnt_lock);
596 INIT_LIST_HEAD(&ln->nsm_handles);
596 return 0; 597 return 0;
597} 598}
598 599
diff --git a/fs/lockd/svc4proc.c b/fs/lockd/svc4proc.c
index b147d1ae71fd..09c576f26c7b 100644
--- a/fs/lockd/svc4proc.c
+++ b/fs/lockd/svc4proc.c
@@ -421,7 +421,7 @@ nlm4svc_proc_sm_notify(struct svc_rqst *rqstp, struct nlm_reboot *argp,
421 return rpc_system_err; 421 return rpc_system_err;
422 } 422 }
423 423
424 nlm_host_rebooted(argp); 424 nlm_host_rebooted(SVC_NET(rqstp), argp);
425 return rpc_success; 425 return rpc_success;
426} 426}
427 427
diff --git a/fs/lockd/svcproc.c b/fs/lockd/svcproc.c
index 21171f0c6477..fb26b9f522e7 100644
--- a/fs/lockd/svcproc.c
+++ b/fs/lockd/svcproc.c
@@ -464,7 +464,7 @@ nlmsvc_proc_sm_notify(struct svc_rqst *rqstp, struct nlm_reboot *argp,
464 return rpc_system_err; 464 return rpc_system_err;
465 } 465 }
466 466
467 nlm_host_rebooted(argp); 467 nlm_host_rebooted(SVC_NET(rqstp), argp);
468 return rpc_success; 468 return rpc_success;
469} 469}
470 470
diff --git a/include/linux/lockd/lockd.h b/include/linux/lockd/lockd.h
index ff82a32871b5..fd3b65bf51b5 100644
--- a/include/linux/lockd/lockd.h
+++ b/include/linux/lockd/lockd.h
@@ -235,7 +235,8 @@ void nlm_rebind_host(struct nlm_host *);
235struct nlm_host * nlm_get_host(struct nlm_host *); 235struct nlm_host * nlm_get_host(struct nlm_host *);
236void nlm_shutdown_hosts(void); 236void nlm_shutdown_hosts(void);
237void nlm_shutdown_hosts_net(struct net *net); 237void nlm_shutdown_hosts_net(struct net *net);
238void nlm_host_rebooted(const struct nlm_reboot *); 238void nlm_host_rebooted(const struct net *net,
239 const struct nlm_reboot *);
239 240
240/* 241/*
241 * Host monitoring 242 * Host monitoring
@@ -243,11 +244,13 @@ void nlm_host_rebooted(const struct nlm_reboot *);
243int nsm_monitor(const struct nlm_host *host); 244int nsm_monitor(const struct nlm_host *host);
244void nsm_unmonitor(const struct nlm_host *host); 245void nsm_unmonitor(const struct nlm_host *host);
245 246
246struct nsm_handle *nsm_get_handle(const struct sockaddr *sap, 247struct nsm_handle *nsm_get_handle(const struct net *net,
248 const struct sockaddr *sap,
247 const size_t salen, 249 const size_t salen,
248 const char *hostname, 250 const char *hostname,
249 const size_t hostname_len); 251 const size_t hostname_len);
250struct nsm_handle *nsm_reboot_lookup(const struct nlm_reboot *info); 252struct nsm_handle *nsm_reboot_lookup(const struct net *net,
253 const struct nlm_reboot *info);
251void nsm_release(struct nsm_handle *nsm); 254void nsm_release(struct nsm_handle *nsm);
252 255
253/* 256/*