fs/nfs/nfs4_fs.h | 3 +-- fs/nfs/nfs4proc.c | 2 +- fs/nfs/nfs4renewd.c | 10 +++++++--- fs/nfs/nfs4state.c | 2 +- 4 files changed, 10 insertions(+), 7 deletions(-)
The nfs_client::cl_lease_time field (as well as the jiffies variable it's
used with) is declared as *unsigned long*, which is 32-bit type on 32-bit
arches and 64-bit type on 64-bit arches. When nfs4_set_lease_period() that
sets nfs_client::cl_lease_time is called, 32-bit nfs_fsinfo::lease_time
field is multiplied by HZ -- that might overflow before being implicitly
cast to *unsigned long*. Actually, there's no need to multiply by HZ at all
the call sites of nfs4_set_lease_period() -- it makes more sense to do that
once, inside that function, calling check_mul_overflow() and capping result
at ULONG_MAX on actual overflow...
Found by Linux Verification Center (linuxtesting.org) with the Svace static
analysis tool.
Signed-off-by: Sergey Shtylyov <s.shtylyov@omp.ru>
Cc: stable@vger.kernel.org
---
The patch is against the master branch of Trond Myklebust's linux-nfs.git repo.
Changes in version 2:
- made use of check_mul_overflow() instead of mul_u32_u32();
- capped the multiplication result at ULONG_MAX instead of returning -ERANGE,
keeping nfs4_set_lease_period() *void*;
- rewrote the patch description accordingly.
fs/nfs/nfs4_fs.h | 3 +--
fs/nfs/nfs4proc.c | 2 +-
fs/nfs/nfs4renewd.c | 10 +++++++---
fs/nfs/nfs4state.c | 2 +-
4 files changed, 10 insertions(+), 7 deletions(-)
Index: linux-nfs/fs/nfs/nfs4_fs.h
===================================================================
--- linux-nfs.orig/fs/nfs/nfs4_fs.h
+++ linux-nfs/fs/nfs/nfs4_fs.h
@@ -463,8 +463,7 @@ struct nfs_client *nfs4_alloc_client(con
extern void nfs4_schedule_state_renewal(struct nfs_client *);
extern void nfs4_kill_renewd(struct nfs_client *);
extern void nfs4_renew_state(struct work_struct *);
-extern void nfs4_set_lease_period(struct nfs_client *clp, unsigned long lease);
-
+extern void nfs4_set_lease_period(struct nfs_client *clp, u32 period);
/* nfs4state.c */
extern const nfs4_stateid current_stateid;
Index: linux-nfs/fs/nfs/nfs4proc.c
===================================================================
--- linux-nfs.orig/fs/nfs/nfs4proc.c
+++ linux-nfs/fs/nfs/nfs4proc.c
@@ -5476,7 +5476,7 @@ static int nfs4_do_fsinfo(struct nfs_ser
err = _nfs4_do_fsinfo(server, fhandle, fsinfo);
trace_nfs4_fsinfo(server, fhandle, fsinfo->fattr, err);
if (err == 0) {
- nfs4_set_lease_period(server->nfs_client, fsinfo->lease_time * HZ);
+ nfs4_set_lease_period(server->nfs_client, fsinfo->lease_time);
break;
}
err = nfs4_handle_exception(server, err, &exception);
Index: linux-nfs/fs/nfs/nfs4renewd.c
===================================================================
--- linux-nfs.orig/fs/nfs/nfs4renewd.c
+++ linux-nfs/fs/nfs/nfs4renewd.c
@@ -137,11 +137,15 @@ nfs4_kill_renewd(struct nfs_client *clp)
* nfs4_set_lease_period - Sets the lease period on a nfs_client
*
* @clp: pointer to nfs_client
- * @lease: new value for lease period
+ * @period: new value for lease period (in seconds)
*/
-void nfs4_set_lease_period(struct nfs_client *clp,
- unsigned long lease)
+void nfs4_set_lease_period(struct nfs_client *clp, u32 period)
{
+ unsigned long lease;
+
+ if (check_mul_overflow(period, HZ, &lease))
+ lease = ULONG_MAX;
+
spin_lock(&clp->cl_lock);
clp->cl_lease_time = lease;
spin_unlock(&clp->cl_lock);
Index: linux-nfs/fs/nfs/nfs4state.c
===================================================================
--- linux-nfs.orig/fs/nfs/nfs4state.c
+++ linux-nfs/fs/nfs/nfs4state.c
@@ -103,7 +103,7 @@ static int nfs4_setup_state_renewal(stru
status = nfs4_proc_get_lease_time(clp, &fsinfo);
if (status == 0) {
- nfs4_set_lease_period(clp, fsinfo.lease_time * HZ);
+ nfs4_set_lease_period(clp, fsinfo.lease_time);
nfs4_schedule_state_renewal(clp);
}
On 7/1/25 11:35 PM, Sergey Shtylyov wrote: > The nfs_client::cl_lease_time field (as well as the jiffies variable it's > used with) is declared as *unsigned long*, which is 32-bit type on 32-bit > arches and 64-bit type on 64-bit arches. When nfs4_set_lease_period() that > sets nfs_client::cl_lease_time is called, 32-bit nfs_fsinfo::lease_time > field is multiplied by HZ -- that might overflow before being implicitly > cast to *unsigned long*. Actually, there's no need to multiply by HZ at all > the call sites of nfs4_set_lease_period() -- it makes more sense to do that > once, inside that function, calling check_mul_overflow() and capping result > at ULONG_MAX on actual overflow... > > Found by Linux Verification Center (linuxtesting.org) with the Svace static > analysis tool. > > Signed-off-by: Sergey Shtylyov <s.shtylyov@omp.ru> > Cc: stable@vger.kernel.org > > --- > The patch is against the master branch of Trond Myklebust's linux-nfs.git repo. > > Changes in version 2: > - made use of check_mul_overflow() instead of mul_u32_u32(); > - capped the multiplication result at ULONG_MAX instead of returning -ERANGE, > keeping nfs4_set_lease_period() *void*; > - rewrote the patch description accordingly. Forgot to say that I had to adjust the patch description to make it clear that the overflow happens on 64-bit arches as well... [...] MBR, Sergey
On 7/2/25 7:47 PM, Sergey Shtylyov wrote: [...] >> The nfs_client::cl_lease_time field (as well as the jiffies variable it's >> used with) is declared as *unsigned long*, which is 32-bit type on 32-bit >> arches and 64-bit type on 64-bit arches. When nfs4_set_lease_period() that >> sets nfs_client::cl_lease_time is called, 32-bit nfs_fsinfo::lease_time >> field is multiplied by HZ -- that might overflow before being implicitly >> cast to *unsigned long*. Actually, there's no need to multiply by HZ at all >> the call sites of nfs4_set_lease_period() -- it makes more sense to do that >> once, inside that function, calling check_mul_overflow() and capping result >> at ULONG_MAX on actual overflow... >> >> Found by Linux Verification Center (linuxtesting.org) with the Svace static >> analysis tool. >> >> Signed-off-by: Sergey Shtylyov <s.shtylyov@omp.ru> >> Cc: stable@vger.kernel.org >> >> --- >> The patch is against the master branch of Trond Myklebust's linux-nfs.git repo. >> >> Changes in version 2: >> - made use of check_mul_overflow() instead of mul_u32_u32(); >> - capped the multiplication result at ULONG_MAX instead of returning -ERANGE, >> keeping nfs4_set_lease_period() *void*; >> - rewrote the patch description accordingly. > > Forgot to say that I had to adjust the patch description to make it clear > that the overflow happens on 64-bit arches as well... Gentle ping! Anna, do you agree with this approach? > [...] MBR, Sergey
On Fri, 2025-07-25 at 23:19 +0300, Sergey Shtylyov wrote: > On 7/2/25 7:47 PM, Sergey Shtylyov wrote: > [...] > > > > The nfs_client::cl_lease_time field (as well as the jiffies > > > variable it's > > > used with) is declared as *unsigned long*, which is 32-bit type > > > on 32-bit > > > arches and 64-bit type on 64-bit arches. When > > > nfs4_set_lease_period() that > > > sets nfs_client::cl_lease_time is called, 32-bit > > > nfs_fsinfo::lease_time > > > field is multiplied by HZ -- that might overflow before being > > > implicitly > > > cast to *unsigned long*. Actually, there's no need to multiply by > > > HZ at all > > > the call sites of nfs4_set_lease_period() -- it makes more sense > > > to do that > > > once, inside that function, calling check_mul_overflow() and > > > capping result > > > at ULONG_MAX on actual overflow... > > > > > > Found by Linux Verification Center (linuxtesting.org) with the > > > Svace static > > > analysis tool. > > > > > > Signed-off-by: Sergey Shtylyov <s.shtylyov@omp.ru> > > > Cc: stable@vger.kernel.org > > > > > > --- > > > The patch is against the master branch of Trond Myklebust's > > > linux-nfs.git repo. > > > > > > Changes in version 2: > > > - made use of check_mul_overflow() instead of mul_u32_u32(); > > > - capped the multiplication result at ULONG_MAX instead of > > > returning -ERANGE, > > > keeping nfs4_set_lease_period() *void*; > > > - rewrote the patch description accordingly. > > > > Forgot to say that I had to adjust the patch description to make > > it clear > > that the overflow happens on 64-bit arches as well... > > Gentle ping! > Anna, do you agree with this approach? > > > [...] > > MBR, Sergey > NACK. If you're going to bound check the lease time, then you can at least make it a sensible value, not a random number chosen by some peddler of silicon. 48 days (a.k.a. 2^32/HZ_1000 seconds) is not a sensible value for a NFSv4 lease, and 571 megayears (a.k.a 2^64 / HZ_1000 seconds) even less so. Just bound it at 1 hour. If some use case turns for making the value larger than that, then we can consider making the limit configurable. Even 1 hour is a long time to wait for a file lock or delegation to expire when the client that held it dies. -- Trond Myklebust Linux NFS client maintainer, Hammerspace trondmy@kernel.org, trond.myklebust@hammerspace.com
© 2016 - 2025 Red Hat, Inc.