diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c index eeae33a19a96..3a6945b3bd8b 100644 --- a/fs/nfsd/nfsctl.c +++ b/fs/nfsd/nfsctl.c @@ -1588,14 +1588,29 @@ int nfsd_nl_rpc_status_get_dumpit(struct sk_buff *skb, rqstp->rq_proc == NFSPROC4_COMPOUND) { /* NFSv4 compound */ struct nfsd4_compoundargs *args; + struct nfsd4_op *ops; + u32 opcnt; int j; args = rqstp->rq_argp; - genl_rqstp.rq_opcnt = min_t(u32, args->opcnt, + opcnt = READ_ONCE(args->opcnt); + ops = READ_ONCE(args->ops); + + /* + * Finish the seqcount retry before + * dereferencing ops. An unchanged counter means + * opcnt and ops came from the same COMPOUND, + * where opcnt cannot exceed what ops holds. + */ + smp_rmb(); + if (READ_ONCE(rqstp->rq_status_counter) != + status_counter) + continue; + + genl_rqstp.rq_opcnt = min_t(u32, opcnt, ARRAY_SIZE(genl_rqstp.rq_opnum)); for (j = 0; j < genl_rqstp.rq_opcnt; j++) - genl_rqstp.rq_opnum[j] = - args->ops[j].opnum; + genl_rqstp.rq_opnum[j] = ops[j].opnum; } #endif /* CONFIG_NFSD_V4 */