[PATCH v2 2/8] NFSD: cap the number of listeners accepted in listener_set

From: Jeff Layton

Date: Tue Aug 11 2026 - 08:04:38 EST


nfsd_nl_listener_set_doit() matches each requested listener against the
existing set in a nested loop that is O(N * M) in the requested (N) and
existing (M) counts, run under sv_lock with bottom halves disabled. A
userland request with a very large listener list can therefore spin in
atomic context for a long time.

Reject requests carrying more than NFSD_NL_LISTENER_MAX (1024) entries in
nfsd_nl_validate_listeners(), before any lock is taken. The limit is far
above any realistic configuration.

M is not capped here: write_ports() can add listeners too, via
svc_addsock() and svc_xprt_create(). But each one costs a real socket, so M
is bounded by resources, where N was bounded only by the message size.
Capping N leaves ~1M iterations plus 1024 nla_parse_nested() calls under
sv_lock as the worst case.

Assisted-by: LLM
Signed-off-by: Jeff Layton <jlayton@xxxxxxxxxx>
---
fs/nfsd/nfsctl.c | 16 +++++++++++-----
1 file changed, 11 insertions(+), 5 deletions(-)

diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c
index e5844d8454b8..66931caaaaed 100644
--- a/fs/nfsd/nfsctl.c
+++ b/fs/nfsd/nfsctl.c
@@ -1992,21 +1992,22 @@ static bool nfsd_nl_transport_supported(const char *name)
return false;
}

+/* Upper bound on the number of listeners a single request may carry. */
+#define NFSD_NL_LISTENER_MAX 1024
+
/**
* 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 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.
+ * Walk every NFSD_A_SERVER_SOCK_ADDR attribute and confirm that the list is
+ * not oversized and that each entry is well-formed.
*
* Return: 0 if every entry is valid, or a negative errno otherwise.
*/
static int nfsd_nl_validate_listeners(struct genl_info *info)
{
const struct nlattr *attr;
+ unsigned int count = 0;
int rem;

nlmsg_for_each_attr_type(attr, NFSD_A_SERVER_SOCK_ADDR, info->nlhdr,
@@ -2015,6 +2016,11 @@ static int nfsd_nl_validate_listeners(struct genl_info *info)
struct sockaddr *sa;
int err;

+ if (++count > NFSD_NL_LISTENER_MAX) {
+ NL_SET_ERR_MSG(info->extack, "too many listeners");
+ return -E2BIG;
+ }
+
err = nla_parse_nested(tb, NFSD_A_SOCK_MAX, attr,
nfsd_sock_nl_policy, info->extack);
if (err < 0)

--
2.55.0