[PATCH v2 1/8] NFSD: validate transport name in listener_set before serv creation

Jeff Layton <[email protected]>
Newsgroups gmane.linux.network,gmane.linux.nfs,gmane.linux.kernel
Message-ID <[email protected]>
nfsd_nl_listener_set_doit() holds nfsd_mutex across the whole listener
teardown/rebuild. NFSD_A_SOCK_TRANSPORT_NAME is only checked for
presence, not content, so an arbitrary name reaches
svc_xprt_create_from_sa(), where a name matching no registered class
triggers request_module("svc%s", name) -- a TASK_KILLABLE usermode-helper
upcall run under nfsd_mutex.

Vet the name against the classes NFSD can instantiate (tcp, udp, rdma) in
nfsd_nl_validate_listeners(), which runs before nfsd_mutex is taken.

This narrows the upcall rather than removing it. "rdma" is accepted
unconditionally, so on a kernel where svcrdma is not built it still
reaches request_module("svcrdma") under nfsd_mutex -- as it must for the
modular case, where autoloading is legitimate.

Assisted-by: LLM
Link: https://syzkaller.appspot.com/bug?extid=c7eae0eb80858a2dba0f
Signed-off-by: Jeff Layton <[email protected]>
---
 fs/nfsd/nfsctl.c | 24 ++++++++++++++++++++++--
 1 file changed, 22 insertions(+), 2 deletions(-)

diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c
index 4e5e083d8477..e5844d8454b8 100644
--- a/fs/nfsd/nfsctl.c
+++ b/fs/nfsd/nfsctl.c
@@ -1975,14 +1975,31 @@ int nfsd_nl_version_get_doit(struct sk_buff *skb, struct genl_info *info)
 	return err;
 }
 
+/*
+ * Transport classes NFSD knows how to instantiate. Vetting the name here
+ * keeps a bogus string from reaching svc_xprt_create_from_sa(), where an
+ * unknown name triggers a request_module("svc%s", name) upcall under
+ * nfsd_mutex.
+ */
+static bool nfsd_nl_transport_supported(const char *name)
+{
+	static const char * const supported[] = { "tcp", "udp", "rdma" };
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(supported); i++)
+		if (!strcmp(name, supported[i]))
+			return true;
+	return false;
+}
+
 /**
  * nfsd_nl_validate_listeners - sanity-check the listener list from userland
  * @info: netlink metadata and command arguments
  *
  * Walk every NFSD_A_SERVER_SOCK_ADDR attribute and confirm that each entry
  * is well-formed: it parses against the policy, carries both an address and
- * a transport name, and the address is long enough for its family. Doing
- * this up front lets the callers below assume every entry is valid and
+ * a supported transport name, and the address is long enough for its family.
+ * Doing this up front lets the callers below assume every entry is valid and
  * guarantees we make no changes when the request is malformed.
  *
  * Return: 0 if every entry is valid, or a negative errno otherwise.
@@ -2006,6 +2023,9 @@ static int nfsd_nl_validate_listeners(struct genl_info *info)
 		if (!tb[NFSD_A_SOCK_ADDR] || !tb[NFSD_A_SOCK_TRANSPORT_NAME])
 			return -EINVAL;
 
+		if (!nfsd_nl_transport_supported(nla_data(tb[NFSD_A_SOCK_TRANSPORT_NAME])))
+			return -EPROTONOSUPPORT;
+
 		sa = nla_data(tb[NFSD_A_SOCK_ADDR]);
 		if (nla_len(tb[NFSD_A_SOCK_ADDR]) < sizeof(sa->sa_family))
 			return -EINVAL;

-- 
2.55.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.