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

Commit 03a9a42a authored by Trond Myklebust's avatar Trond Myklebust
Browse files

SUNRPC: NULL utsname dereference on NFS umount during namespace cleanup

Fix an Oopsable condition when nsm_mon_unmon is called as part of the
namespace cleanup, which now apparently happens after the utsname
has been freed.

Link: http://lkml.kernel.org/r/20150125220604.090121ae@neptune.home


Reported-by: default avatarBruno Prémont <bonbons@linux-vserver.org>
Cc: stable@vger.kernel.org # 3.18
Signed-off-by: default avatarTrond Myklebust <trond.myklebust@primarydata.com>
parent e2c63e09
Loading
Loading
Loading
Loading
+9 −4
Original line number Diff line number Diff line
@@ -65,7 +65,7 @@ static inline struct sockaddr *nsm_addr(const struct nsm_handle *nsm)
	return (struct sockaddr *)&nsm->sm_addr;
}

static struct rpc_clnt *nsm_create(struct net *net)
static struct rpc_clnt *nsm_create(struct net *net, const char *nodename)
{
	struct sockaddr_in sin = {
		.sin_family		= AF_INET,
@@ -77,6 +77,7 @@ static struct rpc_clnt *nsm_create(struct net *net)
		.address		= (struct sockaddr *)&sin,
		.addrsize		= sizeof(sin),
		.servername		= "rpc.statd",
		.nodename		= nodename,
		.program		= &nsm_program,
		.version		= NSM_VERSION,
		.authflavor		= RPC_AUTH_NULL,
@@ -102,7 +103,7 @@ static struct rpc_clnt *nsm_client_set(struct lockd_net *ln,
	return clnt;
}

static struct rpc_clnt *nsm_client_get(struct net *net)
static struct rpc_clnt *nsm_client_get(struct net *net, const char *nodename)
{
	struct rpc_clnt	*clnt, *new;
	struct lockd_net *ln = net_generic(net, lockd_net_id);
@@ -111,7 +112,7 @@ static struct rpc_clnt *nsm_client_get(struct net *net)
	if (clnt != NULL)
		goto out;

	clnt = new = nsm_create(net);
	clnt = new = nsm_create(net, nodename);
	if (IS_ERR(clnt))
		goto out;

@@ -190,19 +191,23 @@ int nsm_monitor(const struct nlm_host *host)
	struct nsm_res	res;
	int		status;
	struct rpc_clnt *clnt;
	const char *nodename = NULL;

	dprintk("lockd: nsm_monitor(%s)\n", nsm->sm_name);

	if (nsm->sm_monitored)
		return 0;

	if (host->h_rpcclnt)
		nodename = host->h_rpcclnt->cl_nodename;

	/*
	 * Choose whether to record the caller_name or IP address of
	 * this peer in the local rpc.statd's database.
	 */
	nsm->sm_mon_name = nsm_use_hostnames ? nsm->sm_name : nsm->sm_addrbuf;

	clnt = nsm_client_get(host->net);
	clnt = nsm_client_get(host->net, nodename);
	if (IS_ERR(clnt)) {
		status = PTR_ERR(clnt);
		dprintk("lockd: failed to create NSM upcall transport, "
+2 −1
Original line number Diff line number Diff line
@@ -57,7 +57,7 @@ struct rpc_clnt {
	const struct rpc_timeout *cl_timeout;	/* Timeout strategy */

	int			cl_nodelen;	/* nodename length */
	char 			cl_nodename[UNX_MAXNODENAME];
	char 			cl_nodename[UNX_MAXNODENAME+1];
	struct rpc_pipe_dir_head cl_pipedir_objects;
	struct rpc_clnt *	cl_parent;	/* Points to parent of clones */
	struct rpc_rtt		cl_rtt_default;
@@ -112,6 +112,7 @@ struct rpc_create_args {
	struct sockaddr		*saddress;
	const struct rpc_timeout *timeout;
	const char		*servername;
	const char		*nodename;
	const struct rpc_program *program;
	u32			prognumber;	/* overrides program->number */
	u32			version;
+7 −5
Original line number Diff line number Diff line
@@ -286,10 +286,8 @@ static struct rpc_xprt *rpc_clnt_set_transport(struct rpc_clnt *clnt,

static void rpc_clnt_set_nodename(struct rpc_clnt *clnt, const char *nodename)
{
	clnt->cl_nodelen = strlen(nodename);
	if (clnt->cl_nodelen > UNX_MAXNODENAME)
		clnt->cl_nodelen = UNX_MAXNODENAME;
	memcpy(clnt->cl_nodename, nodename, clnt->cl_nodelen);
	clnt->cl_nodelen = strlcpy(clnt->cl_nodename,
			nodename, sizeof(clnt->cl_nodename));
}

static int rpc_client_register(struct rpc_clnt *clnt,
@@ -365,6 +363,7 @@ static struct rpc_clnt * rpc_new_client(const struct rpc_create_args *args,
	const struct rpc_version *version;
	struct rpc_clnt *clnt = NULL;
	const struct rpc_timeout *timeout;
	const char *nodename = args->nodename;
	int err;

	/* sanity check the name before trying to print it */
@@ -420,8 +419,10 @@ static struct rpc_clnt * rpc_new_client(const struct rpc_create_args *args,

	atomic_set(&clnt->cl_count, 1);

	if (nodename == NULL)
		nodename = utsname()->nodename;
	/* save the nodename */
	rpc_clnt_set_nodename(clnt, utsname()->nodename);
	rpc_clnt_set_nodename(clnt, nodename);

	err = rpc_client_register(clnt, args->authflavor, args->client_name);
	if (err)
@@ -576,6 +577,7 @@ static struct rpc_clnt *__rpc_clone_client(struct rpc_create_args *args,
	if (xprt == NULL)
		goto out_err;
	args->servername = xprt->servername;
	args->nodename = clnt->cl_nodename;

	new = rpc_new_client(args, xprt, clnt);
	if (IS_ERR(new)) {
+6 −2
Original line number Diff line number Diff line
@@ -355,7 +355,8 @@ int rpcb_create_local(struct net *net)
	return result;
}

static struct rpc_clnt *rpcb_create(struct net *net, const char *hostname,
static struct rpc_clnt *rpcb_create(struct net *net, const char *nodename,
				    const char *hostname,
				    struct sockaddr *srvaddr, size_t salen,
				    int proto, u32 version)
{
@@ -365,6 +366,7 @@ static struct rpc_clnt *rpcb_create(struct net *net, const char *hostname,
		.address	= srvaddr,
		.addrsize	= salen,
		.servername	= hostname,
		.nodename	= nodename,
		.program	= &rpcb_program,
		.version	= version,
		.authflavor	= RPC_AUTH_UNIX,
@@ -740,7 +742,9 @@ void rpcb_getport_async(struct rpc_task *task)
	dprintk("RPC: %5u %s: trying rpcbind version %u\n",
		task->tk_pid, __func__, bind_version);

	rpcb_clnt = rpcb_create(xprt->xprt_net, xprt->servername, sap, salen,
	rpcb_clnt = rpcb_create(xprt->xprt_net,
				clnt->cl_nodename,
				xprt->servername, sap, salen,
				xprt->prot, bind_version);
	if (IS_ERR(rpcb_clnt)) {
		status = PTR_ERR(rpcb_clnt);