Skip to content

Commit 1a86115

Browse files
committed
NFSD: Insulate nfsd4_encode_read() from page boundaries in the encode buffer
Commit 28d5bc4 ("NFSD: Optimize nfsd4_encode_readv()") replaced the use of write_bytes_to_xdr_buf() because it's expensive and the data items to be encoded are already properly aligned. However, the current code will corrupt the encoded data if the XDR data items that are reserved early and then poked into the XDR buffer later happen to fall on a page boundary in the XDR encoding buffer. __xdr_commit_encode can shift encoded data items in the encoding buffer so that pointers returned from xdr_reserve_space() no longer address the same part of the encoding stream. This isn't an issue for splice reads because the reserved encode buffer areas must fall in the XDR buffers header for the splice to work without error. For vectored reads, however, there is a possibility of send buffer corruption in rare cases. Fixes: 28d5bc4 ("NFSD: Optimize nfsd4_encode_readv()") Reviewed-by: NeilBrown <[email protected]> Reviewed-by: Jeff Layton <[email protected]> Signed-off-by: Chuck Lever <[email protected]>
1 parent ef3675b commit 1a86115

File tree

1 file changed

+19
-15
lines changed

1 file changed

+19
-15
lines changed

fs/nfsd/nfs4xdr.c

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4317,6 +4317,15 @@ static __be32 nfsd4_encode_splice_read(
43174317
int status, space_left;
43184318
__be32 nfserr;
43194319

4320+
/*
4321+
* Splice read doesn't work if encoding has already wandered
4322+
* into the XDR buf's page array.
4323+
*/
4324+
if (unlikely(xdr->buf->page_len)) {
4325+
WARN_ON_ONCE(1);
4326+
return nfserr_serverfault;
4327+
}
4328+
43204329
/*
43214330
* Make sure there is room at the end of buf->head for
43224331
* svcxdr_encode_opaque_pages() to create a tail buffer
@@ -4399,25 +4408,23 @@ nfsd4_encode_read(struct nfsd4_compoundres *resp, __be32 nfserr,
43994408
struct nfsd4_compoundargs *argp = resp->rqstp->rq_argp;
44004409
struct nfsd4_read *read = &u->read;
44014410
struct xdr_stream *xdr = resp->xdr;
4402-
int starting_len = xdr->buf->len;
44034411
bool splice_ok = argp->splice_ok;
4412+
unsigned int eof_offset;
44044413
unsigned long maxcount;
4414+
__be32 wire_data[2];
44054415
struct file *file;
4406-
__be32 *p;
44074416

44084417
if (nfserr)
44094418
return nfserr;
4419+
4420+
eof_offset = xdr->buf->len;
44104421
file = read->rd_nf->nf_file;
44114422

4412-
p = xdr_reserve_space(xdr, 8); /* eof flag and byte count */
4413-
if (!p) {
4423+
/* Reserve space for the eof flag and byte count */
4424+
if (unlikely(!xdr_reserve_space(xdr, XDR_UNIT * 2))) {
44144425
WARN_ON_ONCE(splice_ok);
44154426
return nfserr_resource;
44164427
}
4417-
if (resp->xdr->buf->page_len && splice_ok) {
4418-
WARN_ON_ONCE(1);
4419-
return nfserr_serverfault;
4420-
}
44214428
xdr_commit_encode(xdr);
44224429

44234430
maxcount = min_t(unsigned long, read->rd_length,
@@ -4428,12 +4435,13 @@ nfsd4_encode_read(struct nfsd4_compoundres *resp, __be32 nfserr,
44284435
else
44294436
nfserr = nfsd4_encode_readv(resp, read, file, maxcount);
44304437
if (nfserr) {
4431-
xdr_truncate_encode(xdr, starting_len);
4438+
xdr_truncate_encode(xdr, eof_offset);
44324439
return nfserr;
44334440
}
44344441

4435-
p = xdr_encode_bool(p, read->rd_eof);
4436-
*p = cpu_to_be32(read->rd_length);
4442+
wire_data[0] = read->rd_eof ? xdr_one : xdr_zero;
4443+
wire_data[1] = cpu_to_be32(read->rd_length);
4444+
write_bytes_to_xdr_buf(xdr->buf, eof_offset, &wire_data, XDR_UNIT * 2);
44374445
return nfs_ok;
44384446
}
44394447

@@ -5304,10 +5312,6 @@ nfsd4_encode_read_plus_data(struct nfsd4_compoundres *resp,
53045312
p = xdr_reserve_space(xdr, 4 + 8 + 4);
53055313
if (!p)
53065314
return nfserr_io;
5307-
if (resp->xdr->buf->page_len && splice_ok) {
5308-
WARN_ON_ONCE(splice_ok);
5309-
return nfserr_serverfault;
5310-
}
53115315

53125316
maxcount = min_t(unsigned long, read->rd_length,
53135317
(xdr->buf->buflen - xdr->buf->len));

0 commit comments

Comments
 (0)