Skip to content

Commit

Permalink
NLM: Defend against file_lock changes after vfs_test_lock()
Browse files Browse the repository at this point in the history
jira LE-1907
Rebuild_History Non-Buildable kernel-rt-5.14.0-284.30.1.rt14.315.el9_2
commit-author Benjamin Coddington <[email protected]>
commit 184cefb

Instead of trusting that struct file_lock returns completely unchanged
after vfs_test_lock() when there's no conflicting lock, stash away our
nlm_lockowner reference so we can properly release it for all cases.

This defends against another file_lock implementation overwriting fl_owner
when the return type is F_UNLCK.

	Reported-by: Roberto Bergantinos Corpas <[email protected]>
	Tested-by: Roberto Bergantinos Corpas <[email protected]>
	Signed-off-by: Benjamin Coddington <[email protected]>
	Signed-off-by: Chuck Lever <[email protected]>
(cherry picked from commit 184cefb)
	Signed-off-by: Jonathan Maple <[email protected]>
  • Loading branch information
PlaidCat committed Sep 13, 2024
1 parent ec08e43 commit 4acb4df
Show file tree
Hide file tree
Showing 4 changed files with 9 additions and 11 deletions.
4 changes: 3 additions & 1 deletion fs/lockd/svc4proc.c
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ __nlm4svc_proc_test(struct svc_rqst *rqstp, struct nlm_res *resp)
struct nlm_args *argp = rqstp->rq_argp;
struct nlm_host *host;
struct nlm_file *file;
struct nlm_lockowner *test_owner;
__be32 rc = rpc_success;

dprintk("lockd: TEST4 called\n");
Expand All @@ -96,14 +97,15 @@ __nlm4svc_proc_test(struct svc_rqst *rqstp, struct nlm_res *resp)
if ((resp->status = nlm4svc_retrieve_args(rqstp, argp, &host, &file)))
return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success;

test_owner = argp->lock.fl.fl_owner;
/* Now check for conflicting locks */
resp->status = nlmsvc_testlock(rqstp, file, host, &argp->lock, &resp->lock, &resp->cookie);
if (resp->status == nlm_drop_reply)
rc = rpc_drop_reply;
else
dprintk("lockd: TEST4 status %d\n", ntohl(resp->status));

nlmsvc_release_lockowner(&argp->lock);
nlmsvc_put_lockowner(test_owner);
nlmsvc_release_host(host);
nlm_release_file(file);
return rc;
Expand Down
10 changes: 1 addition & 9 deletions fs/lockd/svclock.c
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,7 @@ nlmsvc_get_lockowner(struct nlm_lockowner *lockowner)
return lockowner;
}

static void nlmsvc_put_lockowner(struct nlm_lockowner *lockowner)
void nlmsvc_put_lockowner(struct nlm_lockowner *lockowner)
{
if (!refcount_dec_and_lock(&lockowner->count, &lockowner->host->h_lock))
return;
Expand Down Expand Up @@ -590,7 +590,6 @@ nlmsvc_testlock(struct svc_rqst *rqstp, struct nlm_file *file,
int error;
int mode;
__be32 ret;
struct nlm_lockowner *test_owner;

dprintk("lockd: nlmsvc_testlock(%s/%ld, ty=%d, %Ld-%Ld)\n",
nlmsvc_file_inode(file)->i_sb->s_id,
Expand All @@ -604,9 +603,6 @@ nlmsvc_testlock(struct svc_rqst *rqstp, struct nlm_file *file,
goto out;
}

/* If there's a conflicting lock, remember to clean up the test lock */
test_owner = (struct nlm_lockowner *)lock->fl.fl_owner;

mode = lock_to_openmode(&lock->fl);
error = vfs_test_lock(file->f_file[mode], &lock->fl);
if (error) {
Expand Down Expand Up @@ -635,10 +631,6 @@ nlmsvc_testlock(struct svc_rqst *rqstp, struct nlm_file *file,
conflock->fl.fl_end = lock->fl.fl_end;
locks_release_private(&lock->fl);

/* Clean up the test lock */
lock->fl.fl_owner = NULL;
nlmsvc_put_lockowner(test_owner);

ret = nlm_lck_denied;
out:
return ret;
Expand Down
5 changes: 4 additions & 1 deletion fs/lockd/svcproc.c
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ __nlmsvc_proc_test(struct svc_rqst *rqstp, struct nlm_res *resp)
struct nlm_args *argp = rqstp->rq_argp;
struct nlm_host *host;
struct nlm_file *file;
struct nlm_lockowner *test_owner;
__be32 rc = rpc_success;

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

test_owner = argp->lock.fl.fl_owner;

/* Now check for conflicting locks */
resp->status = cast_status(nlmsvc_testlock(rqstp, file, host, &argp->lock, &resp->lock, &resp->cookie));
if (resp->status == nlm_drop_reply)
Expand All @@ -133,7 +136,7 @@ __nlmsvc_proc_test(struct svc_rqst *rqstp, struct nlm_res *resp)
dprintk("lockd: TEST status %d vers %d\n",
ntohl(resp->status), rqstp->rq_vers);

nlmsvc_release_lockowner(&argp->lock);
nlmsvc_put_lockowner(test_owner);
nlmsvc_release_host(host);
nlm_release_file(file);
return rc;
Expand Down
1 change: 1 addition & 0 deletions include/linux/lockd/lockd.h
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,7 @@ void nlmsvc_locks_init_private(struct file_lock *, struct nlm_host *, pid_t);
__be32 nlm_lookup_file(struct svc_rqst *, struct nlm_file **,
struct nlm_lock *);
void nlm_release_file(struct nlm_file *);
void nlmsvc_put_lockowner(struct nlm_lockowner *);
void nlmsvc_release_lockowner(struct nlm_lock *);
void nlmsvc_mark_resources(struct net *);
void nlmsvc_free_host_resources(struct nlm_host *);
Expand Down

0 comments on commit 4acb4df

Please sign in to comment.