nfsd-7.1 fixes:

Issues reported with v7.1-rc:
 - Tighten bounds checking for sunrpc cache hash tables
 - Don't report key material in the ftrace log
 
 Issues that need expedient stable backports:
 - Fix lockd's implementation of the NLM TEST procedure
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEEKLLlsBKG3yQ88j7+M2qzM29mf5cFAmoV9/kACgkQM2qzM29m
 f5fdpxAAtT3hl4wKNJsVLhFlFhG+9ABL74fwaQ06j5vTSIgXqPm12NuO5YbrkC78
 ZzV/B/YqoHLAw/t8Pgq2taBBuSeLF+H8JqjJRDYE5H2NB/KQOT8n9KTLZtac4/1V
 Dvrk3mP2h12Q//BC3pF9bU9gMR1DO/+yLt9SkH+dtqcW+dWxiyVZWtK0eESIsMfh
 IzkHNKOS0edMZmHl5O7VZSlbyq1jPA4hTZT+NCG7JwnK6YqSkpRGDiZdZIT2FBEI
 C9a9hZHoP9JAJs9fR+xzTCVsIPpNW9OO3fknR2Lg7IScssVc1GIpqjU+g1O1XSVf
 XsMfAl+pEipDBpULu46KM1TDqAKtjaAx8Z+hDmiPxSOCKWuPn/9LMdzwVrzC7Bw8
 S7ftOxUZQLHtbS8Y0eECzwK9tdfBUHN26LAJfvg4P5ZOIsFoUj0LeDryPy0r9xxb
 aEdEI8wro0O3p0krjtW2i+FJB8dtlKEu19LT6PN4MQtmv5a+DY4Hypt4Xovol0i+
 eEugZVmLYE11b52ZFfcTcXf8n89jiWg7rgRBdBdy+vQl/32dKK3SMSIB/zCZYmBc
 JZNywtri6JHeJjkohWJ4xmwrmMaDj4hNr3OqWh7bOQTHleg7igpCuy+9/LHzEF6G
 BX4DgMJ6LqcdG8p4biGr2I2NF/+MJpXO5kNAdS44wpP983T26WE=
 =onHe
 -----END PGP SIGNATURE-----

Merge tag 'nfsd-7.1-2' of git://git.kernel.org/pub/scm/linux/kernel/git/cel/linux

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()
This commit is contained in:
Linus Torvalds 2026-05-26 13:49:13 -07:00
commit eb3f4b7426
8 changed files with 66 additions and 32 deletions

View File

@ -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 *);

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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;
if (*fp)
return nlmerr;
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;
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);

View File

@ -1594,16 +1594,27 @@ int nfsd_nl_rpc_status_get_dumpit(struct sk_buff *skb,
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;
}

View File

@ -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
)
);

View File

@ -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;