{
        int i;
 
-       for (i = 0; i < ses->se_fchannel.maxreqs; i++)
+       for (i = 0; i < ses->se_fchannel.maxreqs; i++) {
+               free_svc_cred(&ses->se_slots[i]->sl_cred);
                kfree(ses->se_slots[i]);
+       }
 }
 
 /*
        slot->sl_flags |= NFSD4_SLOT_INITIALIZED;
        slot->sl_opcnt = resp->opcnt;
        slot->sl_status = resp->cstate.status;
+       free_svc_cred(&slot->sl_cred);
+       copy_cred(&slot->sl_cred, &resp->rqstp->rq_cred);
 
        if (!nfsd4_cache_this(resp)) {
                slot->sl_flags &= ~NFSD4_SLOT_CACHED;
        return xb->len > session->se_fchannel.maxreq_sz;
 }
 
+static bool replay_matches_cache(struct svc_rqst *rqstp,
+                struct nfsd4_sequence *seq, struct nfsd4_slot *slot)
+{
+       struct nfsd4_compoundargs *argp = rqstp->rq_argp;
+
+       if ((bool)(slot->sl_flags & NFSD4_SLOT_CACHETHIS) !=
+           (bool)seq->cachethis)
+               return false;
+       /*
+        * If there's an error than the reply can have fewer ops than
+        * the call.  But if we cached a reply with *more* ops than the
+        * call you're sending us now, then this new call is clearly not
+        * really a replay of the old one:
+        */
+       if (slot->sl_opcnt < argp->opcnt)
+               return false;
+       /* This is the only check explicitly called by spec: */
+       if (!same_creds(&rqstp->rq_cred, &slot->sl_cred))
+               return false;
+       /*
+        * There may be more comparisons we could actually do, but the
+        * spec doesn't require us to catch every case where the calls
+        * don't match (that would require caching the call as well as
+        * the reply), so we don't bother.
+        */
+       return true;
+}
+
 __be32
 nfsd4_sequence(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
                union nfsd4_op_u *u)
                status = nfserr_seq_misordered;
                if (!(slot->sl_flags & NFSD4_SLOT_INITIALIZED))
                        goto out_put_session;
+               status = nfserr_seq_false_retry;
+               if (!replay_matches_cache(rqstp, seq, slot))
+                       goto out_put_session;
                cstate->slot = slot;
                cstate->session = session;
                cstate->clp = clp;