return 0;
 }
 
+/*
+ * We avoid breaking delegations held by a client due to its own activity, but
+ * clearing setuid/setgid bits on a write is an implicit activity and the client
+ * may not notice and continue using the old mode. Avoid giving out a delegation
+ * on setuid/setgid files when the client is requesting an open for write.
+ */
+static int
+nfsd4_verify_setuid_write(struct nfsd4_open *open, struct nfsd_file *nf)
+{
+       struct inode *inode = file_inode(nf->nf_file);
+
+       if ((open->op_share_access & NFS4_SHARE_ACCESS_WRITE) &&
+           (inode->i_mode & (S_ISUID|S_ISGID)))
+               return -EAGAIN;
+       return 0;
+}
+
 static struct nfs4_delegation *
 nfs4_set_delegation(struct nfsd4_open *open, struct nfs4_ol_stateid *stp,
                    struct svc_fh *parent)
        spin_lock(&fp->fi_lock);
        if (nfs4_delegation_exists(clp, fp))
                status = -EAGAIN;
+       else if (nfsd4_verify_setuid_write(open, nf))
+               status = -EAGAIN;
        else if (!fp->fi_deleg_file) {
                fp->fi_deleg_file = nf;
                /* increment early to prevent fi_deleg_file from being
        if (status)
                goto out_unlock;
 
+       /*
+        * Now that the deleg is set, check again to ensure that nothing
+        * raced in and changed the mode while we weren't lookng.
+        */
+       status = nfsd4_verify_setuid_write(open, fp->fi_deleg_file);
+       if (status)
+               goto out_unlock;
+
        spin_lock(&state_lock);
        spin_lock(&fp->fi_lock);
        if (fp->fi_had_conflict)