Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Andrey Ryabinin and committed by
J. Bruce Fields
0ad95472 aaf91ec1

+36 -22
+4 -3
fs/lockd/host.c
··· 116 116 atomic_inc(&nsm->sm_count); 117 117 else { 118 118 host = NULL; 119 - nsm = nsm_get_handle(ni->sap, ni->salen, 119 + nsm = nsm_get_handle(ni->net, ni->sap, ni->salen, 120 120 ni->hostname, ni->hostname_len); 121 121 if (unlikely(nsm == NULL)) { 122 122 dprintk("lockd: %s failed; no nsm handle\n", ··· 534 534 535 535 /** 536 536 * nlm_host_rebooted - Release all resources held by rebooted host 537 + * @net: network namespace 537 538 * @info: pointer to decoded results of NLM_SM_NOTIFY call 538 539 * 539 540 * We were notified that the specified host has rebooted. Release 540 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 545 struct nsm_handle *nsm; 545 546 struct nlm_host *host; 546 547 547 - nsm = nsm_reboot_lookup(info); 548 + nsm = nsm_reboot_lookup(net, info); 548 549 if (unlikely(nsm == NULL)) 549 550 return; 550 551
+22 -14
fs/lockd/mon.c
··· 51 51 }; 52 52 53 53 static const struct rpc_program nsm_program; 54 - static LIST_HEAD(nsm_handles); 55 54 static DEFINE_SPINLOCK(nsm_lock); 56 55 57 56 /* ··· 263 264 } 264 265 } 265 266 266 - static struct nsm_handle *nsm_lookup_hostname(const char *hostname, 267 - const size_t len) 267 + static struct nsm_handle *nsm_lookup_hostname(const struct list_head *nsm_handles, 268 + const char *hostname, const size_t len) 268 269 { 269 270 struct nsm_handle *nsm; 270 271 271 - list_for_each_entry(nsm, &nsm_handles, sm_link) 272 + list_for_each_entry(nsm, nsm_handles, sm_link) 272 273 if (strlen(nsm->sm_name) == len && 273 274 memcmp(nsm->sm_name, hostname, len) == 0) 274 275 return nsm; 275 276 return NULL; 276 277 } 277 278 278 - static struct nsm_handle *nsm_lookup_addr(const struct sockaddr *sap) 279 + static struct nsm_handle *nsm_lookup_addr(const struct list_head *nsm_handles, 280 + const struct sockaddr *sap) 279 281 { 280 282 struct nsm_handle *nsm; 281 283 282 - list_for_each_entry(nsm, &nsm_handles, sm_link) 284 + list_for_each_entry(nsm, nsm_handles, sm_link) 283 285 if (rpc_cmp_addr(nsm_addr(nsm), sap)) 284 286 return nsm; 285 287 return NULL; 286 288 } 287 289 288 - static struct nsm_handle *nsm_lookup_priv(const struct nsm_private *priv) 290 + static struct nsm_handle *nsm_lookup_priv(const struct list_head *nsm_handles, 291 + const struct nsm_private *priv) 289 292 { 290 293 struct nsm_handle *nsm; 291 294 292 - list_for_each_entry(nsm, &nsm_handles, sm_link) 295 + list_for_each_entry(nsm, nsm_handles, sm_link) 293 296 if (memcmp(nsm->sm_priv.data, priv->data, 294 297 sizeof(priv->data)) == 0) 295 298 return nsm; ··· 354 353 355 354 /** 356 355 * nsm_get_handle - Find or create a cached nsm_handle 356 + * @net: network namespace 357 357 * @sap: pointer to socket address of handle to find 358 358 * @salen: length of socket address 359 359 * @hostname: pointer to C string containing hostname to find ··· 367 365 * @hostname cannot be found in the handle cache. Returns NULL if 368 366 * an error occurs. 369 367 */ 370 - struct nsm_handle *nsm_get_handle(const struct sockaddr *sap, 368 + struct nsm_handle *nsm_get_handle(const struct net *net, 369 + const struct sockaddr *sap, 371 370 const size_t salen, const char *hostname, 372 371 const size_t hostname_len) 373 372 { 374 373 struct nsm_handle *cached, *new = NULL; 374 + struct lockd_net *ln = net_generic(net, lockd_net_id); 375 375 376 376 if (hostname && memchr(hostname, '/', hostname_len) != NULL) { 377 377 if (printk_ratelimit()) { ··· 388 384 spin_lock(&nsm_lock); 389 385 390 386 if (nsm_use_hostnames && hostname != NULL) 391 - cached = nsm_lookup_hostname(hostname, hostname_len); 387 + cached = nsm_lookup_hostname(&ln->nsm_handles, 388 + hostname, hostname_len); 392 389 else 393 - cached = nsm_lookup_addr(sap); 390 + cached = nsm_lookup_addr(&ln->nsm_handles, sap); 394 391 395 392 if (cached != NULL) { 396 393 atomic_inc(&cached->sm_count); ··· 405 400 } 406 401 407 402 if (new != NULL) { 408 - list_add(&new->sm_link, &nsm_handles); 403 + list_add(&new->sm_link, &ln->nsm_handles); 409 404 spin_unlock(&nsm_lock); 410 405 dprintk("lockd: created nsm_handle for %s (%s)\n", 411 406 new->sm_name, new->sm_addrbuf); ··· 422 417 423 418 /** 424 419 * nsm_reboot_lookup - match NLMPROC_SM_NOTIFY arguments to an nsm_handle 420 + * @net: network namespace 425 421 * @info: pointer to NLMPROC_SM_NOTIFY arguments 426 422 * 427 423 * Returns a matching nsm_handle if found in the nsm cache. The returned 428 424 * nsm_handle's reference count is bumped. Otherwise returns NULL if some 429 425 * error occurred. 430 426 */ 431 - struct nsm_handle *nsm_reboot_lookup(const struct nlm_reboot *info) 427 + struct nsm_handle *nsm_reboot_lookup(const struct net *net, 428 + const struct nlm_reboot *info) 432 429 { 433 430 struct nsm_handle *cached; 431 + struct lockd_net *ln = net_generic(net, lockd_net_id); 434 432 435 433 spin_lock(&nsm_lock); 436 434 437 - cached = nsm_lookup_priv(&info->priv); 435 + cached = nsm_lookup_priv(&ln->nsm_handles, &info->priv); 438 436 if (unlikely(cached == NULL)) { 439 437 spin_unlock(&nsm_lock); 440 438 dprintk("lockd: never saw rebooted peer '%.*s' before\n",
+1
fs/lockd/netns.h
··· 15 15 spinlock_t nsm_clnt_lock; 16 16 unsigned int nsm_users; 17 17 struct rpc_clnt *nsm_clnt; 18 + struct list_head nsm_handles; 18 19 }; 19 20 20 21 extern int lockd_net_id;
+1
fs/lockd/svc.c
··· 593 593 INIT_LIST_HEAD(&ln->lockd_manager.list); 594 594 ln->lockd_manager.block_opens = false; 595 595 spin_lock_init(&ln->nsm_clnt_lock); 596 + INIT_LIST_HEAD(&ln->nsm_handles); 596 597 return 0; 597 598 } 598 599
+1 -1
fs/lockd/svc4proc.c
··· 421 421 return rpc_system_err; 422 422 } 423 423 424 - nlm_host_rebooted(argp); 424 + nlm_host_rebooted(SVC_NET(rqstp), argp); 425 425 return rpc_success; 426 426 } 427 427
+1 -1
fs/lockd/svcproc.c
··· 464 464 return rpc_system_err; 465 465 } 466 466 467 - nlm_host_rebooted(argp); 467 + nlm_host_rebooted(SVC_NET(rqstp), argp); 468 468 return rpc_success; 469 469 } 470 470
+6 -3
include/linux/lockd/lockd.h
··· 235 235 struct nlm_host * nlm_get_host(struct nlm_host *); 236 236 void nlm_shutdown_hosts(void); 237 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 242 * Host monitoring ··· 244 243 int nsm_monitor(const struct nlm_host *host); 245 244 void nsm_unmonitor(const struct nlm_host *host); 246 245 247 - struct nsm_handle *nsm_get_handle(const struct sockaddr *sap, 246 + struct nsm_handle *nsm_get_handle(const struct net *net, 247 + const struct sockaddr *sap, 248 248 const size_t salen, 249 249 const char *hostname, 250 250 const size_t hostname_len); 251 - struct nsm_handle *nsm_reboot_lookup(const struct nlm_reboot *info); 251 + struct nsm_handle *nsm_reboot_lookup(const struct net *net, 252 + const struct nlm_reboot *info); 252 253 void nsm_release(struct nsm_handle *nsm); 253 254 254 255 /*