Skip to content

Commit

Permalink
NFS: Fix up nfs_ctx_key_to_expire()
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Trond Myklebust committed Oct 4, 2021
1 parent 9019fb3 commit ca05cba
Show file tree
Hide file tree
Showing 3 changed files with 31 additions and 16 deletions.
4 changes: 2 additions & 2 deletions fs/nfs/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
}
Expand Down
41 changes: 28 additions & 13 deletions fs/nfs/write.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
}

/*
Expand Down
2 changes: 1 addition & 1 deletion include/linux/nfs_fs.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down

0 comments on commit ca05cba

Please sign in to comment.