OSDN Git Service

NFS: Fix up nfs_ctx_key_to_expire()
authorTrond Myklebust <trond.myklebust@hammerspace.com>
Sat, 10 Jul 2021 22:07:14 +0000 (18:07 -0400)
committerTrond Myklebust <trond.myklebust@hammerspace.com>
Mon, 4 Oct 2021 00:49:05 +0000 (20:49 -0400)
If the cached credential exists but doesn't have any expiration callback
then exit early.
Fix up atomicity issues when replacing the credential with a new one
since the existing code could lead to refcount leaks.

Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
fs/nfs/inode.c
fs/nfs/write.c
include/linux/nfs_fs.h

index 853213b..4f45281 100644 (file)
@@ -1024,7 +1024,7 @@ struct nfs_open_context *alloc_nfs_open_context(struct dentry *dentry,
                ctx->cred = get_cred(filp->f_cred);
        else
                ctx->cred = get_current_cred();
-       ctx->ll_cred = NULL;
+       rcu_assign_pointer(ctx->ll_cred, NULL);
        ctx->state = NULL;
        ctx->mode = f_mode;
        ctx->flags = 0;
@@ -1063,7 +1063,7 @@ static void __put_nfs_open_context(struct nfs_open_context *ctx, int is_sync)
        put_cred(ctx->cred);
        dput(ctx->dentry);
        nfs_sb_deactive(sb);
-       put_rpccred(ctx->ll_cred);
+       put_rpccred(rcu_dereference_protected(ctx->ll_cred, 1));
        kfree(ctx->mdsthreshold);
        kfree_rcu(ctx, rcu_head);
 }
index eae9bf1..773ea2c 100644 (file)
@@ -1246,7 +1246,7 @@ nfs_key_timeout_notify(struct file *filp, struct inode *inode)
        struct nfs_open_context *ctx = nfs_file_open_context(filp);
 
        if (nfs_ctx_key_to_expire(ctx, inode) &&
-           !ctx->ll_cred)
+           !rcu_access_pointer(ctx->ll_cred))
                /* Already expired! */
                return -EACCES;
        return 0;
@@ -1258,23 +1258,38 @@ nfs_key_timeout_notify(struct file *filp, struct inode *inode)
 bool nfs_ctx_key_to_expire(struct nfs_open_context *ctx, struct inode *inode)
 {
        struct rpc_auth *auth = NFS_SERVER(inode)->client->cl_auth;
-       struct rpc_cred *cred = ctx->ll_cred;
+       struct rpc_cred *cred, *new, *old = NULL;
        struct auth_cred acred = {
                .cred = ctx->cred,
        };
+       bool ret = false;
 
-       if (cred && !cred->cr_ops->crmatch(&acred, cred, 0)) {
-               put_rpccred(cred);
-               ctx->ll_cred = NULL;
-               cred = NULL;
-       }
-       if (!cred)
-               cred = auth->au_ops->lookup_cred(auth, &acred, 0);
-       if (!cred || IS_ERR(cred))
+       rcu_read_lock();
+       cred = rcu_dereference(ctx->ll_cred);
+       if (cred && !(cred->cr_ops->crkey_timeout &&
+                     cred->cr_ops->crkey_timeout(cred)))
+               goto out;
+       rcu_read_unlock();
+
+       new = auth->au_ops->lookup_cred(auth, &acred, 0);
+       if (new == cred) {
+               put_rpccred(new);
                return true;
-       ctx->ll_cred = cred;
-       return !!(cred->cr_ops->crkey_timeout &&
-                 cred->cr_ops->crkey_timeout(cred));
+       }
+       if (IS_ERR_OR_NULL(new)) {
+               new = NULL;
+               ret = true;
+       } else if (new->cr_ops->crkey_timeout &&
+                  new->cr_ops->crkey_timeout(new))
+               ret = true;
+
+       rcu_read_lock();
+       old = rcu_dereference_protected(xchg(&ctx->ll_cred,
+                                            RCU_INITIALIZER(new)), 1);
+out:
+       rcu_read_unlock();
+       put_rpccred(old);
+       return ret;
 }
 
 /*
index b9a8b92..9b75448 100644 (file)
@@ -81,7 +81,7 @@ struct nfs_open_context {
        fl_owner_t flock_owner;
        struct dentry *dentry;
        const struct cred *cred;
-       struct rpc_cred *ll_cred;       /* low-level cred - use to check for expiry */
+       struct rpc_cred __rcu *ll_cred; /* low-level cred - use to check for expiry */
        struct nfs4_state *state;
        fmode_t mode;