[PATCH] NFS: cap the legacy DNS resolver upcall timeout

From: Yingjie Wang

Date: Fri Nov 06 2026 - 03:17:14 EST


nfs_cache_wait_for_upcall() converts nfsv4.cache_getent_timeout from
seconds to jiffies and passes the result to wait_for_completion_timeout().
The parameter is an unbounded unsigned long, so the product can exceed
LONG_MAX, where schedule_timeout() prints "wrong timeout value" with a
stack dump and returns at once, or wrap when the value is above
ULONG_MAX / HZ.

The wait is reached from nfs_parse_server_name() when a server name in
fs_locations is not a numeric address, i.e. for referrals, migration and
trunking discovery with CONFIG_NFS_USE_LEGACY_DNS. On x86-64 with
HZ=1000, 9223372036854776 seconds made the wait fail with -ETIMEDOUT
after 0.5 ms, and 18446744073709552 seconds wrapped to 384 jiffies and
gave up after 0.39 s instead of waiting for the upcall.

Cap the seconds value at the largest whole-second delay not exceeding
MAX_JIFFY_OFFSET before multiplying by HZ. This prevents both a negative
signed timeout and multiplication wrap. The timer wheel's existing
handling of very long delays is unchanged.

Fixes: e571cbf1a4f8 ("NFS: Add a dns resolver for use with NFSv4 referrals and migration")
Signed-off-by: Yingjie Wang <x-professor@xxxxxxxxxxx>
---

Notes:
Tested on x86-64 (HZ=1000) in QEMU. A test module (out of tree, not part
of this patch) looks up nfs_cache_wait_for_upcall() and calls it with a
completion that is signalled 100 ms (1 s for the wrapping value) later.
With the patch every case returned 0 after the completion; a 2-second
control and 9223372036854775 seconds behaved the same on both kernels.
I did not set up an NFSv4 server returning a referral. Also built for
i386 with W=1.

fs/nfs/cache_lib.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/fs/nfs/cache_lib.c b/fs/nfs/cache_lib.c
index ca4e81d4e315..5938256a2cdd 100644
--- a/fs/nfs/cache_lib.c
+++ b/fs/nfs/cache_lib.c
@@ -107,8 +107,10 @@ struct nfs_cache_defer_req *nfs_cache_defer_req_alloc(void)

int nfs_cache_wait_for_upcall(struct nfs_cache_defer_req *dreq)
{
- if (wait_for_completion_timeout(&dreq->completion,
- nfs_cache_getent_timeout * HZ) == 0)
+ unsigned long timeout = min_t(unsigned long, nfs_cache_getent_timeout,
+ MAX_JIFFY_OFFSET / HZ) * HZ;
+
+ if (wait_for_completion_timeout(&dreq->completion, timeout) == 0)
return -ETIMEDOUT;
return 0;
}

base-commit: f66469d9905b39d2c260a15d7d6db450f4db67c1
--
2.43.0