lockd: create NSM handles per net namespace
authorAndrey Ryabinin <aryabinin@virtuozzo.com>
Wed, 23 Sep 2015 12:49:29 +0000 (15:49 +0300)
committerJ. Bruce Fields <bfields@redhat.com>
Mon, 12 Oct 2015 21:31:05 +0000 (17:31 -0400)
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>
fs/lockd/host.c
fs/lockd/mon.c
fs/lockd/netns.h
fs/lockd/svc.c
fs/lockd/svc4proc.c
fs/lockd/svcproc.c
include/linux/lockd/lockd.h

index 969d589c848df8e066754e5ca5f3b02729eed919..b5f3c3ab0d5f28f7df7e2c9a6e3cbacbe348c8c6 100644 (file)
@@ -116,7 +116,7 @@ static struct nlm_host *nlm_alloc_host(struct nlm_lookup_host_info *ni,
                atomic_inc(&nsm->sm_count);
        else {
                host = NULL;
-               nsm = nsm_get_handle(ni->sap, ni->salen,
+               nsm = nsm_get_handle(ni->net, ni->sap, ni->salen,
                                        ni->hostname, ni->hostname_len);
                if (unlikely(nsm == NULL)) {
                        dprintk("lockd: %s failed; no nsm handle\n",
@@ -534,17 +534,18 @@ static struct nlm_host *next_host_state(struct hlist_head *cache,
 
 /**
  * nlm_host_rebooted - Release all resources held by rebooted host
+ * @net:  network namespace
  * @info: pointer to decoded results of NLM_SM_NOTIFY call
  *
  * We were notified that the specified host has rebooted.  Release
  * all resources held by that peer.
  */
-void nlm_host_rebooted(const struct nlm_reboot *info)
+void nlm_host_rebooted(const struct net *net, const struct nlm_reboot *info)
 {
        struct nsm_handle *nsm;
        struct nlm_host *host;
 
-       nsm = nsm_reboot_lookup(info);
+       nsm = nsm_reboot_lookup(net, info);
        if (unlikely(nsm == NULL))
                return;
 
index 47a32b6d9b9001a2a58a54fd154e50f7c08891be..6c05cd17e520f5c5c0ff3fdb2a7c251f5d7bd8ca 100644 (file)
@@ -51,7 +51,6 @@ struct nsm_res {
 };
 
 static const struct rpc_program        nsm_program;
-static                         LIST_HEAD(nsm_handles);
 static                         DEFINE_SPINLOCK(nsm_lock);
 
 /*
@@ -264,33 +263,35 @@ void nsm_unmonitor(const struct nlm_host *host)
        }
 }
 
-static struct nsm_handle *nsm_lookup_hostname(const char *hostname,
-                                             const size_t len)
+static struct nsm_handle *nsm_lookup_hostname(const struct list_head *nsm_handles,
+                                       const char *hostname, const size_t len)
 {
        struct nsm_handle *nsm;
 
-       list_for_each_entry(nsm, &nsm_handles, sm_link)
+       list_for_each_entry(nsm, nsm_handles, sm_link)
                if (strlen(nsm->sm_name) == len &&
                    memcmp(nsm->sm_name, hostname, len) == 0)
                        return nsm;
        return NULL;
 }
 
-static struct nsm_handle *nsm_lookup_addr(const struct sockaddr *sap)
+static struct nsm_handle *nsm_lookup_addr(const struct list_head *nsm_handles,
+                                       const struct sockaddr *sap)
 {
        struct nsm_handle *nsm;
 
-       list_for_each_entry(nsm, &nsm_handles, sm_link)
+       list_for_each_entry(nsm, nsm_handles, sm_link)
                if (rpc_cmp_addr(nsm_addr(nsm), sap))
                        return nsm;
        return NULL;
 }
 
-static struct nsm_handle *nsm_lookup_priv(const struct nsm_private *priv)
+static struct nsm_handle *nsm_lookup_priv(const struct list_head *nsm_handles,
+                                       const struct nsm_private *priv)
 {
        struct nsm_handle *nsm;
 
-       list_for_each_entry(nsm, &nsm_handles, sm_link)
+       list_for_each_entry(nsm, nsm_handles, sm_link)
                if (memcmp(nsm->sm_priv.data, priv->data,
                                        sizeof(priv->data)) == 0)
                        return nsm;
@@ -353,6 +354,7 @@ static struct nsm_handle *nsm_create_handle(const struct sockaddr *sap,
 
 /**
  * nsm_get_handle - Find or create a cached nsm_handle
+ * @net: network namespace
  * @sap: pointer to socket address of handle to find
  * @salen: length of socket address
  * @hostname: pointer to C string containing hostname to find
@@ -365,11 +367,13 @@ static struct nsm_handle *nsm_create_handle(const struct sockaddr *sap,
  * @hostname cannot be found in the handle cache.  Returns NULL if
  * an error occurs.
  */
-struct nsm_handle *nsm_get_handle(const struct sockaddr *sap,
+struct nsm_handle *nsm_get_handle(const struct net *net,
+                                 const struct sockaddr *sap,
                                  const size_t salen, const char *hostname,
                                  const size_t hostname_len)
 {
        struct nsm_handle *cached, *new = NULL;
+       struct lockd_net *ln = net_generic(net, lockd_net_id);
 
        if (hostname && memchr(hostname, '/', hostname_len) != NULL) {
                if (printk_ratelimit()) {
@@ -384,9 +388,10 @@ retry:
        spin_lock(&nsm_lock);
 
        if (nsm_use_hostnames && hostname != NULL)
-               cached = nsm_lookup_hostname(hostname, hostname_len);
+               cached = nsm_lookup_hostname(&ln->nsm_handles,
+                                       hostname, hostname_len);
        else
-               cached = nsm_lookup_addr(sap);
+               cached = nsm_lookup_addr(&ln->nsm_handles, sap);
 
        if (cached != NULL) {
                atomic_inc(&cached->sm_count);
@@ -400,7 +405,7 @@ retry:
        }
 
        if (new != NULL) {
-               list_add(&new->sm_link, &nsm_handles);
+               list_add(&new->sm_link, &ln->nsm_handles);
                spin_unlock(&nsm_lock);
                dprintk("lockd: created nsm_handle for %s (%s)\n",
                                new->sm_name, new->sm_addrbuf);
@@ -417,19 +422,22 @@ retry:
 
 /**
  * nsm_reboot_lookup - match NLMPROC_SM_NOTIFY arguments to an nsm_handle
+ * @net:  network namespace
  * @info: pointer to NLMPROC_SM_NOTIFY arguments
  *
  * Returns a matching nsm_handle if found in the nsm cache. The returned
  * nsm_handle's reference count is bumped. Otherwise returns NULL if some
  * error occurred.
  */
-struct nsm_handle *nsm_reboot_lookup(const struct nlm_reboot *info)
+struct nsm_handle *nsm_reboot_lookup(const struct net *net,
+                               const struct nlm_reboot *info)
 {
        struct nsm_handle *cached;
+       struct lockd_net *ln = net_generic(net, lockd_net_id);
 
        spin_lock(&nsm_lock);
 
-       cached = nsm_lookup_priv(&info->priv);
+       cached = nsm_lookup_priv(&ln->nsm_handles, &info->priv);
        if (unlikely(cached == NULL)) {
                spin_unlock(&nsm_lock);
                dprintk("lockd: never saw rebooted peer '%.*s' before\n",
index 097bfa3adb1c54a6402e5dabcfce7a3bc13b88a4..89fe011b1335fb8cb02580f1e4a30be289bb61ff 100644 (file)
@@ -15,6 +15,7 @@ struct lockd_net {
        spinlock_t nsm_clnt_lock;
        unsigned int nsm_users;
        struct rpc_clnt *nsm_clnt;
+       struct list_head nsm_handles;
 };
 
 extern int lockd_net_id;
index d678bcc3cbcb440e90045e578c755f6972079852..0dff13f41808add83dabf2149aeb5260f2e5f42a 100644 (file)
@@ -593,6 +593,7 @@ static int lockd_init_net(struct net *net)
        INIT_LIST_HEAD(&ln->lockd_manager.list);
        ln->lockd_manager.block_opens = false;
        spin_lock_init(&ln->nsm_clnt_lock);
+       INIT_LIST_HEAD(&ln->nsm_handles);
        return 0;
 }
 
index b147d1ae71fd9ec3c21ea69f847be1b2b047896d..09c576f26c7b76f5f39a6f7e98a37f297d09e533 100644 (file)
@@ -421,7 +421,7 @@ nlm4svc_proc_sm_notify(struct svc_rqst *rqstp, struct nlm_reboot *argp,
                return rpc_system_err;
        }
 
-       nlm_host_rebooted(argp);
+       nlm_host_rebooted(SVC_NET(rqstp), argp);
        return rpc_success;
 }
 
index 21171f0c6477560d7a5f6deb3e336e6c98581f96..fb26b9f522e74df4529b59c55b02502284f37dbc 100644 (file)
@@ -464,7 +464,7 @@ nlmsvc_proc_sm_notify(struct svc_rqst *rqstp, struct nlm_reboot *argp,
                return rpc_system_err;
        }
 
-       nlm_host_rebooted(argp);
+       nlm_host_rebooted(SVC_NET(rqstp), argp);
        return rpc_success;
 }
 
index ff82a32871b566401eb0a177a4b0bf45450b8f4d..fd3b65bf51b50731bf7e58f3d23f8f489ab18cd0 100644 (file)
@@ -235,7 +235,8 @@ void                  nlm_rebind_host(struct nlm_host *);
 struct nlm_host * nlm_get_host(struct nlm_host *);
 void             nlm_shutdown_hosts(void);
 void             nlm_shutdown_hosts_net(struct net *net);
-void             nlm_host_rebooted(const struct nlm_reboot *);
+void             nlm_host_rebooted(const struct net *net,
+                                       const struct nlm_reboot *);
 
 /*
  * Host monitoring
@@ -243,11 +244,13 @@ void                nlm_host_rebooted(const struct nlm_reboot *);
 int              nsm_monitor(const struct nlm_host *host);
 void             nsm_unmonitor(const struct nlm_host *host);
 
-struct nsm_handle *nsm_get_handle(const struct sockaddr *sap,
+struct nsm_handle *nsm_get_handle(const struct net *net,
+                                       const struct sockaddr *sap,
                                        const size_t salen,
                                        const char *hostname,
                                        const size_t hostname_len);
-struct nsm_handle *nsm_reboot_lookup(const struct nlm_reboot *info);
+struct nsm_handle *nsm_reboot_lookup(const struct net *net,
+                                       const struct nlm_reboot *info);
 void             nsm_release(struct nsm_handle *nsm);
 
 /*