Skip to content

Commit c9fc777

Browse files
committed
NFSD: Insulate nfsd4_encode_read_plus() from page boundaries in the encode buffer
Commit eeadcb7 ("NFSD: Simplify READ_PLUS") replaced the use of write_bytes_to_xdr_buf(), copying what was in nfsd4_encode_read() at the time. 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. Fixes: eeadcb7 ("NFSD: Simplify READ_PLUS") Reviewed-by: NeilBrown <[email protected]> Reviewed-by: Jeff Layton <[email protected]> Signed-off-by: Chuck Lever <[email protected]>
1 parent 1a86115 commit c9fc777

File tree

1 file changed

+10
-8
lines changed

1 file changed

+10
-8
lines changed

fs/nfsd/nfs4xdr.c

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5337,16 +5337,17 @@ nfsd4_encode_read_plus(struct nfsd4_compoundres *resp, __be32 nfserr,
53375337
struct nfsd4_read *read = &u->read;
53385338
struct file *file = read->rd_nf->nf_file;
53395339
struct xdr_stream *xdr = resp->xdr;
5340-
int starting_len = xdr->buf->len;
5340+
unsigned int eof_offset;
5341+
__be32 wire_data[2];
53415342
u32 segments = 0;
5342-
__be32 *p;
53435343

53445344
if (nfserr)
53455345
return nfserr;
53465346

5347-
/* eof flag, segment count */
5348-
p = xdr_reserve_space(xdr, 4 + 4);
5349-
if (!p)
5347+
eof_offset = xdr->buf->len;
5348+
5349+
/* Reserve space for the eof flag and segment count */
5350+
if (unlikely(!xdr_reserve_space(xdr, XDR_UNIT * 2)))
53505351
return nfserr_io;
53515352
xdr_commit_encode(xdr);
53525353

@@ -5356,15 +5357,16 @@ nfsd4_encode_read_plus(struct nfsd4_compoundres *resp, __be32 nfserr,
53565357

53575358
nfserr = nfsd4_encode_read_plus_data(resp, read);
53585359
if (nfserr) {
5359-
xdr_truncate_encode(xdr, starting_len);
5360+
xdr_truncate_encode(xdr, eof_offset);
53605361
return nfserr;
53615362
}
53625363

53635364
segments++;
53645365

53655366
out:
5366-
p = xdr_encode_bool(p, read->rd_eof);
5367-
*p = cpu_to_be32(segments);
5367+
wire_data[0] = read->rd_eof ? xdr_one : xdr_zero;
5368+
wire_data[1] = cpu_to_be32(segments);
5369+
write_bytes_to_xdr_buf(xdr->buf, eof_offset, &wire_data, XDR_UNIT * 2);
53685370
return nfserr;
53695371
}
53705372

0 commit comments

Comments
 (0)