Skip to content

Commit 24d92de

Browse files
Trond Myklebustchucklever
authored andcommitted
nfsd: Fix NFSv3 atomicity bugs in nfsd_setattr()
The main point of the guarded SETATTR is to prevent races with other WRITE and SETATTR calls. That requires that the check of the guard time against the inode ctime be done after taking the inode lock. Furthermore, we need to take into account the 32-bit nature of timestamps in NFSv3, and the possibility that files may change at a faster rate than once a second. Signed-off-by: Trond Myklebust <[email protected]> Reviewed-by: Jeff Layton <[email protected]> Reviewed-by: NeilBrown <[email protected]> Signed-off-by: Chuck Lever <[email protected]>
1 parent 6412e44 commit 24d92de

File tree

8 files changed

+25
-21
lines changed

8 files changed

+25
-21
lines changed

fs/nfsd/nfs3proc.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,13 +71,15 @@ nfsd3_proc_setattr(struct svc_rqst *rqstp)
7171
struct nfsd_attrs attrs = {
7272
.na_iattr = &argp->attrs,
7373
};
74+
const struct timespec64 *guardtime = NULL;
7475

7576
dprintk("nfsd: SETATTR(3) %s\n",
7677
SVCFH_fmt(&argp->fh));
7778

7879
fh_copy(&resp->fh, &argp->fh);
79-
resp->status = nfsd_setattr(rqstp, &resp->fh, &attrs,
80-
argp->check_guard, argp->guardtime);
80+
if (argp->check_guard)
81+
guardtime = &argp->guardtime;
82+
resp->status = nfsd_setattr(rqstp, &resp->fh, &attrs, guardtime);
8183
return rpc_success;
8284
}
8385

fs/nfsd/nfs3xdr.c

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -295,17 +295,14 @@ svcxdr_decode_sattr3(struct svc_rqst *rqstp, struct xdr_stream *xdr,
295295
static bool
296296
svcxdr_decode_sattrguard3(struct xdr_stream *xdr, struct nfsd3_sattrargs *args)
297297
{
298-
__be32 *p;
299298
u32 check;
300299

301300
if (xdr_stream_decode_bool(xdr, &check) < 0)
302301
return false;
303302
if (check) {
304-
p = xdr_inline_decode(xdr, XDR_UNIT * 2);
305-
if (!p)
303+
if (!svcxdr_decode_nfstime3(xdr, &args->guardtime))
306304
return false;
307305
args->check_guard = 1;
308-
args->guardtime = be32_to_cpup(p);
309306
} else
310307
args->check_guard = 0;
311308

fs/nfsd/nfs4proc.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1171,8 +1171,7 @@ nfsd4_setattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
11711171
goto out;
11721172
save_no_wcc = cstate->current_fh.fh_no_wcc;
11731173
cstate->current_fh.fh_no_wcc = true;
1174-
status = nfsd_setattr(rqstp, &cstate->current_fh, &attrs,
1175-
0, (time64_t)0);
1174+
status = nfsd_setattr(rqstp, &cstate->current_fh, &attrs, NULL);
11761175
cstate->current_fh.fh_no_wcc = save_no_wcc;
11771176
if (!status)
11781177
status = nfserrno(attrs.na_labelerr);

fs/nfsd/nfs4state.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5460,7 +5460,7 @@ nfsd4_truncate(struct svc_rqst *rqstp, struct svc_fh *fh,
54605460
return 0;
54615461
if (!(open->op_share_access & NFS4_SHARE_ACCESS_WRITE))
54625462
return nfserr_inval;
5463-
return nfsd_setattr(rqstp, fh, &attrs, 0, (time64_t)0);
5463+
return nfsd_setattr(rqstp, fh, &attrs, NULL);
54645464
}
54655465

54665466
static __be32 nfs4_get_vfs_file(struct svc_rqst *rqstp, struct nfs4_file *fp,

fs/nfsd/nfsproc.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ nfsd_proc_setattr(struct svc_rqst *rqstp)
103103
}
104104
}
105105

106-
resp->status = nfsd_setattr(rqstp, fhp, &attrs, 0, (time64_t)0);
106+
resp->status = nfsd_setattr(rqstp, fhp, &attrs, NULL);
107107
if (resp->status != nfs_ok)
108108
goto out;
109109

@@ -390,8 +390,8 @@ nfsd_proc_create(struct svc_rqst *rqstp)
390390
*/
391391
attr->ia_valid &= ATTR_SIZE;
392392
if (attr->ia_valid)
393-
resp->status = nfsd_setattr(rqstp, newfhp, &attrs, 0,
394-
(time64_t)0);
393+
resp->status = nfsd_setattr(rqstp, newfhp, &attrs,
394+
NULL);
395395
}
396396

397397
out_unlock:

fs/nfsd/vfs.c

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -476,7 +476,6 @@ static int __nfsd_setattr(struct dentry *dentry, struct iattr *iap)
476476
* @rqstp: controlling RPC transaction
477477
* @fhp: filehandle of target
478478
* @attr: attributes to set
479-
* @check_guard: set to 1 if guardtime is a valid timestamp
480479
* @guardtime: do not act if ctime.tv_sec does not match this timestamp
481480
*
482481
* This call may adjust the contents of @attr (in particular, this
@@ -488,8 +487,7 @@ static int __nfsd_setattr(struct dentry *dentry, struct iattr *iap)
488487
*/
489488
__be32
490489
nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp,
491-
struct nfsd_attrs *attr,
492-
int check_guard, time64_t guardtime)
490+
struct nfsd_attrs *attr, const struct timespec64 *guardtime)
493491
{
494492
struct dentry *dentry;
495493
struct inode *inode;
@@ -538,9 +536,6 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp,
538536

539537
nfsd_sanitize_attrs(inode, iap);
540538

541-
if (check_guard && guardtime != inode_get_ctime_sec(inode))
542-
return nfserr_notsync;
543-
544539
/*
545540
* The size case is special, it changes the file in addition to the
546541
* attributes, and file systems don't expect it to be mixed with
@@ -558,6 +553,16 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp,
558553
err = fh_fill_pre_attrs(fhp);
559554
if (err)
560555
goto out_unlock;
556+
557+
if (guardtime) {
558+
struct timespec64 ctime = inode_get_ctime(inode);
559+
if ((u32)guardtime->tv_sec != (u32)ctime.tv_sec ||
560+
guardtime->tv_nsec != ctime.tv_nsec) {
561+
err = nfserr_notsync;
562+
goto out_fill_attrs;
563+
}
564+
}
565+
561566
for (retries = 1;;) {
562567
struct iattr attrs;
563568

@@ -585,6 +590,7 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp,
585590
attr->na_aclerr = set_posix_acl(&nop_mnt_idmap,
586591
dentry, ACL_TYPE_DEFAULT,
587592
attr->na_dpacl);
593+
out_fill_attrs:
588594
fh_fill_post_attrs(fhp);
589595
out_unlock:
590596
inode_unlock(inode);
@@ -1411,7 +1417,7 @@ nfsd_create_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp,
14111417
* if the attributes have not changed.
14121418
*/
14131419
if (iap->ia_valid)
1414-
status = nfsd_setattr(rqstp, resfhp, attrs, 0, (time64_t)0);
1420+
status = nfsd_setattr(rqstp, resfhp, attrs, NULL);
14151421
else
14161422
status = nfserrno(commit_metadata(resfhp));
14171423

fs/nfsd/vfs.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ __be32 nfsd_lookup_dentry(struct svc_rqst *, struct svc_fh *,
6969
const char *, unsigned int,
7070
struct svc_export **, struct dentry **);
7171
__be32 nfsd_setattr(struct svc_rqst *, struct svc_fh *,
72-
struct nfsd_attrs *, int, time64_t);
72+
struct nfsd_attrs *, const struct timespec64 *);
7373
int nfsd_mountpoint(struct dentry *, struct svc_export *);
7474
#ifdef CONFIG_NFSD_V4
7575
__be32 nfsd4_vfs_fallocate(struct svc_rqst *, struct svc_fh *,

fs/nfsd/xdr3.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ struct nfsd3_sattrargs {
1414
struct svc_fh fh;
1515
struct iattr attrs;
1616
int check_guard;
17-
time64_t guardtime;
17+
struct timespec64 guardtime;
1818
};
1919

2020
struct nfsd3_diropargs {

0 commit comments

Comments
 (0)