Skip to content

Commit a49a2a1

Browse files
neilbrownchucklever
authored andcommitted
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 <[email protected]> Closes: https://lore.kernel.org/all/[email protected] Signed-off-by: NeilBrown <[email protected]> Fixes: 20fa190 ("nfs: add export operations") Cc: [email protected] Reviewed-by: Jeff Layton <[email protected]> Signed-off-by: Chuck Lever <[email protected]>
1 parent 913f7cf commit a49a2a1

4 files changed

Lines changed: 24 additions & 18 deletions

File tree

fs/lockd/svc4proc.c

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,6 @@ __nlm4svc_proc_test(struct svc_rqst *rqstp, struct nlm_res *resp)
9797
struct nlm_args *argp = rqstp->rq_argp;
9898
struct nlm_host *host;
9999
struct nlm_file *file;
100-
struct nlm_lockowner *test_owner;
101100
__be32 rc = rpc_success;
102101

103102
dprintk("lockd: TEST4 called\n");
@@ -107,7 +106,6 @@ __nlm4svc_proc_test(struct svc_rqst *rqstp, struct nlm_res *resp)
107106
if ((resp->status = nlm4svc_retrieve_args(rqstp, argp, &host, &file)))
108107
return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success;
109108

110-
test_owner = argp->lock.fl.c.flc_owner;
111109
/* Now check for conflicting locks */
112110
resp->status = nlmsvc_testlock(rqstp, file, host, &argp->lock,
113111
&resp->lock);
@@ -116,7 +114,7 @@ __nlm4svc_proc_test(struct svc_rqst *rqstp, struct nlm_res *resp)
116114
else
117115
dprintk("lockd: TEST4 status %d\n", ntohl(resp->status));
118116

119-
nlmsvc_put_lockowner(test_owner);
117+
nlmsvc_release_lockowner(&argp->lock);
120118
nlmsvc_release_host(host);
121119
nlm_release_file(file);
122120
return rc;

fs/lockd/svclock.c

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -633,7 +633,13 @@ nlmsvc_testlock(struct svc_rqst *rqstp, struct nlm_file *file,
633633
}
634634

635635
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);
637643
if (error) {
638644
/* We can't currently deal with deferred test requests */
639645
if (error == FILE_LOCK_DEFERRED)
@@ -643,22 +649,19 @@ nlmsvc_testlock(struct svc_rqst *rqstp, struct nlm_file *file,
643649
goto out;
644650
}
645651

646-
if (lock->fl.c.flc_type == F_UNLCK) {
652+
if (conflock->fl.c.flc_type == F_UNLCK) {
647653
ret = nlm_granted;
648654
goto out;
649655
}
650656

651657
dprintk("lockd: conflicting lock(ty=%d, %Ld-%Ld)\n",
652-
lock->fl.c.flc_type, (long long)lock->fl.fl_start,
653-
(long long)lock->fl.fl_end);
658+
conflock->fl.c.flc_type, (long long)conflock->fl.fl_start,
659+
(long long)conflock->fl.fl_end);
654660
conflock->caller = "somehost"; /* FIXME */
655661
conflock->len = strlen(conflock->caller);
656662
conflock->oh.len = 0; /* don't return OH info */
657-
conflock->svid = lock->fl.c.flc_pid;
658-
conflock->fl.c.flc_type = lock->fl.c.flc_type;
659-
conflock->fl.fl_start = lock->fl.fl_start;
660-
conflock->fl.fl_end = lock->fl.fl_end;
661-
locks_release_private(&lock->fl);
663+
conflock->svid = conflock->fl.c.flc_pid;
664+
locks_release_private(&conflock->fl);
662665

663666
ret = nlm_lck_denied;
664667
out:

fs/lockd/svcproc.c

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,6 @@ __nlmsvc_proc_test(struct svc_rqst *rqstp, struct nlm_res *resp)
117117
struct nlm_args *argp = rqstp->rq_argp;
118118
struct nlm_host *host;
119119
struct nlm_file *file;
120-
struct nlm_lockowner *test_owner;
121120
__be32 rc = rpc_success;
122121

123122
dprintk("lockd: TEST called\n");
@@ -127,8 +126,6 @@ __nlmsvc_proc_test(struct svc_rqst *rqstp, struct nlm_res *resp)
127126
if ((resp->status = nlmsvc_retrieve_args(rqstp, argp, &host, &file)))
128127
return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success;
129128

130-
test_owner = argp->lock.fl.c.flc_owner;
131-
132129
/* Now check for conflicting locks */
133130
resp->status = cast_status(nlmsvc_testlock(rqstp, file, host,
134131
&argp->lock, &resp->lock));
@@ -138,7 +135,7 @@ __nlmsvc_proc_test(struct svc_rqst *rqstp, struct nlm_res *resp)
138135
dprintk("lockd: TEST status %d vers %d\n",
139136
ntohl(resp->status), rqstp->rq_vers);
140137

141-
nlmsvc_put_lockowner(test_owner);
138+
nlmsvc_release_lockowner(&argp->lock);
142139
nlmsvc_release_host(host);
143140
nlm_release_file(file);
144141
return rc;

fs/locks.c

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2185,13 +2185,21 @@ SYSCALL_DEFINE2(flock, unsigned int, fd, unsigned int, cmd)
21852185
/**
21862186
* vfs_test_lock - test file byte range lock
21872187
* @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
21892189
*
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.
21902194
* 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.
21922199
*/
21932200
int vfs_test_lock(struct file *filp, struct file_lock *fl)
21942201
{
2202+
WARN_ON_ONCE(fl->fl_ops || fl->fl_lmops);
21952203
WARN_ON_ONCE(filp != fl->c.flc_file);
21962204
if (filp->f_op->lock)
21972205
return filp->f_op->lock(filp, F_GETLK, fl);

0 commit comments

Comments
 (0)