[PATCH 3.19.y-ckt 038/164] lockd: create NSM handles per net namespace

2015-12-02 Thread Kamal Mostafa
3.19.8-ckt11 -stable review patch.  If anyone has any objections, please let me 
know.

--

From: Andrey Ryabinin 

commit 0ad95472bf169a3501991f8f33f5147f792a8116 upstream.

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 
Signed-off-by: J. Bruce Fields 
Signed-off-by: Kamal Mostafa 
---
 fs/lockd/host.c |  7 ---
 fs/lockd/mon.c  | 36 ++--
 fs/lockd/netns.h|  1 +
 fs/lockd/svc.c  |  1 +
 fs/lockd/svc4proc.c |  2 +-
 fs/lockd/svcproc.c  |  2 +-
 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 969d589..b5f3c3a 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,
atomic_inc(>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;
 
diff --git a/fs/lockd/mon.c b/fs/lockd/mon.c
index 47a32b6..6c05cd1 100644
--- a/fs/lockd/mon.c
+++ b/fs/lockd/mon.c
@@ -51,7 +51,6 @@ struct nsm_res {
 };
 
 static const struct rpc_programnsm_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, _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, _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, _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 

[PATCH 3.19.y-ckt 038/164] lockd: create NSM handles per net namespace

2015-12-02 Thread Kamal Mostafa
3.19.8-ckt11 -stable review patch.  If anyone has any objections, please let me 
know.

--

From: Andrey Ryabinin 

commit 0ad95472bf169a3501991f8f33f5147f792a8116 upstream.

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 
Signed-off-by: J. Bruce Fields 
Signed-off-by: Kamal Mostafa 
---
 fs/lockd/host.c |  7 ---
 fs/lockd/mon.c  | 36 ++--
 fs/lockd/netns.h|  1 +
 fs/lockd/svc.c  |  1 +
 fs/lockd/svc4proc.c |  2 +-
 fs/lockd/svcproc.c  |  2 +-
 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 969d589..b5f3c3a 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,
atomic_inc(>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;
 
diff --git a/fs/lockd/mon.c b/fs/lockd/mon.c
index 47a32b6..6c05cd1 100644
--- a/fs/lockd/mon.c
+++ b/fs/lockd/mon.c
@@ -51,7 +51,6 @@ struct nsm_res {
 };
 
 static const struct rpc_programnsm_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, _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, _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, _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