Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

lockd: fix vfs_test_lock() calls

Usage of vfs_test_lock() is somewhat confused. Documentation suggests
it is given a "lock" but this is not the case. It is given a struct
file_lock which contains some details of the sort of lock it should be
looking for.

In particular passing a "file_lock" containing fl_lmops or fl_ops is
meaningless and possibly confusing.

This is particularly problematic in lockd. nlmsvc_testlock() receives
an initialised "file_lock" from xdr-decode, including manager ops and an
owner. It then mistakenly passes this to vfs_test_lock() which might
replace the owner and the ops. This can lead to confusion when freeing
the lock.

The primary role of the 'struct file_lock' passed to vfs_test_lock() is
to report a conflicting lock that was found, so it makes more sense for
nlmsvc_testlock() to pass "conflock", which it uses for returning the
conflicting lock.

With this change, freeing of the lock is not confused and code in
__nlm4svc_proc_test() and __nlmsvc_proc_test() can be simplified.

Documentation for vfs_test_lock() is improved to reflect its real
purpose, and a WARN_ON_ONCE() is added to avoid a similar problem in the
future.

Reported-by: Olga Kornievskaia <okorniev@redhat.com>
Closes: https://lore.kernel.org/all/20251021130506.45065-1-okorniev@redhat.com
Signed-off-by: NeilBrown <neil@brown.name>
Fixes: 20fa19027286 ("nfs: add export operations")
Cc: stable@vger.kernel.org
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>

authored by

NeilBrown and committed by
Chuck Lever
a49a2a1b 913f7cf7

+24 -18
+1 -3
fs/lockd/svc4proc.c
··· 97 97 struct nlm_args *argp = rqstp->rq_argp; 98 98 struct nlm_host *host; 99 99 struct nlm_file *file; 100 - struct nlm_lockowner *test_owner; 101 100 __be32 rc = rpc_success; 102 101 103 102 dprintk("lockd: TEST4 called\n"); ··· 106 107 if ((resp->status = nlm4svc_retrieve_args(rqstp, argp, &host, &file))) 107 108 return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success; 108 109 109 - test_owner = argp->lock.fl.c.flc_owner; 110 110 /* Now check for conflicting locks */ 111 111 resp->status = nlmsvc_testlock(rqstp, file, host, &argp->lock, 112 112 &resp->lock); ··· 114 116 else 115 117 dprintk("lockd: TEST4 status %d\n", ntohl(resp->status)); 116 118 117 - nlmsvc_put_lockowner(test_owner); 119 + nlmsvc_release_lockowner(&argp->lock); 118 120 nlmsvc_release_host(host); 119 121 nlm_release_file(file); 120 122 return rc;
+12 -9
fs/lockd/svclock.c
··· 633 633 } 634 634 635 635 mode = lock_to_openmode(&lock->fl); 636 - error = vfs_test_lock(file->f_file[mode], &lock->fl); 636 + locks_init_lock(&conflock->fl); 637 + /* vfs_test_lock only uses start, end, and owner, but tests flc_file */ 638 + conflock->fl.c.flc_file = lock->fl.c.flc_file; 639 + conflock->fl.fl_start = lock->fl.fl_start; 640 + conflock->fl.fl_end = lock->fl.fl_end; 641 + conflock->fl.c.flc_owner = lock->fl.c.flc_owner; 642 + error = vfs_test_lock(file->f_file[mode], &conflock->fl); 637 643 if (error) { 638 644 /* We can't currently deal with deferred test requests */ 639 645 if (error == FILE_LOCK_DEFERRED) ··· 649 643 goto out; 650 644 } 651 645 652 - if (lock->fl.c.flc_type == F_UNLCK) { 646 + if (conflock->fl.c.flc_type == F_UNLCK) { 653 647 ret = nlm_granted; 654 648 goto out; 655 649 } 656 650 657 651 dprintk("lockd: conflicting lock(ty=%d, %Ld-%Ld)\n", 658 - lock->fl.c.flc_type, (long long)lock->fl.fl_start, 659 - (long long)lock->fl.fl_end); 652 + conflock->fl.c.flc_type, (long long)conflock->fl.fl_start, 653 + (long long)conflock->fl.fl_end); 660 654 conflock->caller = "somehost"; /* FIXME */ 661 655 conflock->len = strlen(conflock->caller); 662 656 conflock->oh.len = 0; /* don't return OH info */ 663 - conflock->svid = lock->fl.c.flc_pid; 664 - conflock->fl.c.flc_type = lock->fl.c.flc_type; 665 - conflock->fl.fl_start = lock->fl.fl_start; 666 - conflock->fl.fl_end = lock->fl.fl_end; 667 - locks_release_private(&lock->fl); 657 + conflock->svid = conflock->fl.c.flc_pid; 658 + locks_release_private(&conflock->fl); 668 659 669 660 ret = nlm_lck_denied; 670 661 out:
+1 -4
fs/lockd/svcproc.c
··· 117 117 struct nlm_args *argp = rqstp->rq_argp; 118 118 struct nlm_host *host; 119 119 struct nlm_file *file; 120 - struct nlm_lockowner *test_owner; 121 120 __be32 rc = rpc_success; 122 121 123 122 dprintk("lockd: TEST called\n"); ··· 125 126 /* Obtain client and file */ 126 127 if ((resp->status = nlmsvc_retrieve_args(rqstp, argp, &host, &file))) 127 128 return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success; 128 - 129 - test_owner = argp->lock.fl.c.flc_owner; 130 129 131 130 /* Now check for conflicting locks */ 132 131 resp->status = cast_status(nlmsvc_testlock(rqstp, file, host, ··· 135 138 dprintk("lockd: TEST status %d vers %d\n", 136 139 ntohl(resp->status), rqstp->rq_vers); 137 140 138 - nlmsvc_put_lockowner(test_owner); 141 + nlmsvc_release_lockowner(&argp->lock); 139 142 nlmsvc_release_host(host); 140 143 nlm_release_file(file); 141 144 return rc;
+10 -2
fs/locks.c
··· 2185 2185 /** 2186 2186 * vfs_test_lock - test file byte range lock 2187 2187 * @filp: The file to test lock for 2188 - * @fl: The lock to test; also used to hold result 2188 + * @fl: The byte-range in the file to test; also used to hold result 2189 2189 * 2190 + * On entry, @fl does not contain a lock, but identifies a range (fl_start, fl_end) 2191 + * in the file (c.flc_file), and an owner (c.flc_owner) for whom existing locks 2192 + * should be ignored. c.flc_type and c.flc_flags are ignored. 2193 + * Both fl_lmops and fl_ops in @fl must be NULL. 2190 2194 * Returns -ERRNO on failure. Indicates presence of conflicting lock by 2191 - * setting conf->fl_type to something other than F_UNLCK. 2195 + * setting fl->fl_type to something other than F_UNLCK. 2196 + * 2197 + * If vfs_test_lock() does find a lock and return it, the caller must 2198 + * use locks_free_lock() or locks_release_private() on the returned lock. 2192 2199 */ 2193 2200 int vfs_test_lock(struct file *filp, struct file_lock *fl) 2194 2201 { 2202 + WARN_ON_ONCE(fl->fl_ops || fl->fl_lmops); 2195 2203 WARN_ON_ONCE(filp != fl->c.flc_file); 2196 2204 if (filp->f_op->lock) 2197 2205 return filp->f_op->lock(filp, F_GETLK, fl);