Skip to content

Commit cc6cc11

Browse files
committed
NFSD: Insulate nfsd4_encode_read() from page boundaries in the encode buffer
JIRA: https://issues.redhat.com/browse/RHEL-108616 commit 1a86115 Author: Chuck Lever <chuck.lever@oracle.com> Date: Mon Dec 30 19:28:53 2024 -0500 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 <neilb@suse.de> Reviewed-by: Jeff Layton <jlayton@kernel.org> Signed-off-by: Chuck Lever <chuck.lever@oracle.com> Signed-off-by: Benjamin Coddington <bcodding@redhat.com>
1 parent bee3364 commit cc6cc11

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
@@ -4325,6 +4325,15 @@ static __be32 nfsd4_encode_splice_read(
43254325
int status, space_left;
43264326
__be32 nfserr;
43274327

4328+
/*
4329+
* Splice read doesn't work if encoding has already wandered
4330+
* into the XDR buf's page array.
4331+
*/
4332+
if (unlikely(xdr->buf->page_len)) {
4333+
WARN_ON_ONCE(1);
4334+
return nfserr_serverfault;
4335+
}
4336+
43284337
/*
43294338
* Make sure there is room at the end of buf->head for
43304339
* svcxdr_encode_opaque_pages() to create a tail buffer
@@ -4407,25 +4416,23 @@ nfsd4_encode_read(struct nfsd4_compoundres *resp, __be32 nfserr,
44074416
struct nfsd4_compoundargs *argp = resp->rqstp->rq_argp;
44084417
struct nfsd4_read *read = &u->read;
44094418
struct xdr_stream *xdr = resp->xdr;
4410-
int starting_len = xdr->buf->len;
44114419
bool splice_ok = argp->splice_ok;
4420+
unsigned int eof_offset;
44124421
unsigned long maxcount;
4422+
__be32 wire_data[2];
44134423
struct file *file;
4414-
__be32 *p;
44154424

44164425
if (nfserr)
44174426
return nfserr;
4427+
4428+
eof_offset = xdr->buf->len;
44184429
file = read->rd_nf->nf_file;
44194430

4420-
p = xdr_reserve_space(xdr, 8); /* eof flag and byte count */
4421-
if (!p) {
4431+
/* Reserve space for the eof flag and byte count */
4432+
if (unlikely(!xdr_reserve_space(xdr, XDR_UNIT * 2))) {
44224433
WARN_ON_ONCE(splice_ok);
44234434
return nfserr_resource;
44244435
}
4425-
if (resp->xdr->buf->page_len && splice_ok) {
4426-
WARN_ON_ONCE(1);
4427-
return nfserr_serverfault;
4428-
}
44294436
xdr_commit_encode(xdr);
44304437

44314438
maxcount = min_t(unsigned long, read->rd_length,
@@ -4436,12 +4443,13 @@ nfsd4_encode_read(struct nfsd4_compoundres *resp, __be32 nfserr,
44364443
else
44374444
nfserr = nfsd4_encode_readv(resp, read, file, maxcount);
44384445
if (nfserr) {
4439-
xdr_truncate_encode(xdr, starting_len);
4446+
xdr_truncate_encode(xdr, eof_offset);
44404447
return nfserr;
44414448
}
44424449

4443-
p = xdr_encode_bool(p, read->rd_eof);
4444-
*p = cpu_to_be32(read->rd_length);
4450+
wire_data[0] = read->rd_eof ? xdr_one : xdr_zero;
4451+
wire_data[1] = cpu_to_be32(read->rd_length);
4452+
write_bytes_to_xdr_buf(xdr->buf, eof_offset, &wire_data, XDR_UNIT * 2);
44454453
return nfs_ok;
44464454
}
44474455

@@ -5290,10 +5298,6 @@ nfsd4_encode_read_plus_data(struct nfsd4_compoundres *resp,
52905298
p = xdr_reserve_space(xdr, 4 + 8 + 4);
52915299
if (!p)
52925300
return nfserr_io;
5293-
if (resp->xdr->buf->page_len && splice_ok) {
5294-
WARN_ON_ONCE(splice_ok);
5295-
return nfserr_serverfault;
5296-
}
52975301

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

0 commit comments

Comments
 (0)