From 184cefbe62627730c30282df12bcff9aae4816ea Mon Sep 17 00:00:00 2001
From: Benjamin Coddington <bcodding@redhat.com>
Date: Mon, 13 Jun 2022 09:40:06 -0400
Subject: [PATCH] NLM: Defend against file_lock changes after vfs_test_lock()
Git-commit: 184cefbe62627730c30282df12bcff9aae4816ea
Patch-mainline: v6.0-rc1
References: bsc#1217692
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 <rbergant@redhat.com>
Tested-by: Roberto Bergantinos Corpas <rbergant@redhat.com>
Signed-off-by: Benjamin Coddington <bcodding@redhat.com>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Acked-by: Ali Abdallah <ali.abdallah@suse.de>
---
fs/lockd/svc4proc.c | 4 +++-
fs/lockd/svclock.c | 10 +---------
fs/lockd/svcproc.c | 5 ++++-
include/linux/lockd/lockd.h | 1 +
4 files changed, 9 insertions(+), 11 deletions(-)
--- a/fs/lockd/svc4proc.c
+++ b/fs/lockd/svc4proc.c
@@ -84,6 +84,7 @@ __nlm4svc_proc_test(struct svc_rqst *rqs
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");
@@ -93,6 +94,7 @@ __nlm4svc_proc_test(struct svc_rqst *rqs
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)
@@ -100,7 +102,7 @@ __nlm4svc_proc_test(struct svc_rqst *rqs
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;
--- a/fs/lockd/svclock.c
+++ b/fs/lockd/svclock.c
@@ -339,7 +339,7 @@ nlmsvc_get_lockowner(struct nlm_lockowne
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;
@@ -578,7 +578,6 @@ nlmsvc_testlock(struct svc_rqst *rqstp,
{
int error;
__be32 ret;
- struct nlm_lockowner *test_owner;
dprintk("lockd: nlmsvc_testlock(%s/%ld, ty=%d, %Ld-%Ld)\n",
locks_inode(file->f_file)->i_sb->s_id,
@@ -592,9 +591,6 @@ nlmsvc_testlock(struct svc_rqst *rqstp,
goto out;
}
- /* If there's a conflicting lock, remember to clean up the test lock */
- test_owner = (struct nlm_lockowner *)lock->fl.fl_owner;
-
error = vfs_test_lock(file->f_file, &lock->fl);
if (error) {
/* We can't currently deal with deferred test requests */
@@ -622,10 +618,6 @@ nlmsvc_testlock(struct svc_rqst *rqstp,
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;
--- a/fs/lockd/svcproc.c
+++ b/fs/lockd/svcproc.c
@@ -114,6 +114,7 @@ __nlmsvc_proc_test(struct svc_rqst *rqst
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");
@@ -123,6 +124,8 @@ __nlmsvc_proc_test(struct svc_rqst *rqst
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)
@@ -131,7 +134,7 @@ __nlmsvc_proc_test(struct svc_rqst *rqst
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;
--- a/include/linux/lockd/lockd.h
+++ b/include/linux/lockd/lockd.h
@@ -288,6 +288,7 @@ void nlmsvc_locks_init_private(struct
__be32 nlm_lookup_file(struct svc_rqst *, struct nlm_file **,
struct nfs_fh *);
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 *);