Commit e7f460fe authored by Andrey Ryabinin's avatar Andrey Ryabinin Committed by Greg Kroah-Hartman

lockd: create NSM handles per net namespace

commit 0ad95472 upstream.

Commit cb7323ff ("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: default avatarAndrey Ryabinin <aryabinin@virtuozzo.com>
Signed-off-by: default avatarJ. Bruce Fields <bfields@redhat.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 4eed7b9c
...@@ -116,7 +116,7 @@ static struct nlm_host *nlm_alloc_host(struct nlm_lookup_host_info *ni, ...@@ -116,7 +116,7 @@ static struct nlm_host *nlm_alloc_host(struct nlm_lookup_host_info *ni,
atomic_inc(&nsm->sm_count); atomic_inc(&nsm->sm_count);
else { else {
host = NULL; 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); ni->hostname, ni->hostname_len);
if (unlikely(nsm == NULL)) { if (unlikely(nsm == NULL)) {
dprintk("lockd: %s failed; no nsm handle\n", dprintk("lockd: %s failed; no nsm handle\n",
...@@ -534,17 +534,18 @@ static struct nlm_host *next_host_state(struct hlist_head *cache, ...@@ -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 * nlm_host_rebooted - Release all resources held by rebooted host
* @net: network namespace
* @info: pointer to decoded results of NLM_SM_NOTIFY call * @info: pointer to decoded results of NLM_SM_NOTIFY call
* *
* We were notified that the specified host has rebooted. Release * We were notified that the specified host has rebooted. Release
* all resources held by that peer. * 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 nsm_handle *nsm;
struct nlm_host *host; struct nlm_host *host;
nsm = nsm_reboot_lookup(info); nsm = nsm_reboot_lookup(net, info);
if (unlikely(nsm == NULL)) if (unlikely(nsm == NULL))
return; return;
......
...@@ -51,7 +51,6 @@ struct nsm_res { ...@@ -51,7 +51,6 @@ struct nsm_res {
}; };
static const struct rpc_program nsm_program; static const struct rpc_program nsm_program;
static LIST_HEAD(nsm_handles);
static DEFINE_SPINLOCK(nsm_lock); static DEFINE_SPINLOCK(nsm_lock);
/* /*
...@@ -259,33 +258,35 @@ void nsm_unmonitor(const struct nlm_host *host) ...@@ -259,33 +258,35 @@ void nsm_unmonitor(const struct nlm_host *host)
} }
} }
static struct nsm_handle *nsm_lookup_hostname(const char *hostname, static struct nsm_handle *nsm_lookup_hostname(const struct list_head *nsm_handles,
const size_t len) const char *hostname, const size_t len)
{ {
struct nsm_handle *nsm; 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 && if (strlen(nsm->sm_name) == len &&
memcmp(nsm->sm_name, hostname, len) == 0) memcmp(nsm->sm_name, hostname, len) == 0)
return nsm; return nsm;
return NULL; 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; 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)) if (rpc_cmp_addr(nsm_addr(nsm), sap))
return nsm; return nsm;
return NULL; 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; 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, if (memcmp(nsm->sm_priv.data, priv->data,
sizeof(priv->data)) == 0) sizeof(priv->data)) == 0)
return nsm; return nsm;
...@@ -350,6 +351,7 @@ static struct nsm_handle *nsm_create_handle(const struct sockaddr *sap, ...@@ -350,6 +351,7 @@ static struct nsm_handle *nsm_create_handle(const struct sockaddr *sap,
/** /**
* nsm_get_handle - Find or create a cached nsm_handle * nsm_get_handle - Find or create a cached nsm_handle
* @net: network namespace
* @sap: pointer to socket address of handle to find * @sap: pointer to socket address of handle to find
* @salen: length of socket address * @salen: length of socket address
* @hostname: pointer to C string containing hostname to find * @hostname: pointer to C string containing hostname to find
...@@ -362,11 +364,13 @@ static struct nsm_handle *nsm_create_handle(const struct sockaddr *sap, ...@@ -362,11 +364,13 @@ static struct nsm_handle *nsm_create_handle(const struct sockaddr *sap,
* @hostname cannot be found in the handle cache. Returns NULL if * @hostname cannot be found in the handle cache. Returns NULL if
* an error occurs. * 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 salen, const char *hostname,
const size_t hostname_len) const size_t hostname_len)
{ {
struct nsm_handle *cached, *new = NULL; struct nsm_handle *cached, *new = NULL;
struct lockd_net *ln = net_generic(net, lockd_net_id);
if (hostname && memchr(hostname, '/', hostname_len) != NULL) { if (hostname && memchr(hostname, '/', hostname_len) != NULL) {
if (printk_ratelimit()) { if (printk_ratelimit()) {
...@@ -381,9 +385,10 @@ struct nsm_handle *nsm_get_handle(const struct sockaddr *sap, ...@@ -381,9 +385,10 @@ struct nsm_handle *nsm_get_handle(const struct sockaddr *sap,
spin_lock(&nsm_lock); spin_lock(&nsm_lock);
if (nsm_use_hostnames && hostname != NULL) if (nsm_use_hostnames && hostname != NULL)
cached = nsm_lookup_hostname(hostname, hostname_len); cached = nsm_lookup_hostname(&ln->nsm_handles,
hostname, hostname_len);
else else
cached = nsm_lookup_addr(sap); cached = nsm_lookup_addr(&ln->nsm_handles, sap);
if (cached != NULL) { if (cached != NULL) {
atomic_inc(&cached->sm_count); atomic_inc(&cached->sm_count);
...@@ -397,7 +402,7 @@ struct nsm_handle *nsm_get_handle(const struct sockaddr *sap, ...@@ -397,7 +402,7 @@ struct nsm_handle *nsm_get_handle(const struct sockaddr *sap,
} }
if (new != NULL) { if (new != NULL) {
list_add(&new->sm_link, &nsm_handles); list_add(&new->sm_link, &ln->nsm_handles);
spin_unlock(&nsm_lock); spin_unlock(&nsm_lock);
dprintk("lockd: created nsm_handle for %s (%s)\n", dprintk("lockd: created nsm_handle for %s (%s)\n",
new->sm_name, new->sm_addrbuf); new->sm_name, new->sm_addrbuf);
...@@ -414,19 +419,22 @@ struct nsm_handle *nsm_get_handle(const struct sockaddr *sap, ...@@ -414,19 +419,22 @@ struct nsm_handle *nsm_get_handle(const struct sockaddr *sap,
/** /**
* nsm_reboot_lookup - match NLMPROC_SM_NOTIFY arguments to an nsm_handle * nsm_reboot_lookup - match NLMPROC_SM_NOTIFY arguments to an nsm_handle
* @net: network namespace
* @info: pointer to NLMPROC_SM_NOTIFY arguments * @info: pointer to NLMPROC_SM_NOTIFY arguments
* *
* Returns a matching nsm_handle if found in the nsm cache. The returned * 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 * nsm_handle's reference count is bumped. Otherwise returns NULL if some
* error occurred. * 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 nsm_handle *cached;
struct lockd_net *ln = net_generic(net, lockd_net_id);
spin_lock(&nsm_lock); spin_lock(&nsm_lock);
cached = nsm_lookup_priv(&info->priv); cached = nsm_lookup_priv(&ln->nsm_handles, &info->priv);
if (unlikely(cached == NULL)) { if (unlikely(cached == NULL)) {
spin_unlock(&nsm_lock); spin_unlock(&nsm_lock);
dprintk("lockd: never saw rebooted peer '%.*s' before\n", dprintk("lockd: never saw rebooted peer '%.*s' before\n",
......
...@@ -16,6 +16,7 @@ struct lockd_net { ...@@ -16,6 +16,7 @@ struct lockd_net {
spinlock_t nsm_clnt_lock; spinlock_t nsm_clnt_lock;
unsigned int nsm_users; unsigned int nsm_users;
struct rpc_clnt *nsm_clnt; struct rpc_clnt *nsm_clnt;
struct list_head nsm_handles;
}; };
extern int lockd_net_id; extern int lockd_net_id;
......
...@@ -583,6 +583,7 @@ static int lockd_init_net(struct net *net) ...@@ -583,6 +583,7 @@ static int lockd_init_net(struct net *net)
INIT_DELAYED_WORK(&ln->grace_period_end, grace_ender); INIT_DELAYED_WORK(&ln->grace_period_end, grace_ender);
INIT_LIST_HEAD(&ln->grace_list); INIT_LIST_HEAD(&ln->grace_list);
spin_lock_init(&ln->nsm_clnt_lock); spin_lock_init(&ln->nsm_clnt_lock);
INIT_LIST_HEAD(&ln->nsm_handles);
return 0; return 0;
} }
......
...@@ -421,7 +421,7 @@ nlm4svc_proc_sm_notify(struct svc_rqst *rqstp, struct nlm_reboot *argp, ...@@ -421,7 +421,7 @@ nlm4svc_proc_sm_notify(struct svc_rqst *rqstp, struct nlm_reboot *argp,
return rpc_system_err; return rpc_system_err;
} }
nlm_host_rebooted(argp); nlm_host_rebooted(SVC_NET(rqstp), argp);
return rpc_success; return rpc_success;
} }
......
...@@ -464,7 +464,7 @@ nlmsvc_proc_sm_notify(struct svc_rqst *rqstp, struct nlm_reboot *argp, ...@@ -464,7 +464,7 @@ nlmsvc_proc_sm_notify(struct svc_rqst *rqstp, struct nlm_reboot *argp,
return rpc_system_err; return rpc_system_err;
} }
nlm_host_rebooted(argp); nlm_host_rebooted(SVC_NET(rqstp), argp);
return rpc_success; return rpc_success;
} }
......
...@@ -236,7 +236,8 @@ void nlm_rebind_host(struct nlm_host *); ...@@ -236,7 +236,8 @@ void nlm_rebind_host(struct nlm_host *);
struct nlm_host * nlm_get_host(struct nlm_host *); struct nlm_host * nlm_get_host(struct nlm_host *);
void nlm_shutdown_hosts(void); void nlm_shutdown_hosts(void);
void nlm_shutdown_hosts_net(struct net *net); 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 * Host monitoring
...@@ -244,11 +245,13 @@ void nlm_host_rebooted(const struct nlm_reboot *); ...@@ -244,11 +245,13 @@ void nlm_host_rebooted(const struct nlm_reboot *);
int nsm_monitor(const struct nlm_host *host); int nsm_monitor(const struct nlm_host *host);
void nsm_unmonitor(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 size_t salen,
const char *hostname, const char *hostname,
const size_t hostname_len); 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); void nsm_release(struct nsm_handle *nsm);
/* /*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment