lockd: create NSM handles per net namespace
Commit cb7323fffa
("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>
This commit is contained in:
parent
aaf91ec148
commit
0ad95472bf
@ -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,
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 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 {
|
|||||||
};
|
};
|
||||||
|
|
||||||
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);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -264,33 +263,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;
|
||||||
@ -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
|
* 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
|
||||||
@ -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
|
* @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()) {
|
||||||
@ -384,9 +388,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);
|
||||||
@ -400,7 +405,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);
|
||||||
@ -417,19 +422,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",
|
||||||
|
@ -15,6 +15,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;
|
||||||
|
@ -593,6 +593,7 @@ static int lockd_init_net(struct net *net)
|
|||||||
INIT_LIST_HEAD(&ln->lockd_manager.list);
|
INIT_LIST_HEAD(&ln->lockd_manager.list);
|
||||||
ln->lockd_manager.block_opens = false;
|
ln->lockd_manager.block_opens = false;
|
||||||
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,
|
|||||||
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,
|
|||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -235,7 +235,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
|
||||||
@ -243,11 +244,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);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
Loading…
Reference in New Issue
Block a user