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_data() 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>

+17 -8
+17 -8
fs/nfsd/nfs4xdr.c
··· 5305 5305 struct file *file = read->rd_nf->nf_file; 5306 5306 struct xdr_stream *xdr = resp->xdr; 5307 5307 bool splice_ok = argp->splice_ok; 5308 + unsigned int offset_offset; 5309 + __be32 nfserr, wire_count; 5308 5310 unsigned long maxcount; 5309 - __be32 nfserr, *p; 5311 + __be64 wire_offset; 5310 5312 5311 - /* Content type, offset, byte count */ 5312 - p = xdr_reserve_space(xdr, 4 + 8 + 4); 5313 - if (!p) 5313 + if (xdr_stream_encode_u32(xdr, NFS4_CONTENT_DATA) != XDR_UNIT) 5314 5314 return nfserr_io; 5315 + 5316 + offset_offset = xdr->buf->len; 5317 + 5318 + /* Reserve space for the byte offset and count */ 5319 + if (unlikely(!xdr_reserve_space(xdr, XDR_UNIT * 3))) 5320 + return nfserr_io; 5321 + xdr_commit_encode(xdr); 5315 5322 5316 5323 maxcount = min_t(unsigned long, read->rd_length, 5317 5324 (xdr->buf->buflen - xdr->buf->len)); ··· 5330 5323 if (nfserr) 5331 5324 return nfserr; 5332 5325 5333 - *p++ = cpu_to_be32(NFS4_CONTENT_DATA); 5334 - p = xdr_encode_hyper(p, read->rd_offset); 5335 - *p = cpu_to_be32(read->rd_length); 5336 - 5326 + wire_offset = cpu_to_be64(read->rd_offset); 5327 + write_bytes_to_xdr_buf(xdr->buf, offset_offset, &wire_offset, 5328 + XDR_UNIT * 2); 5329 + wire_count = cpu_to_be32(read->rd_length); 5330 + write_bytes_to_xdr_buf(xdr->buf, offset_offset + XDR_UNIT * 2, 5331 + &wire_count, XDR_UNIT); 5337 5332 return nfs_ok; 5338 5333 } 5339 5334