Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit ca05cba

Browse files
author
Trond Myklebust
committed
NFS: Fix up nfs_ctx_key_to_expire()
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 <[email protected]>
1 parent 9019fb3 commit ca05cba

File tree

3 files changed

+31
-16
lines changed

3 files changed

+31
-16
lines changed

fs/nfs/inode.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1024,7 +1024,7 @@ struct nfs_open_context *alloc_nfs_open_context(struct dentry *dentry,
10241024
ctx->cred = get_cred(filp->f_cred);
10251025
else
10261026
ctx->cred = get_current_cred();
1027-
ctx->ll_cred = NULL;
1027+
rcu_assign_pointer(ctx->ll_cred, NULL);
10281028
ctx->state = NULL;
10291029
ctx->mode = f_mode;
10301030
ctx->flags = 0;
@@ -1063,7 +1063,7 @@ static void __put_nfs_open_context(struct nfs_open_context *ctx, int is_sync)
10631063
put_cred(ctx->cred);
10641064
dput(ctx->dentry);
10651065
nfs_sb_deactive(sb);
1066-
put_rpccred(ctx->ll_cred);
1066+
put_rpccred(rcu_dereference_protected(ctx->ll_cred, 1));
10671067
kfree(ctx->mdsthreshold);
10681068
kfree_rcu(ctx, rcu_head);
10691069
}

fs/nfs/write.c

Lines changed: 28 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1246,7 +1246,7 @@ nfs_key_timeout_notify(struct file *filp, struct inode *inode)
12461246
struct nfs_open_context *ctx = nfs_file_open_context(filp);
12471247

12481248
if (nfs_ctx_key_to_expire(ctx, inode) &&
1249-
!ctx->ll_cred)
1249+
!rcu_access_pointer(ctx->ll_cred))
12501250
/* Already expired! */
12511251
return -EACCES;
12521252
return 0;
@@ -1258,23 +1258,38 @@ nfs_key_timeout_notify(struct file *filp, struct inode *inode)
12581258
bool nfs_ctx_key_to_expire(struct nfs_open_context *ctx, struct inode *inode)
12591259
{
12601260
struct rpc_auth *auth = NFS_SERVER(inode)->client->cl_auth;
1261-
struct rpc_cred *cred = ctx->ll_cred;
1261+
struct rpc_cred *cred, *new, *old = NULL;
12621262
struct auth_cred acred = {
12631263
.cred = ctx->cred,
12641264
};
1265+
bool ret = false;
12651266

1266-
if (cred && !cred->cr_ops->crmatch(&acred, cred, 0)) {
1267-
put_rpccred(cred);
1268-
ctx->ll_cred = NULL;
1269-
cred = NULL;
1270-
}
1271-
if (!cred)
1272-
cred = auth->au_ops->lookup_cred(auth, &acred, 0);
1273-
if (!cred || IS_ERR(cred))
1267+
rcu_read_lock();
1268+
cred = rcu_dereference(ctx->ll_cred);
1269+
if (cred && !(cred->cr_ops->crkey_timeout &&
1270+
cred->cr_ops->crkey_timeout(cred)))
1271+
goto out;
1272+
rcu_read_unlock();
1273+
1274+
new = auth->au_ops->lookup_cred(auth, &acred, 0);
1275+
if (new == cred) {
1276+
put_rpccred(new);
12741277
return true;
1275-
ctx->ll_cred = cred;
1276-
return !!(cred->cr_ops->crkey_timeout &&
1277-
cred->cr_ops->crkey_timeout(cred));
1278+
}
1279+
if (IS_ERR_OR_NULL(new)) {
1280+
new = NULL;
1281+
ret = true;
1282+
} else if (new->cr_ops->crkey_timeout &&
1283+
new->cr_ops->crkey_timeout(new))
1284+
ret = true;
1285+
1286+
rcu_read_lock();
1287+
old = rcu_dereference_protected(xchg(&ctx->ll_cred,
1288+
RCU_INITIALIZER(new)), 1);
1289+
out:
1290+
rcu_read_unlock();
1291+
put_rpccred(old);
1292+
return ret;
12781293
}
12791294

12801295
/*

include/linux/nfs_fs.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ struct nfs_open_context {
8181
fl_owner_t flock_owner;
8282
struct dentry *dentry;
8383
const struct cred *cred;
84-
struct rpc_cred *ll_cred; /* low-level cred - use to check for expiry */
84+
struct rpc_cred __rcu *ll_cred; /* low-level cred - use to check for expiry */
8585
struct nfs4_state *state;
8686
fmode_t mode;
8787

0 commit comments

Comments
 (0)