Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

NFSD: Insulate nfsd4_encode_read_plus() from page boundaries in the encode buffer

Commit eeadcb757945 ("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: eeadcb757945 ("NFSD: Simplify READ_PLUS")
Reviewed-by: NeilBrown <neilb@suse.de>
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>

+10 -8
+10 -8
fs/nfsd/nfs4xdr.c
··· 5337 5337 struct nfsd4_read *read = &u->read; 5338 5338 struct file *file = read->rd_nf->nf_file; 5339 5339 struct xdr_stream *xdr = resp->xdr; 5340 - int starting_len = xdr->buf->len; 5340 + unsigned int eof_offset; 5341 + __be32 wire_data[2]; 5341 5342 u32 segments = 0; 5342 - __be32 *p; 5343 5343 5344 5344 if (nfserr) 5345 5345 return nfserr; 5346 5346 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))) 5350 5351 return nfserr_io; 5351 5352 xdr_commit_encode(xdr); 5352 5353 ··· 5357 5356 5358 5357 nfserr = nfsd4_encode_read_plus_data(resp, read); 5359 5358 if (nfserr) { 5360 - xdr_truncate_encode(xdr, starting_len); 5359 + xdr_truncate_encode(xdr, eof_offset); 5361 5360 return nfserr; 5362 5361 } 5363 5362 5364 5363 segments++; 5365 5364 5366 5365 out: 5367 - p = xdr_encode_bool(p, read->rd_eof); 5368 - *p = cpu_to_be32(segments); 5366 + wire_data[0] = read->rd_eof ? xdr_one : xdr_zero; 5367 + wire_data[1] = cpu_to_be32(segments); 5368 + write_bytes_to_xdr_buf(xdr->buf, eof_offset, &wire_data, XDR_UNIT * 2); 5369 5369 return nfserr; 5370 5370 } 5371 5371