diff options
author | Andrey Ryabinin <aryabinin@virtuozzo.com> | 2015-09-23 08:49:29 -0400 |
---|---|---|
committer | J. Bruce Fields <bfields@redhat.com> | 2015-10-12 17:31:05 -0400 |
commit | 0ad95472bf169a3501991f8f33f5147f792a8116 (patch) | |
tree | 3c2303ba0350c2dcd54db07c9883954ed9092c49 | |
parent | aaf91ec148910e0c2bfd135ea19f870e7196e64f (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.c | 7 | ||||
-rw-r--r-- | fs/lockd/mon.c | 36 | ||||
-rw-r--r-- | fs/lockd/netns.h | 1 | ||||
-rw-r--r-- | fs/lockd/svc.c | 1 | ||||
-rw-r--r-- | fs/lockd/svc4proc.c | 2 | ||||
-rw-r--r-- | fs/lockd/svcproc.c | 2 | ||||
-rw-r--r-- | include/linux/lockd/lockd.h | 9 |
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 | */ |
542 | void nlm_host_rebooted(const struct nlm_reboot *info) | 543 | void 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 | ||
53 | static const struct rpc_program nsm_program; | 53 | static const struct rpc_program nsm_program; |
54 | static LIST_HEAD(nsm_handles); | ||
55 | static DEFINE_SPINLOCK(nsm_lock); | 54 | static 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 | ||
267 | static struct nsm_handle *nsm_lookup_hostname(const char *hostname, | 266 | static 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 | ||
279 | static struct nsm_handle *nsm_lookup_addr(const struct sockaddr *sap) | 278 | static 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 | ||
289 | static struct nsm_handle *nsm_lookup_priv(const struct nsm_private *priv) | 289 | static 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 | */ |
368 | struct nsm_handle *nsm_get_handle(const struct sockaddr *sap, | 370 | struct 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 | */ |
426 | struct nsm_handle *nsm_reboot_lookup(const struct nlm_reboot *info) | 432 | struct 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 | ||
20 | extern int lockd_net_id; | 21 | extern 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 *); | |||
235 | struct nlm_host * nlm_get_host(struct nlm_host *); | 235 | struct nlm_host * nlm_get_host(struct nlm_host *); |
236 | void nlm_shutdown_hosts(void); | 236 | void nlm_shutdown_hosts(void); |
237 | void nlm_shutdown_hosts_net(struct net *net); | 237 | void nlm_shutdown_hosts_net(struct net *net); |
238 | void nlm_host_rebooted(const struct nlm_reboot *); | 238 | void 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 *); | |||
243 | int nsm_monitor(const struct nlm_host *host); | 244 | int nsm_monitor(const struct nlm_host *host); |
244 | void nsm_unmonitor(const struct nlm_host *host); | 245 | void nsm_unmonitor(const struct nlm_host *host); |
245 | 246 | ||
246 | struct nsm_handle *nsm_get_handle(const struct sockaddr *sap, | 247 | struct 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); |
250 | struct nsm_handle *nsm_reboot_lookup(const struct nlm_reboot *info); | 252 | struct nsm_handle *nsm_reboot_lookup(const struct net *net, |
253 | const struct nlm_reboot *info); | ||
251 | void nsm_release(struct nsm_handle *nsm); | 254 | void nsm_release(struct nsm_handle *nsm); |
252 | 255 | ||
253 | /* | 256 | /* |