Skip to content

Commit b51fdff

Browse files
chuckleverSasha Levin
authored and
Sasha Levin
committed
NFSD: Fix handling of oversized NFSv4 COMPOUND requests
[ Upstream commit 7518a3d ] If an NFS server returns NFS4ERR_RESOURCE on the first operation in an NFSv4 COMPOUND, there's no way for a client to know where the problem is and then simplify the compound to make forward progress. So instead, make NFSD process as many operations in an oversized COMPOUND as it can and then return NFS4ERR_RESOURCE on the first operation it did not process. pynfs NFSv4.0 COMP6 exercises this case, but checks only for the COMPOUND status code, not whether the server has processed any of the operations. pynfs NFSv4.1 SEQ6 and SEQ7 exercise the NFSv4.1 case, which detects too many operations per COMPOUND by checking against the limits negotiated when the session was created. Suggested-by: Bruce Fields <[email protected]> Fixes: 0078117 ("nfsd: return RESOURCE not GARBAGE_ARGS on too many ops") Signed-off-by: Chuck Lever <[email protected]> Signed-off-by: Sasha Levin <[email protected]>
1 parent f0f8740 commit b51fdff

File tree

3 files changed

+18
-16
lines changed

3 files changed

+18
-16
lines changed

fs/nfsd/nfs4proc.c

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2663,9 +2663,6 @@ nfsd4_proc_compound(struct svc_rqst *rqstp)
26632663
status = nfserr_minor_vers_mismatch;
26642664
if (nfsd_minorversion(nn, args->minorversion, NFSD_TEST) <= 0)
26652665
goto out;
2666-
status = nfserr_resource;
2667-
if (args->opcnt > NFSD_MAX_OPS_PER_COMPOUND)
2668-
goto out;
26692666

26702667
status = nfs41_check_op_ordering(args);
26712668
if (status) {
@@ -2678,10 +2675,20 @@ nfsd4_proc_compound(struct svc_rqst *rqstp)
26782675

26792676
rqstp->rq_lease_breaker = (void **)&cstate->clp;
26802677

2681-
trace_nfsd_compound(rqstp, args->opcnt);
2678+
trace_nfsd_compound(rqstp, args->client_opcnt);
26822679
while (!status && resp->opcnt < args->opcnt) {
26832680
op = &args->ops[resp->opcnt++];
26842681

2682+
if (unlikely(resp->opcnt == NFSD_MAX_OPS_PER_COMPOUND)) {
2683+
/* If there are still more operations to process,
2684+
* stop here and report NFS4ERR_RESOURCE. */
2685+
if (cstate->minorversion == 0 &&
2686+
args->client_opcnt > resp->opcnt) {
2687+
op->status = nfserr_resource;
2688+
goto encode_op;
2689+
}
2690+
}
2691+
26852692
/*
26862693
* The XDR decode routines may have pre-set op->status;
26872694
* for example, if there is a miscellaneous XDR error
@@ -2757,8 +2764,8 @@ nfsd4_proc_compound(struct svc_rqst *rqstp)
27572764
status = op->status;
27582765
}
27592766

2760-
trace_nfsd_compound_status(args->opcnt, resp->opcnt, status,
2761-
nfsd4_op_name(op->opnum));
2767+
trace_nfsd_compound_status(args->client_opcnt, resp->opcnt,
2768+
status, nfsd4_op_name(op->opnum));
27622769

27632770
nfsd4_cstate_clear_replay(cstate);
27642771
nfsd4_increment_op_stats(op->opnum);

fs/nfsd/nfs4xdr.c

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2347,16 +2347,10 @@ nfsd4_decode_compound(struct nfsd4_compoundargs *argp)
23472347

23482348
if (xdr_stream_decode_u32(argp->xdr, &argp->minorversion) < 0)
23492349
return false;
2350-
if (xdr_stream_decode_u32(argp->xdr, &argp->opcnt) < 0)
2350+
if (xdr_stream_decode_u32(argp->xdr, &argp->client_opcnt) < 0)
23512351
return false;
2352-
2353-
/*
2354-
* NFS4ERR_RESOURCE is a more helpful error than GARBAGE_ARGS
2355-
* here, so we return success at the xdr level so that
2356-
* nfsd4_proc can handle this is an NFS-level error.
2357-
*/
2358-
if (argp->opcnt > NFSD_MAX_OPS_PER_COMPOUND)
2359-
return true;
2352+
argp->opcnt = min_t(u32, argp->client_opcnt,
2353+
NFSD_MAX_OPS_PER_COMPOUND);
23602354

23612355
if (argp->opcnt > ARRAY_SIZE(argp->iops)) {
23622356
argp->ops = kzalloc(argp->opcnt * sizeof(*argp->ops), GFP_KERNEL);

fs/nfsd/xdr4.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -689,9 +689,10 @@ struct nfsd4_compoundargs {
689689
struct svcxdr_tmpbuf *to_free;
690690
struct svc_rqst *rqstp;
691691

692-
u32 taglen;
693692
char * tag;
693+
u32 taglen;
694694
u32 minorversion;
695+
u32 client_opcnt;
695696
u32 opcnt;
696697
struct nfsd4_op *ops;
697698
struct nfsd4_op iops[8];

0 commit comments

Comments
 (0)