Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0ad95472 authored by Andrey Ryabinin's avatar Andrey Ryabinin Committed by J. Bruce Fields
Browse files

lockd: create NSM handles per net namespace



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>
Cc: <stable@vger.kernel.org>
Signed-off-by: default avatarJ. Bruce Fields <bfields@redhat.com>
parent aaf91ec1
Loading
Loading
Loading
Loading
+4 −3
Original line number Diff line number Diff line
@@ -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;

+22 −14
Original line number Diff line number Diff line
@@ -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 @@ struct nsm_handle *nsm_get_handle(const struct sockaddr *sap,
	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 @@ struct nsm_handle *nsm_get_handle(const struct sockaddr *sap,
	}

	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 @@ struct nsm_handle *nsm_get_handle(const struct sockaddr *sap,

/**
 * 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",
+1 −0
Original line number Diff line number Diff line
@@ -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;
+1 −0
Original line number Diff line number Diff line
@@ -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;
}

+1 −1
Original line number Diff line number Diff line
@@ -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;
}

Loading