Skip to content

Commit 826b67e

Browse files
jtlaytonchucklever
authored andcommitted
nfsd: don't hand out delegation on setuid files being opened for write
We had a bug report that xfstest generic/355 was failing on NFSv4.0. This test sets various combinations of setuid/setgid modes and tests whether DIO writes will cause them to be stripped. What I found was that the server did properly strip those bits, but the client didn't notice because it held a delegation that was not recalled. The recall didn't occur because the client itself was the one generating the activity and we avoid recalls in that case. Clearing setuid bits is an "implicit" activity. The client didn't specifically request that we do that, so we need the server to issue a CB_RECALL, or avoid the situation entirely by not issuing a delegation. The easiest fix here is to simply not give out a delegation if the file is being opened for write, and the mode has the setuid and/or setgid bit set. Note that there is a potential race between the mode and lease being set, so we test for this condition both before and after setting the lease. This patch fixes generic/355, generic/683 and generic/684 for me. (Note that 355 fails only on v4.0, and 683 and 684 require NFSv4.2 to run and fail). Reported-by: Boyang Xue <bxue@redhat.com> Signed-off-by: Jeff Layton <jlayton@kernel.org> Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
1 parent 319951e commit 826b67e

File tree

1 file changed

+27
-0
lines changed

1 file changed

+27
-0
lines changed

fs/nfsd/nfs4state.c

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5421,6 +5421,23 @@ nfsd4_verify_deleg_dentry(struct nfsd4_open *open, struct nfs4_file *fp,
54215421
return 0;
54225422
}
54235423

5424+
/*
5425+
* We avoid breaking delegations held by a client due to its own activity, but
5426+
* clearing setuid/setgid bits on a write is an implicit activity and the client
5427+
* may not notice and continue using the old mode. Avoid giving out a delegation
5428+
* on setuid/setgid files when the client is requesting an open for write.
5429+
*/
5430+
static int
5431+
nfsd4_verify_setuid_write(struct nfsd4_open *open, struct nfsd_file *nf)
5432+
{
5433+
struct inode *inode = file_inode(nf->nf_file);
5434+
5435+
if ((open->op_share_access & NFS4_SHARE_ACCESS_WRITE) &&
5436+
(inode->i_mode & (S_ISUID|S_ISGID)))
5437+
return -EAGAIN;
5438+
return 0;
5439+
}
5440+
54245441
static struct nfs4_delegation *
54255442
nfs4_set_delegation(struct nfsd4_open *open, struct nfs4_ol_stateid *stp,
54265443
struct svc_fh *parent)
@@ -5454,6 +5471,8 @@ nfs4_set_delegation(struct nfsd4_open *open, struct nfs4_ol_stateid *stp,
54545471
spin_lock(&fp->fi_lock);
54555472
if (nfs4_delegation_exists(clp, fp))
54565473
status = -EAGAIN;
5474+
else if (nfsd4_verify_setuid_write(open, nf))
5475+
status = -EAGAIN;
54575476
else if (!fp->fi_deleg_file) {
54585477
fp->fi_deleg_file = nf;
54595478
/* increment early to prevent fi_deleg_file from being
@@ -5494,6 +5513,14 @@ nfs4_set_delegation(struct nfsd4_open *open, struct nfs4_ol_stateid *stp,
54945513
if (status)
54955514
goto out_unlock;
54965515

5516+
/*
5517+
* Now that the deleg is set, check again to ensure that nothing
5518+
* raced in and changed the mode while we weren't lookng.
5519+
*/
5520+
status = nfsd4_verify_setuid_write(open, fp->fi_deleg_file);
5521+
if (status)
5522+
goto out_unlock;
5523+
54975524
spin_lock(&state_lock);
54985525
spin_lock(&fp->fi_lock);
54995526
if (fp->fi_had_conflict)

0 commit comments

Comments
 (0)