summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-05-26 13:49:13 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2026-05-26 13:49:13 -0700
commiteb3f4b7426cfd2b79d65b7d37155480b32259a11 (patch)
treeadb9ed5b086c5c501915884cb8a26247b2c4f1c5
parente909cedf6800ef493063f18a089f3632817a8c2d (diff)
parent0b474240327cebeff08ad429e8ed3cfc6c8ee816 (diff)
Merge tag 'nfsd-7.1-2' of git://git.kernel.org/pub/scm/linux/kernel/git/cel/linuxHEADmaster
Pull nfsd fixes from Chuck Lever: "Regressions: - Tighten bounds checking for sunrpc cache hash tables - Don't report key material in the ftrace log Stable fix: - Fix lockd's implementation of the NLM TEST procedure" * tag 'nfsd-7.1-2' of git://git.kernel.org/pub/scm/linux/kernel/git/cel/linux: lockd: fix TEST handling when not all permissions are available. NFSD: Report whether fh_key was actually updated sunrpc: prevent out-of-bounds read in __cache_seq_start()
-rw-r--r--fs/lockd/lockd.h2
-rw-r--r--fs/lockd/svc4proc.c9
-rw-r--r--fs/lockd/svclock.c4
-rw-r--r--fs/lockd/svcproc.c15
-rw-r--r--fs/lockd/svcsubs.c31
-rw-r--r--fs/nfsd/nfsctl.c18
-rw-r--r--fs/nfsd/trace.h16
-rw-r--r--net/sunrpc/cache.c3
8 files changed, 66 insertions, 32 deletions
diff --git a/fs/lockd/lockd.h b/fs/lockd/lockd.h
index a7c85ab6d4b5..1db6cb352542 100644
--- a/fs/lockd/lockd.h
+++ b/fs/lockd/lockd.h
@@ -332,7 +332,7 @@ int nlmsvc_dispatch(struct svc_rqst *rqstp);
* File handling for the server personality
*/
__be32 nlm_lookup_file(struct svc_rqst *, struct nlm_file **,
- struct nlm_lock *);
+ struct nlm_lock *, int);
void nlm_release_file(struct nlm_file *);
void nlmsvc_put_lockowner(struct nlm_lockowner *);
void nlmsvc_release_lockowner(struct nlm_lock *);
diff --git a/fs/lockd/svc4proc.c b/fs/lockd/svc4proc.c
index 5de41e249534..41cab858de57 100644
--- a/fs/lockd/svc4proc.c
+++ b/fs/lockd/svc4proc.c
@@ -146,8 +146,11 @@ nlm4svc_lookup_file(struct svc_rqst *rqstp, struct nlm_host *host,
struct nlm_lock *lock, struct nlm_file **filp,
struct nlm4_lock *xdr_lock, unsigned char type)
{
+ bool is_test = (rqstp->rq_proc == NLMPROC4_TEST ||
+ rqstp->rq_proc == NLMPROC4_TEST_MSG);
struct file_lock *fl = &lock->fl;
struct nlm_file *file = NULL;
+ int mode;
__be32 error;
if (xdr_lock->fh.len > NFS_MAXFHSIZE)
@@ -170,7 +173,8 @@ nlm4svc_lookup_file(struct svc_rqst *rqstp, struct nlm_host *host,
fl->c.flc_type = type;
lockd_set_file_lock_range4(fl, lock->lock_start, lock->lock_len);
- error = nlm_lookup_file(rqstp, &file, lock);
+ mode = is_test ? O_RDWR : lock_to_openmode(fl);
+ error = nlm_lookup_file(rqstp, &file, lock, mode);
switch (error) {
case nlm_granted:
break;
@@ -184,7 +188,8 @@ nlm4svc_lookup_file(struct svc_rqst *rqstp, struct nlm_host *host,
*filp = file;
fl->c.flc_flags = FL_POSIX;
- fl->c.flc_file = file->f_file[lock_to_openmode(fl)];
+ fl->c.flc_file = is_test ? nlmsvc_file_file(file)
+ : file->f_file[mode];
fl->c.flc_pid = current->tgid;
fl->fl_lmops = &nlmsvc_lock_operations;
nlmsvc_locks_init_private(fl, host, (pid_t)lock->svid);
diff --git a/fs/lockd/svclock.c b/fs/lockd/svclock.c
index b98b1d0ada35..f4520149d6d7 100644
--- a/fs/lockd/svclock.c
+++ b/fs/lockd/svclock.c
@@ -613,7 +613,6 @@ nlmsvc_testlock(struct svc_rqst *rqstp, struct nlm_file *file,
struct nlm_lock *conflock)
{
int error;
- int mode;
__be32 ret;
dprintk("lockd: nlmsvc_testlock(%s/%llu, ty=%d, %Ld-%Ld)\n",
@@ -631,14 +630,13 @@ nlmsvc_testlock(struct svc_rqst *rqstp, struct nlm_file *file,
goto out;
}
- mode = lock_to_openmode(&lock->fl);
locks_init_lock(&conflock->fl);
/* vfs_test_lock only uses start, end, and owner, but tests flc_file */
conflock->fl.c.flc_file = lock->fl.c.flc_file;
conflock->fl.fl_start = lock->fl.fl_start;
conflock->fl.fl_end = lock->fl.fl_end;
conflock->fl.c.flc_owner = lock->fl.c.flc_owner;
- error = vfs_test_lock(file->f_file[mode], &conflock->fl);
+ error = vfs_test_lock(lock->fl.c.flc_file, &conflock->fl);
if (error) {
ret = nlm_lck_denied_nolocks;
goto out;
diff --git a/fs/lockd/svcproc.c b/fs/lockd/svcproc.c
index 749abf8886ba..c0a3487719e2 100644
--- a/fs/lockd/svcproc.c
+++ b/fs/lockd/svcproc.c
@@ -68,6 +68,8 @@ nlmsvc_retrieve_args(struct svc_rqst *rqstp, struct nlm_args *argp,
struct nlm_host *host = NULL;
struct nlm_file *file = NULL;
struct nlm_lock *lock = &argp->lock;
+ bool is_test = (rqstp->rq_proc == NLMPROC_TEST ||
+ rqstp->rq_proc == NLMPROC_TEST_MSG);
int mode;
__be32 error = 0;
@@ -83,15 +85,22 @@ nlmsvc_retrieve_args(struct svc_rqst *rqstp, struct nlm_args *argp,
/* Obtain file pointer. Not used by FREE_ALL call. */
if (filp != NULL) {
- error = cast_status(nlm_lookup_file(rqstp, &file, lock));
+ mode = lock_to_openmode(&lock->fl);
+
+ if (is_test)
+ mode = O_RDWR;
+
+ error = cast_status(nlm_lookup_file(rqstp, &file, lock, mode));
if (error != 0)
goto no_locks;
*filp = file;
/* Set up the missing parts of the file_lock structure */
- mode = lock_to_openmode(&lock->fl);
lock->fl.c.flc_flags = FL_POSIX;
- lock->fl.c.flc_file = file->f_file[mode];
+ if (is_test)
+ lock->fl.c.flc_file = nlmsvc_file_file(file);
+ else
+ lock->fl.c.flc_file = file->f_file[mode];
lock->fl.c.flc_pid = current->tgid;
lock->fl.fl_lmops = &nlmsvc_lock_operations;
nlmsvc_locks_init_private(&lock->fl, host, (pid_t)lock->svid);
diff --git a/fs/lockd/svcsubs.c b/fs/lockd/svcsubs.c
index 344e6c187cde..9da9d6e0b42e 100644
--- a/fs/lockd/svcsubs.c
+++ b/fs/lockd/svcsubs.c
@@ -83,23 +83,36 @@ int lock_to_openmode(struct file_lock *lock)
*
* We have to make sure we have the right credential to open
* the file.
+ *
+ * @mode is O_RDONLY, O_WRONLY, or O_RDWR. O_RDWR means success
+ * is achieved with EITHER O_RDONLY or O_WRONLY; it does not
+ * require both.
*/
static __be32 nlm_do_fopen(struct svc_rqst *rqstp,
struct nlm_file *file, int mode)
{
- struct file **fp = &file->f_file[mode];
- __be32 nlmerr = nlm_granted;
+ __be32 nlmerr = nlm__int__failed;
+ __be32 deferred = 0;
int error;
+ int m;
+
+ for (m = O_RDONLY; m <= O_WRONLY; m++) {
+ struct file **fp = &file->f_file[m];
+
+ if (mode != O_RDWR && mode != m)
+ continue;
+ if (*fp)
+ return nlm_granted;
- if (*fp)
- return nlmerr;
+ error = nlmsvc_ops->fopen(rqstp, &file->f_handle, fp, m);
+ if (!error)
+ return nlm_granted;
- error = nlmsvc_ops->fopen(rqstp, &file->f_handle, fp, mode);
- if (error) {
dprintk("lockd: open failed (errno %d)\n", error);
switch (error) {
case -EWOULDBLOCK:
nlmerr = nlm__int__drop_reply;
+ deferred = nlmerr;
break;
case -ESTALE:
nlmerr = nlm__int__stale_fh;
@@ -110,7 +123,7 @@ static __be32 nlm_do_fopen(struct svc_rqst *rqstp,
}
}
- return nlmerr;
+ return deferred ? deferred : nlmerr;
}
/*
@@ -119,17 +132,15 @@ static __be32 nlm_do_fopen(struct svc_rqst *rqstp,
*/
__be32
nlm_lookup_file(struct svc_rqst *rqstp, struct nlm_file **result,
- struct nlm_lock *lock)
+ struct nlm_lock *lock, int mode)
{
struct nlm_file *file;
unsigned int hash;
__be32 nfserr;
- int mode;
nlm_debug_print_fh("nlm_lookup_file", &lock->fh);
hash = file_hash(&lock->fh);
- mode = lock_to_openmode(&lock->fl);
/* Lock file table */
mutex_lock(&nlm_file_mutex);
diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c
index 39e7012a60d8..04e3954d54bd 100644
--- a/fs/nfsd/nfsctl.c
+++ b/fs/nfsd/nfsctl.c
@@ -1594,16 +1594,27 @@ out_unlock:
static int nfsd_nl_fh_key_set(const struct nlattr *attr, struct nfsd_net *nn)
{
siphash_key_t *fh_key = nn->fh_key;
+ u64 k0, k1;
+ bool changed;
+
+ k0 = get_unaligned_le64(nla_data(attr));
+ k1 = get_unaligned_le64(nla_data(attr) + 8);
if (!fh_key) {
fh_key = kmalloc(sizeof(siphash_key_t), GFP_KERNEL);
- if (!fh_key)
+ if (!fh_key) {
+ trace_nfsd_ctl_fh_key_set(false, -ENOMEM);
return -ENOMEM;
+ }
nn->fh_key = fh_key;
+ changed = true;
+ } else {
+ changed = fh_key->key[0] != k0 || fh_key->key[1] != k1;
}
- fh_key->key[0] = get_unaligned_le64(nla_data(attr));
- fh_key->key[1] = get_unaligned_le64(nla_data(attr) + 8);
+ fh_key->key[0] = k0;
+ fh_key->key[1] = k1;
+ trace_nfsd_ctl_fh_key_set(changed, 0);
return 0;
}
@@ -1682,7 +1693,6 @@ int nfsd_nl_threads_set_doit(struct sk_buff *skb, struct genl_info *info)
attr = info->attrs[NFSD_A_SERVER_FH_KEY];
if (attr) {
ret = nfsd_nl_fh_key_set(attr, nn);
- trace_nfsd_ctl_fh_key_set((const char *)nn->fh_key, ret);
if (ret)
goto out_unlock;
}
diff --git a/fs/nfsd/trace.h b/fs/nfsd/trace.h
index 5ad38f50836d..b631a472222b 100644
--- a/fs/nfsd/trace.h
+++ b/fs/nfsd/trace.h
@@ -2243,23 +2243,21 @@ TRACE_EVENT(nfsd_end_grace,
TRACE_EVENT(nfsd_ctl_fh_key_set,
TP_PROTO(
- const char *key,
+ bool changed,
int result
),
- TP_ARGS(key, result),
+ TP_ARGS(changed, result),
TP_STRUCT__entry(
- __field(u32, key_hash)
+ __field(bool, changed)
__field(int, result)
),
TP_fast_assign(
- if (key)
- __entry->key_hash = ~crc32_le(0xFFFFFFFF, key, 16);
- else
- __entry->key_hash = 0;
+ __entry->changed = changed;
__entry->result = result;
),
- TP_printk("key=0x%08x result=%d",
- __entry->key_hash, __entry->result
+ TP_printk("key %s, result=%d",
+ __entry->changed ? "updated" : "unmodified",
+ __entry->result
)
);
diff --git a/net/sunrpc/cache.c b/net/sunrpc/cache.c
index b5474ce534fb..27dd6b58b8ff 100644
--- a/net/sunrpc/cache.c
+++ b/net/sunrpc/cache.c
@@ -1348,6 +1348,9 @@ static void *__cache_seq_start(struct seq_file *m, loff_t *pos)
hash = n >> 32;
entry = n & ((1LL<<32) - 1);
+ if (hash >= cd->hash_size)
+ return NULL;
+
hlist_for_each_entry_rcu(ch, &cd->hash_table[hash], cache_list)
if (!entry--)
return ch;