public inbox for linux-nfs@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH v4 0/9] Fix XDR encoding near page boundaries
@ 2024-12-31  0:28 cel
  2024-12-31  0:28 ` [PATCH v4 1/9] NFSD: Encode COMPOUND operation status on " cel
                   ` (9 more replies)
  0 siblings, 10 replies; 14+ messages in thread
From: cel @ 2024-12-31  0:28 UTC (permalink / raw)
  To: Neil Brown, Jeff Layton, Olga Kornievskaia, Dai Ngo, Tom Talpey
  Cc: linux-nfs, Rick Macklem, j.david.lists, Chuck Lever

From: Chuck Lever <chuck.lever@oracle.com>

Refresh the patch series to address the longstanding bug pointed out
by J David and Rick Macklem.

Changes since v2:
- Address same issue in NFSv4 READLINK and SECINFO operations
- Update kdoc comment for xdr_reserve_space()

Chuck Lever (9):
  NFSD: Encode COMPOUND operation status on page boundaries
  NFSD: Insulate nfsd4_encode_read() from page boundaries in the encode
    buffer
  NFSD: Insulate nfsd4_encode_read_plus() from page boundaries in the
    encode buffer
  NFSD: Insulate nfsd4_encode_read_plus_data() from page boundaries in
    the encode buffer
  NFSD: Insulate nfsd4_encode_fattr4() from page boundaries in the
    encode buffer
  NFSD: Insulate nfsd4_encode_readlink() from page boundaries in the
    encode buffer
  NFSD: Refactor nfsd4_do_encode_secinfo() again
  NFSD: Insulate nfsd4_encode_secinfo() from page boundaries in the
    encode buffer
  SUNRPC: Document validity guarantees of the pointer returned by
    reserve_space

 fs/nfsd/nfs4xdr.c | 214 +++++++++++++++++++++++++---------------------
 net/sunrpc/xdr.c  |   6 ++
 2 files changed, 122 insertions(+), 98 deletions(-)

-- 
2.47.0


^ permalink raw reply	[flat|nested] 14+ messages in thread

* [PATCH v4 1/9] NFSD: Encode COMPOUND operation status on page boundaries
  2024-12-31  0:28 [PATCH v4 0/9] Fix XDR encoding near page boundaries cel
@ 2024-12-31  0:28 ` cel
  2024-12-31  0:28 ` [PATCH v4 2/9] NFSD: Insulate nfsd4_encode_read() from page boundaries in the encode buffer cel
                   ` (8 subsequent siblings)
  9 siblings, 0 replies; 14+ messages in thread
From: cel @ 2024-12-31  0:28 UTC (permalink / raw)
  To: Neil Brown, Jeff Layton, Olga Kornievskaia, Dai Ngo, Tom Talpey
  Cc: linux-nfs, Rick Macklem, j.david.lists, Chuck Lever, Rick Macklem

From: Chuck Lever <chuck.lever@oracle.com>

J. David reports an odd corruption of a READDIR reply sent to a
FreeBSD client.

xdr_reserve_space() has to do a special trick when the @nbytes value
requests more space than there is in the current page of the XDR
buffer.

In that case, xdr_reserve_space() returns a pointer to the start of
the next page, and then the next call to xdr_reserve_space() invokes
__xdr_commit_encode() to copy enough of the data item back into the
previous page to make that data item contiguous across the page
boundary.

But we need to be careful in the case where buffer space is reserved
early for a data item whose value will be inserted into the buffer
later.

One such caller, nfsd4_encode_operation(), reserves 8 bytes in the
encoding buffer for each COMPOUND operation. However, a READDIR
result can sometimes encode file names so that there are only 4
bytes left at the end of the current XDR buffer page (though plenty
of pages are left to handle the remaining encoding tasks).

If a COMPOUND operation follows the READDIR result (say, a GETATTR),
then nfsd4_encode_operation() will reserve 8 bytes for the op number
(9) and the op status (usually NFS4_OK). In this weird case,
xdr_reserve_space() returns a pointer to byte zero of the next buffer
page, as it assumes the data item will be copied back into place (in
the previous page) on the next call to xdr_reserve_space().

nfsd4_encode_operation() writes the op num into the buffer, then
saves the next 4-byte location for the op's status code. The next
xdr_reserve_space() call is part of GETATTR encoding, so the op num
gets copied back into the previous page, but the saved location for
the op status continues to point to the wrong spot in the current
XDR buffer page because __xdr_commit_encode() moved that data item.

After GETATTR encoding is complete, nfsd4_encode_operation() writes
the op status over the first XDR data item in the GETATTR result.
The NFS4_OK status code (0) makes it look like there are zero items
in the GETATTR's attribute bitmask.

The patch description of commit 2825a7f90753 ("nfsd4: allow encoding
across page boundaries") [2014] remarks that NFSD "can't handle a
new operation starting close to the end of a page." This bug appears
to be one reason for that remark.

Reported-by: J David <j.david.lists@gmail.com>
Closes: https://lore.kernel.org/linux-nfs/3998d739-c042-46b4-8166-dbd6c5f0e804@oracle.com/T/#t
Tested-by: Rick Macklem <rmacklem@uoguelph.ca>
Reviewed-by: NeilBrown <neilb@suse.de>
X-Cc: stable@vger.kernel.org
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
 fs/nfsd/nfs4xdr.c | 20 ++++++++++----------
 1 file changed, 10 insertions(+), 10 deletions(-)

diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index 53fac037611c..efcb132c19d4 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -5760,15 +5760,14 @@ nfsd4_encode_operation(struct nfsd4_compoundres *resp, struct nfsd4_op *op)
 	struct nfs4_stateowner *so = resp->cstate.replay_owner;
 	struct svc_rqst *rqstp = resp->rqstp;
 	const struct nfsd4_operation *opdesc = op->opdesc;
-	int post_err_offset;
+	unsigned int op_status_offset;
 	nfsd4_enc encoder;
-	__be32 *p;
 
-	p = xdr_reserve_space(xdr, 8);
-	if (!p)
+	if (xdr_stream_encode_u32(xdr, op->opnum) != XDR_UNIT)
+		goto release;
+	op_status_offset = xdr_stream_pos(xdr);
+	if (!xdr_reserve_space(xdr, XDR_UNIT))
 		goto release;
-	*p++ = cpu_to_be32(op->opnum);
-	post_err_offset = xdr->buf->len;
 
 	if (op->opnum == OP_ILLEGAL)
 		goto status;
@@ -5809,20 +5808,21 @@ nfsd4_encode_operation(struct nfsd4_compoundres *resp, struct nfsd4_op *op)
 		 * bug if we had to do this on a non-idempotent op:
 		 */
 		warn_on_nonidempotent_op(op);
-		xdr_truncate_encode(xdr, post_err_offset);
+		xdr_truncate_encode(xdr, op_status_offset + XDR_UNIT);
 	}
 	if (so) {
-		int len = xdr->buf->len - post_err_offset;
+		int len = xdr->buf->len - (op_status_offset + XDR_UNIT);
 
 		so->so_replay.rp_status = op->status;
 		so->so_replay.rp_buflen = len;
-		read_bytes_from_xdr_buf(xdr->buf, post_err_offset,
+		read_bytes_from_xdr_buf(xdr->buf, op_status_offset + XDR_UNIT,
 						so->so_replay.rp_buf, len);
 	}
 status:
 	op->status = nfsd4_map_status(op->status,
 				      resp->cstate.minorversion);
-	*p = op->status;
+	write_bytes_to_xdr_buf(xdr->buf, op_status_offset,
+			       &op->status, XDR_UNIT);
 release:
 	if (opdesc && opdesc->op_release)
 		opdesc->op_release(&op->u);
-- 
2.47.0


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [PATCH v4 2/9] NFSD: Insulate nfsd4_encode_read() from page boundaries in the encode buffer
  2024-12-31  0:28 [PATCH v4 0/9] Fix XDR encoding near page boundaries cel
  2024-12-31  0:28 ` [PATCH v4 1/9] NFSD: Encode COMPOUND operation status on " cel
@ 2024-12-31  0:28 ` cel
  2024-12-31  0:28 ` [PATCH v4 3/9] NFSD: Insulate nfsd4_encode_read_plus() " cel
                   ` (7 subsequent siblings)
  9 siblings, 0 replies; 14+ messages in thread
From: cel @ 2024-12-31  0:28 UTC (permalink / raw)
  To: Neil Brown, Jeff Layton, Olga Kornievskaia, Dai Ngo, Tom Talpey
  Cc: linux-nfs, Rick Macklem, j.david.lists, Chuck Lever

From: Chuck Lever <chuck.lever@oracle.com>

Commit 28d5bc468efe ("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: 28d5bc468efe ("NFSD: Optimize nfsd4_encode_readv()")
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
 fs/nfsd/nfs4xdr.c | 34 +++++++++++++++++++---------------
 1 file changed, 19 insertions(+), 15 deletions(-)

diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index efcb132c19d4..094806fe1a32 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -4316,6 +4316,15 @@ static __be32 nfsd4_encode_splice_read(
 	int status, space_left;
 	__be32 nfserr;
 
+	/*
+	 * Splice read doesn't work if encoding has already wandered
+	 * into the XDR buf's page array.
+	 */
+	if (unlikely(xdr->buf->page_len)) {
+		WARN_ON_ONCE(1);
+		return nfserr_serverfault;
+	}
+
 	/*
 	 * Make sure there is room at the end of buf->head for
 	 * svcxdr_encode_opaque_pages() to create a tail buffer
@@ -4398,25 +4407,23 @@ nfsd4_encode_read(struct nfsd4_compoundres *resp, __be32 nfserr,
 	struct nfsd4_compoundargs *argp = resp->rqstp->rq_argp;
 	struct nfsd4_read *read = &u->read;
 	struct xdr_stream *xdr = resp->xdr;
-	int starting_len = xdr->buf->len;
 	bool splice_ok = argp->splice_ok;
+	unsigned int eof_offset;
 	unsigned long maxcount;
+	__be32 wire_data[2];
 	struct file *file;
-	__be32 *p;
 
 	if (nfserr)
 		return nfserr;
+
+	eof_offset = xdr_stream_pos(xdr);
 	file = read->rd_nf->nf_file;
 
-	p = xdr_reserve_space(xdr, 8); /* eof flag and byte count */
-	if (!p) {
+	/* Reserve space for the eof flag and byte count */
+	if (unlikely(!xdr_reserve_space(xdr, XDR_UNIT * 2))) {
 		WARN_ON_ONCE(splice_ok);
 		return nfserr_resource;
 	}
-	if (resp->xdr->buf->page_len && splice_ok) {
-		WARN_ON_ONCE(1);
-		return nfserr_serverfault;
-	}
 	xdr_commit_encode(xdr);
 
 	maxcount = min_t(unsigned long, read->rd_length,
@@ -4427,12 +4434,13 @@ nfsd4_encode_read(struct nfsd4_compoundres *resp, __be32 nfserr,
 	else
 		nfserr = nfsd4_encode_readv(resp, read, file, maxcount);
 	if (nfserr) {
-		xdr_truncate_encode(xdr, starting_len);
+		xdr_truncate_encode(xdr, eof_offset);
 		return nfserr;
 	}
 
-	p = xdr_encode_bool(p, read->rd_eof);
-	*p = cpu_to_be32(read->rd_length);
+	wire_data[0] = read->rd_eof ? xdr_one : xdr_zero;
+	wire_data[1] = cpu_to_be32(read->rd_length);
+	write_bytes_to_xdr_buf(xdr->buf, eof_offset, &wire_data, XDR_UNIT * 2);
 	return nfs_ok;
 }
 
@@ -5303,10 +5311,6 @@ nfsd4_encode_read_plus_data(struct nfsd4_compoundres *resp,
 	p = xdr_reserve_space(xdr, 4 + 8 + 4);
 	if (!p)
 		return nfserr_io;
-	if (resp->xdr->buf->page_len && splice_ok) {
-		WARN_ON_ONCE(splice_ok);
-		return nfserr_serverfault;
-	}
 
 	maxcount = min_t(unsigned long, read->rd_length,
 			 (xdr->buf->buflen - xdr->buf->len));
-- 
2.47.0


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [PATCH v4 3/9] NFSD: Insulate nfsd4_encode_read_plus() from page boundaries in the encode buffer
  2024-12-31  0:28 [PATCH v4 0/9] Fix XDR encoding near page boundaries cel
  2024-12-31  0:28 ` [PATCH v4 1/9] NFSD: Encode COMPOUND operation status on " cel
  2024-12-31  0:28 ` [PATCH v4 2/9] NFSD: Insulate nfsd4_encode_read() from page boundaries in the encode buffer cel
@ 2024-12-31  0:28 ` cel
  2024-12-31  0:28 ` [PATCH v4 4/9] NFSD: Insulate nfsd4_encode_read_plus_data() " cel
                   ` (6 subsequent siblings)
  9 siblings, 0 replies; 14+ messages in thread
From: cel @ 2024-12-31  0:28 UTC (permalink / raw)
  To: Neil Brown, Jeff Layton, Olga Kornievskaia, Dai Ngo, Tom Talpey
  Cc: linux-nfs, Rick Macklem, j.david.lists, Chuck Lever

From: Chuck Lever <chuck.lever@oracle.com>

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")
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
 fs/nfsd/nfs4xdr.c | 18 ++++++++++--------
 1 file changed, 10 insertions(+), 8 deletions(-)

diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index 094806fe1a32..00e2f4fc4e19 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -5336,16 +5336,17 @@ nfsd4_encode_read_plus(struct nfsd4_compoundres *resp, __be32 nfserr,
 	struct nfsd4_read *read = &u->read;
 	struct file *file = read->rd_nf->nf_file;
 	struct xdr_stream *xdr = resp->xdr;
-	int starting_len = xdr->buf->len;
+	unsigned int eof_offset;
+	__be32 wire_data[2];
 	u32 segments = 0;
-	__be32 *p;
 
 	if (nfserr)
 		return nfserr;
 
-	/* eof flag, segment count */
-	p = xdr_reserve_space(xdr, 4 + 4);
-	if (!p)
+	eof_offset = xdr_stream_pos(xdr);
+
+	/* Reserve space for the eof flag and segment count */
+	if (unlikely(!xdr_reserve_space(xdr, XDR_UNIT * 2)))
 		return nfserr_io;
 	xdr_commit_encode(xdr);
 
@@ -5355,15 +5356,16 @@ nfsd4_encode_read_plus(struct nfsd4_compoundres *resp, __be32 nfserr,
 
 	nfserr = nfsd4_encode_read_plus_data(resp, read);
 	if (nfserr) {
-		xdr_truncate_encode(xdr, starting_len);
+		xdr_truncate_encode(xdr, eof_offset);
 		return nfserr;
 	}
 
 	segments++;
 
 out:
-	p = xdr_encode_bool(p, read->rd_eof);
-	*p = cpu_to_be32(segments);
+	wire_data[0] = read->rd_eof ? xdr_one : xdr_zero;
+	wire_data[1] = cpu_to_be32(segments);
+	write_bytes_to_xdr_buf(xdr->buf, eof_offset, &wire_data, XDR_UNIT * 2);
 	return nfserr;
 }
 
-- 
2.47.0


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [PATCH v4 4/9] NFSD: Insulate nfsd4_encode_read_plus_data() from page boundaries in the encode buffer
  2024-12-31  0:28 [PATCH v4 0/9] Fix XDR encoding near page boundaries cel
                   ` (2 preceding siblings ...)
  2024-12-31  0:28 ` [PATCH v4 3/9] NFSD: Insulate nfsd4_encode_read_plus() " cel
@ 2024-12-31  0:28 ` cel
  2024-12-31  0:28 ` [PATCH v4 5/9] NFSD: Insulate nfsd4_encode_fattr4() " cel
                   ` (5 subsequent siblings)
  9 siblings, 0 replies; 14+ messages in thread
From: cel @ 2024-12-31  0:28 UTC (permalink / raw)
  To: Neil Brown, Jeff Layton, Olga Kornievskaia, Dai Ngo, Tom Talpey
  Cc: linux-nfs, Rick Macklem, j.david.lists, Chuck Lever

From: Chuck Lever <chuck.lever@oracle.com>

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")
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
 fs/nfsd/nfs4xdr.c | 25 +++++++++++++++++--------
 1 file changed, 17 insertions(+), 8 deletions(-)

diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index 00e2f4fc4e19..b770225d63dc 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -5304,14 +5304,21 @@ nfsd4_encode_read_plus_data(struct nfsd4_compoundres *resp,
 	struct file *file = read->rd_nf->nf_file;
 	struct xdr_stream *xdr = resp->xdr;
 	bool splice_ok = argp->splice_ok;
+	unsigned int offset_offset;
+	__be32 nfserr, wire_count;
 	unsigned long maxcount;
-	__be32 nfserr, *p;
+	__be64 wire_offset;
 
-	/* Content type, offset, byte count */
-	p = xdr_reserve_space(xdr, 4 + 8 + 4);
-	if (!p)
+	if (xdr_stream_encode_u32(xdr, NFS4_CONTENT_DATA) != XDR_UNIT)
 		return nfserr_io;
 
+	offset_offset = xdr_stream_pos(xdr);
+
+	/* Reserve space for the byte offset and count */
+	if (unlikely(!xdr_reserve_space(xdr, XDR_UNIT * 3)))
+		return nfserr_io;
+	xdr_commit_encode(xdr);
+
 	maxcount = min_t(unsigned long, read->rd_length,
 			 (xdr->buf->buflen - xdr->buf->len));
 
@@ -5322,10 +5329,12 @@ nfsd4_encode_read_plus_data(struct nfsd4_compoundres *resp,
 	if (nfserr)
 		return nfserr;
 
-	*p++ = cpu_to_be32(NFS4_CONTENT_DATA);
-	p = xdr_encode_hyper(p, read->rd_offset);
-	*p = cpu_to_be32(read->rd_length);
-
+	wire_offset = cpu_to_be64(read->rd_offset);
+	write_bytes_to_xdr_buf(xdr->buf, offset_offset, &wire_offset,
+			       XDR_UNIT * 2);
+	wire_count = cpu_to_be32(read->rd_length);
+	write_bytes_to_xdr_buf(xdr->buf, offset_offset + XDR_UNIT * 2,
+			       &wire_count, XDR_UNIT);
 	return nfs_ok;
 }
 
-- 
2.47.0


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [PATCH v4 5/9] NFSD: Insulate nfsd4_encode_fattr4() from page boundaries in the encode buffer
  2024-12-31  0:28 [PATCH v4 0/9] Fix XDR encoding near page boundaries cel
                   ` (3 preceding siblings ...)
  2024-12-31  0:28 ` [PATCH v4 4/9] NFSD: Insulate nfsd4_encode_read_plus_data() " cel
@ 2024-12-31  0:28 ` cel
  2024-12-31  0:28 ` [PATCH v4 6/9] NFSD: Insulate nfsd4_encode_readlink() " cel
                   ` (4 subsequent siblings)
  9 siblings, 0 replies; 14+ messages in thread
From: cel @ 2024-12-31  0:28 UTC (permalink / raw)
  To: Neil Brown, Jeff Layton, Olga Kornievskaia, Dai Ngo, Tom Talpey
  Cc: linux-nfs, Rick Macklem, j.david.lists, Chuck Lever

From: Chuck Lever <chuck.lever@oracle.com>

Commit ab04de60ae1c ("NFSD: Optimize nfsd4_encode_fattr()") 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, there's no guarantee that the pointer returned from
xdr_reserve_space() will still point to the correct reserved space
in the encode buffer after one or more intervening calls to
xdr_reserve_space(). It just happens to work with the current
implementation of xdr_reserve_space().

This commit effectively reverts the optimization.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
 fs/nfsd/nfs4xdr.c | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index b770225d63dc..d4ee633b01e3 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -3506,8 +3506,8 @@ nfsd4_encode_fattr4(struct svc_rqst *rqstp, struct xdr_stream *xdr,
 	struct nfsd4_fattr_args args;
 	struct svc_fh *tempfh = NULL;
 	int starting_len = xdr->buf->len;
-	__be32 *attrlen_p, status;
-	int attrlen_offset;
+	unsigned int attrlen_offset;
+	__be32 attrlen, status;
 	u32 attrmask[3];
 	int err;
 	struct nfsd4_compoundres *resp = rqstp->rq_resp;
@@ -3627,9 +3627,8 @@ nfsd4_encode_fattr4(struct svc_rqst *rqstp, struct xdr_stream *xdr,
 		goto out;
 
 	/* attr_vals */
-	attrlen_offset = xdr->buf->len;
-	attrlen_p = xdr_reserve_space(xdr, XDR_UNIT);
-	if (!attrlen_p)
+	attrlen_offset = xdr_stream_pos(xdr);
+	if (unlikely(!xdr_reserve_space(xdr, XDR_UNIT)))
 		goto out_resource;
 	bitmap_from_arr32(attr_bitmap, attrmask,
 			  ARRAY_SIZE(nfsd4_enc_fattr4_encode_ops));
@@ -3639,7 +3638,8 @@ nfsd4_encode_fattr4(struct svc_rqst *rqstp, struct xdr_stream *xdr,
 		if (status != nfs_ok)
 			goto out;
 	}
-	*attrlen_p = cpu_to_be32(xdr->buf->len - attrlen_offset - XDR_UNIT);
+	attrlen = cpu_to_be32(xdr_stream_pos(xdr) - attrlen_offset - XDR_UNIT);
+	write_bytes_to_xdr_buf(xdr->buf, attrlen_offset, &attrlen, XDR_UNIT);
 	status = nfs_ok;
 
 out:
-- 
2.47.0


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [PATCH v4 6/9] NFSD: Insulate nfsd4_encode_readlink() from page boundaries in the encode buffer
  2024-12-31  0:28 [PATCH v4 0/9] Fix XDR encoding near page boundaries cel
                   ` (4 preceding siblings ...)
  2024-12-31  0:28 ` [PATCH v4 5/9] NFSD: Insulate nfsd4_encode_fattr4() " cel
@ 2024-12-31  0:28 ` cel
  2024-12-31  0:28 ` [PATCH v4 7/9] NFSD: Refactor nfsd4_do_encode_secinfo() again cel
                   ` (3 subsequent siblings)
  9 siblings, 0 replies; 14+ messages in thread
From: cel @ 2024-12-31  0:28 UTC (permalink / raw)
  To: Neil Brown, Jeff Layton, Olga Kornievskaia, Dai Ngo, Tom Talpey
  Cc: linux-nfs, Rick Macklem, j.david.lists, Chuck Lever

From: Chuck Lever <chuck.lever@oracle.com>

There's no guarantee that the pointer returned from
xdr_reserve_space() will still point to the correct reserved space
in the encode buffer after one or more intervening calls to
xdr_reserve_space(). It just happens to work with the current
implementation of xdr_reserve_space().

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
 fs/nfsd/nfs4xdr.c | 22 ++++++++++------------
 1 file changed, 10 insertions(+), 12 deletions(-)

diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index d4ee633b01e3..1afe5fe41f22 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -4449,25 +4449,21 @@ nfsd4_encode_readlink(struct nfsd4_compoundres *resp, __be32 nfserr,
 		      union nfsd4_op_u *u)
 {
 	struct nfsd4_readlink *readlink = &u->readlink;
-	__be32 *p, *maxcount_p, zero = xdr_zero;
+	__be32 *p, wire_count, zero = xdr_zero;
 	struct xdr_stream *xdr = resp->xdr;
-	int length_offset = xdr->buf->len;
+	unsigned int length_offset;
 	int maxcount, status;
 
-	maxcount_p = xdr_reserve_space(xdr, XDR_UNIT);
-	if (!maxcount_p)
+	/* linktext4.count */
+	length_offset = xdr_stream_pos(xdr);
+	if (unlikely(!xdr_reserve_space(xdr, XDR_UNIT)))
 		return nfserr_resource;
-	maxcount = PAGE_SIZE;
 
+	/* linktext4.data */
+	maxcount = PAGE_SIZE;
 	p = xdr_reserve_space(xdr, maxcount);
 	if (!p)
 		return nfserr_resource;
-	/*
-	 * XXX: By default, vfs_readlink() will truncate symlinks if they
-	 * would overflow the buffer.  Is this kosher in NFSv4?  If not, one
-	 * easy fix is: if vfs_readlink() precisely fills the buffer, assume
-	 * that truncation occurred, and return NFS4ERR_RESOURCE.
-	 */
 	nfserr = nfsd_readlink(readlink->rl_rqstp, readlink->rl_fhp,
 						(char *)p, &maxcount);
 	if (nfserr == nfserr_isdir)
@@ -4480,7 +4476,9 @@ nfsd4_encode_readlink(struct nfsd4_compoundres *resp, __be32 nfserr,
 		nfserr = nfserrno(status);
 		goto out_err;
 	}
-	*maxcount_p = cpu_to_be32(maxcount);
+
+	wire_count = cpu_to_be32(maxcount);
+	write_bytes_to_xdr_buf(xdr->buf, length_offset, &wire_count, XDR_UNIT);
 	xdr_truncate_encode(xdr, length_offset + 4 + xdr_align_size(maxcount));
 	write_bytes_to_xdr_buf(xdr->buf, length_offset + 4 + maxcount, &zero,
 			       xdr_pad_size(maxcount));
-- 
2.47.0


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [PATCH v4 7/9] NFSD: Refactor nfsd4_do_encode_secinfo() again
  2024-12-31  0:28 [PATCH v4 0/9] Fix XDR encoding near page boundaries cel
                   ` (5 preceding siblings ...)
  2024-12-31  0:28 ` [PATCH v4 6/9] NFSD: Insulate nfsd4_encode_readlink() " cel
@ 2024-12-31  0:28 ` cel
  2024-12-31  0:28 ` [PATCH v4 8/9] NFSD: Insulate nfsd4_encode_secinfo() from page boundaries in the encode buffer cel
                   ` (2 subsequent siblings)
  9 siblings, 0 replies; 14+ messages in thread
From: cel @ 2024-12-31  0:28 UTC (permalink / raw)
  To: Neil Brown, Jeff Layton, Olga Kornievskaia, Dai Ngo, Tom Talpey
  Cc: linux-nfs, Rick Macklem, j.david.lists, Chuck Lever

From: Chuck Lever <chuck.lever@oracle.com>

Extract the code that encodes the secinfo4 union data type to
clarify the logic. The removed warning is pretty well obscured and
thus probably not terribly useful.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
 fs/nfsd/nfs4xdr.c | 65 +++++++++++++++++++++++++----------------------
 1 file changed, 34 insertions(+), 31 deletions(-)

diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index 1afe5fe41f22..a8b99e4796c9 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -4612,13 +4612,41 @@ nfsd4_encode_rpcsec_gss_info(struct xdr_stream *xdr,
 	return nfs_ok;
 }
 
+static __be32
+nfsd4_encode_secinfo4(struct xdr_stream *xdr, rpc_authflavor_t pf,
+		      u32 *supported)
+{
+	struct rpcsec_gss_info info;
+	__be32 status;
+
+	if (rpcauth_get_gssinfo(pf, &info) == 0) {
+		(*supported)++;
+
+		/* flavor */
+		status = nfsd4_encode_uint32_t(xdr, RPC_AUTH_GSS);
+		if (status != nfs_ok)
+			return status;
+		/* flavor_info */
+		status = nfsd4_encode_rpcsec_gss_info(xdr, &info);
+		if (status != nfs_ok)
+			return status;
+	} else if (pf < RPC_AUTH_MAXFLAVOR) {
+		(*supported)++;
+
+		/* flavor */
+		status = nfsd4_encode_uint32_t(xdr, pf);
+		if (status != nfs_ok)
+			return status;
+	}
+	return nfs_ok;
+}
+
 static __be32
 nfsd4_do_encode_secinfo(struct xdr_stream *xdr, struct svc_export *exp)
 {
 	u32 i, nflavs, supported;
 	struct exp_flavor_info *flavs;
 	struct exp_flavor_info def_flavs[2];
-	static bool report = true;
 	__be32 *flavorsp;
 	__be32 status;
 
@@ -4642,42 +4670,17 @@ nfsd4_do_encode_secinfo(struct xdr_stream *xdr, struct svc_export *exp)
 		}
 	}
 
-	supported = 0;
 	flavorsp = xdr_reserve_space(xdr, XDR_UNIT);
 	if (!flavorsp)
 		return nfserr_resource;
 
-	for (i = 0; i < nflavs; i++) {
-		rpc_authflavor_t pf = flavs[i].pseudoflavor;
-		struct rpcsec_gss_info info;
-
-		if (rpcauth_get_gssinfo(pf, &info) == 0) {
-			supported++;
-
-			/* flavor */
-			status = nfsd4_encode_uint32_t(xdr, RPC_AUTH_GSS);
-			if (status != nfs_ok)
-				return status;
-			/* flavor_info */
-			status = nfsd4_encode_rpcsec_gss_info(xdr, &info);
-			if (status != nfs_ok)
-				return status;
-		} else if (pf < RPC_AUTH_MAXFLAVOR) {
-			supported++;
-
-			/* flavor */
-			status = nfsd4_encode_uint32_t(xdr, pf);
-			if (status != nfs_ok)
-				return status;
-		} else {
-			if (report)
-				pr_warn("NFS: SECINFO: security flavor %u "
-					"is not supported\n", pf);
-		}
+	for (i = 0, supported = 0; i < nflavs; i++) {
+		status = nfsd4_encode_secinfo4(xdr, flavs[i].pseudoflavor,
+					       &supported);
+		if (status != nfs_ok)
+			return status;
 	}
 
-	if (nflavs != supported)
-		report = false;
 	*flavorsp = cpu_to_be32(supported);
 	return 0;
 }
-- 
2.47.0


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [PATCH v4 8/9] NFSD: Insulate nfsd4_encode_secinfo() from page boundaries in the encode buffer
  2024-12-31  0:28 [PATCH v4 0/9] Fix XDR encoding near page boundaries cel
                   ` (6 preceding siblings ...)
  2024-12-31  0:28 ` [PATCH v4 7/9] NFSD: Refactor nfsd4_do_encode_secinfo() again cel
@ 2024-12-31  0:28 ` cel
  2024-12-31  0:29 ` [PATCH v4 9/9] SUNRPC: Document validity guarantees of the pointer returned by reserve_space cel
  2025-01-02 13:23 ` [PATCH v4 0/9] Fix XDR encoding near page boundaries Jeff Layton
  9 siblings, 0 replies; 14+ messages in thread
From: cel @ 2024-12-31  0:28 UTC (permalink / raw)
  To: Neil Brown, Jeff Layton, Olga Kornievskaia, Dai Ngo, Tom Talpey
  Cc: linux-nfs, Rick Macklem, j.david.lists, Chuck Lever

From: Chuck Lever <chuck.lever@oracle.com>

There's no guarantee that the pointer returned from
xdr_reserve_space() will still point to the correct reserved space
in the encode buffer after one or more intervening calls to
xdr_reserve_space(). It just happens to work with the current
implementation of xdr_reserve_space().

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
 fs/nfsd/nfs4xdr.c | 18 ++++++++++--------
 1 file changed, 10 insertions(+), 8 deletions(-)

diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index a8b99e4796c9..348cd13f9012 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -4642,13 +4642,13 @@ nfsd4_encode_secinfo4(struct xdr_stream *xdr, rpc_authflavor_t pf,
 }
 
 static __be32
-nfsd4_do_encode_secinfo(struct xdr_stream *xdr, struct svc_export *exp)
+nfsd4_encode_SECINFO4resok(struct xdr_stream *xdr, struct svc_export *exp)
 {
 	u32 i, nflavs, supported;
 	struct exp_flavor_info *flavs;
 	struct exp_flavor_info def_flavs[2];
-	__be32 *flavorsp;
-	__be32 status;
+	unsigned int count_offset;
+	__be32 status, wire_count;
 
 	if (exp->ex_nflavors) {
 		flavs = exp->ex_flavors;
@@ -4670,8 +4670,8 @@ nfsd4_do_encode_secinfo(struct xdr_stream *xdr, struct svc_export *exp)
 		}
 	}
 
-	flavorsp = xdr_reserve_space(xdr, XDR_UNIT);
-	if (!flavorsp)
+	count_offset = xdr_stream_pos(xdr);
+	if (unlikely(!xdr_reserve_space(xdr, XDR_UNIT)))
 		return nfserr_resource;
 
 	for (i = 0, supported = 0; i < nflavs; i++) {
@@ -4681,7 +4681,9 @@ nfsd4_do_encode_secinfo(struct xdr_stream *xdr, struct svc_export *exp)
 			return status;
 	}
 
-	*flavorsp = cpu_to_be32(supported);
+	wire_count = cpu_to_be32(supported);
+	write_bytes_to_xdr_buf(xdr->buf, count_offset, &wire_count,
+			       XDR_UNIT);
 	return 0;
 }
 
@@ -4692,7 +4694,7 @@ nfsd4_encode_secinfo(struct nfsd4_compoundres *resp, __be32 nfserr,
 	struct nfsd4_secinfo *secinfo = &u->secinfo;
 	struct xdr_stream *xdr = resp->xdr;
 
-	return nfsd4_do_encode_secinfo(xdr, secinfo->si_exp);
+	return nfsd4_encode_SECINFO4resok(xdr, secinfo->si_exp);
 }
 
 static __be32
@@ -4702,7 +4704,7 @@ nfsd4_encode_secinfo_no_name(struct nfsd4_compoundres *resp, __be32 nfserr,
 	struct nfsd4_secinfo_no_name *secinfo = &u->secinfo_no_name;
 	struct xdr_stream *xdr = resp->xdr;
 
-	return nfsd4_do_encode_secinfo(xdr, secinfo->sin_exp);
+	return nfsd4_encode_SECINFO4resok(xdr, secinfo->sin_exp);
 }
 
 static __be32
-- 
2.47.0


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [PATCH v4 9/9] SUNRPC: Document validity guarantees of the pointer returned by reserve_space
  2024-12-31  0:28 [PATCH v4 0/9] Fix XDR encoding near page boundaries cel
                   ` (7 preceding siblings ...)
  2024-12-31  0:28 ` [PATCH v4 8/9] NFSD: Insulate nfsd4_encode_secinfo() from page boundaries in the encode buffer cel
@ 2024-12-31  0:29 ` cel
  2025-01-01 21:49   ` NeilBrown
  2025-01-02 13:21   ` Jeff Layton
  2025-01-02 13:23 ` [PATCH v4 0/9] Fix XDR encoding near page boundaries Jeff Layton
  9 siblings, 2 replies; 14+ messages in thread
From: cel @ 2024-12-31  0:29 UTC (permalink / raw)
  To: Neil Brown, Jeff Layton, Olga Kornievskaia, Dai Ngo, Tom Talpey
  Cc: linux-nfs, Rick Macklem, j.david.lists, Chuck Lever

From: Chuck Lever <chuck.lever@oracle.com>

A subtlety of this API is that if the @nbytes region traverses a
page boundary, the next __xdr_commit_encode will shift the data item
in the XDR encode buffer. This makes the returned pointer point to
something else, leading to unexpected behavior.

There are a few cases where the caller saves the returned pointer
and then later uses it to insert a computed value into an earlier
part of the stream. This can be safe only if either:

 - the data item is guaranteed to be in the XDR buffer's head, and
   thus is not ever going to be near a page boundary, or
 - the data item is no larger than 4 octets, since XDR alignment
   rules require all data items to start on 4-octet boundaries

But that safety is only an artifact of the current implementation.
It would be less brittle if these "safe" uses were eventually
replaced.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
 net/sunrpc/xdr.c | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/net/sunrpc/xdr.c b/net/sunrpc/xdr.c
index 62e07c330a66..4e003cb516fe 100644
--- a/net/sunrpc/xdr.c
+++ b/net/sunrpc/xdr.c
@@ -1097,6 +1097,12 @@ static noinline __be32 *xdr_get_next_encode_buffer(struct xdr_stream *xdr,
  * Checks that we have enough buffer space to encode 'nbytes' more
  * bytes of data. If so, update the total xdr_buf length, and
  * adjust the length of the current kvec.
+ *
+ * The returned pointer is valid only until the next call to
+ * xdr_reserve_space() or xdr_commit_encode() on @xdr. The current
+ * implementation of this API guarantees that space reserved for a
+ * four-byte data item remains valid until @xdr is destroyed, but
+ * that might not always be true in the future.
  */
 __be32 * xdr_reserve_space(struct xdr_stream *xdr, size_t nbytes)
 {
-- 
2.47.0


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* Re: [PATCH v4 9/9] SUNRPC: Document validity guarantees of the pointer returned by reserve_space
  2024-12-31  0:29 ` [PATCH v4 9/9] SUNRPC: Document validity guarantees of the pointer returned by reserve_space cel
@ 2025-01-01 21:49   ` NeilBrown
  2025-01-01 23:09     ` Chuck Lever
  2025-01-02 13:21   ` Jeff Layton
  1 sibling, 1 reply; 14+ messages in thread
From: NeilBrown @ 2025-01-01 21:49 UTC (permalink / raw)
  To: cel
  Cc: Jeff Layton, Olga Kornievskaia, Dai Ngo, Tom Talpey, linux-nfs,
	Rick Macklem, j.david.lists, Chuck Lever

On Tue, 31 Dec 2024, cel@kernel.org wrote:
> From: Chuck Lever <chuck.lever@oracle.com>
> 
> A subtlety of this API is that if the @nbytes region traverses a
> page boundary, the next __xdr_commit_encode will shift the data item
> in the XDR encode buffer. This makes the returned pointer point to
> something else, leading to unexpected behavior.
> 
> There are a few cases where the caller saves the returned pointer
> and then later uses it to insert a computed value into an earlier
> part of the stream. This can be safe only if either:
> 
>  - the data item is guaranteed to be in the XDR buffer's head, and
>    thus is not ever going to be near a page boundary, or
>  - the data item is no larger than 4 octets, since XDR alignment
>    rules require all data items to start on 4-octet boundaries
> 
> But that safety is only an artifact of the current implementation.
> It would be less brittle if these "safe" uses were eventually
> replaced.
> 
> Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
> ---
>  net/sunrpc/xdr.c | 6 ++++++
>  1 file changed, 6 insertions(+)
> 
> diff --git a/net/sunrpc/xdr.c b/net/sunrpc/xdr.c
> index 62e07c330a66..4e003cb516fe 100644
> --- a/net/sunrpc/xdr.c
> +++ b/net/sunrpc/xdr.c
> @@ -1097,6 +1097,12 @@ static noinline __be32 *xdr_get_next_encode_buffer(struct xdr_stream *xdr,
>   * Checks that we have enough buffer space to encode 'nbytes' more
>   * bytes of data. If so, update the total xdr_buf length, and
>   * adjust the length of the current kvec.
> + *
> + * The returned pointer is valid only until the next call to
> + * xdr_reserve_space() or xdr_commit_encode() on @xdr. The current
> + * implementation of this API guarantees that space reserved for a
> + * four-byte data item remains valid until @xdr is destroyed, but
> + * that might not always be true in the future.
>   */
>  __be32 * xdr_reserve_space(struct xdr_stream *xdr, size_t nbytes)
>  {
> -- 

This series all looks good to me
Reviewed-by: NeilBrown <neilb@suse.de>

though I do wonder if it would be better make the "four-byte" behaviour
a guaranteed part of the API rather than working around a problem that
doesn't currently exist and quite possibly never will.

Thanks,
NeilBrown

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v4 9/9] SUNRPC: Document validity guarantees of the pointer returned by reserve_space
  2025-01-01 21:49   ` NeilBrown
@ 2025-01-01 23:09     ` Chuck Lever
  0 siblings, 0 replies; 14+ messages in thread
From: Chuck Lever @ 2025-01-01 23:09 UTC (permalink / raw)
  To: NeilBrown, cel
  Cc: Jeff Layton, Olga Kornievskaia, Dai Ngo, Tom Talpey, linux-nfs,
	Rick Macklem, j.david.lists

On 1/1/25 4:49 PM, NeilBrown wrote:
> On Tue, 31 Dec 2024, cel@kernel.org wrote:
>> From: Chuck Lever <chuck.lever@oracle.com>
>>
>> A subtlety of this API is that if the @nbytes region traverses a
>> page boundary, the next __xdr_commit_encode will shift the data item
>> in the XDR encode buffer. This makes the returned pointer point to
>> something else, leading to unexpected behavior.
>>
>> There are a few cases where the caller saves the returned pointer
>> and then later uses it to insert a computed value into an earlier
>> part of the stream. This can be safe only if either:
>>
>>   - the data item is guaranteed to be in the XDR buffer's head, and
>>     thus is not ever going to be near a page boundary, or
>>   - the data item is no larger than 4 octets, since XDR alignment
>>     rules require all data items to start on 4-octet boundaries
>>
>> But that safety is only an artifact of the current implementation.
>> It would be less brittle if these "safe" uses were eventually
>> replaced.
>>
>> Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
>> ---
>>   net/sunrpc/xdr.c | 6 ++++++
>>   1 file changed, 6 insertions(+)
>>
>> diff --git a/net/sunrpc/xdr.c b/net/sunrpc/xdr.c
>> index 62e07c330a66..4e003cb516fe 100644
>> --- a/net/sunrpc/xdr.c
>> +++ b/net/sunrpc/xdr.c
>> @@ -1097,6 +1097,12 @@ static noinline __be32 *xdr_get_next_encode_buffer(struct xdr_stream *xdr,
>>    * Checks that we have enough buffer space to encode 'nbytes' more
>>    * bytes of data. If so, update the total xdr_buf length, and
>>    * adjust the length of the current kvec.
>> + *
>> + * The returned pointer is valid only until the next call to
>> + * xdr_reserve_space() or xdr_commit_encode() on @xdr. The current
>> + * implementation of this API guarantees that space reserved for a
>> + * four-byte data item remains valid until @xdr is destroyed, but
>> + * that might not always be true in the future.
>>    */
>>   __be32 * xdr_reserve_space(struct xdr_stream *xdr, size_t nbytes)
>>   {
>> -- 
> 
> This series all looks good to me
> Reviewed-by: NeilBrown <neilb@suse.de>

Thanks!


> though I do wonder if it would be better make the "four-byte" behaviour
> a guaranteed part of the API rather than working around a problem that
> doesn't currently exist and quite possibly never will.

It might be better, but I would like to fix the known problem and
document this expectation for the moment. I'm not closing the book
on this by any means.


-- 
Chuck Lever

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v4 9/9] SUNRPC: Document validity guarantees of the pointer returned by reserve_space
  2024-12-31  0:29 ` [PATCH v4 9/9] SUNRPC: Document validity guarantees of the pointer returned by reserve_space cel
  2025-01-01 21:49   ` NeilBrown
@ 2025-01-02 13:21   ` Jeff Layton
  1 sibling, 0 replies; 14+ messages in thread
From: Jeff Layton @ 2025-01-02 13:21 UTC (permalink / raw)
  To: cel, Neil Brown, Olga Kornievskaia, Dai Ngo, Tom Talpey
  Cc: linux-nfs, Rick Macklem, j.david.lists, Chuck Lever

On Mon, 2024-12-30 at 19:29 -0500, cel@kernel.org wrote:
> From: Chuck Lever <chuck.lever@oracle.com>
> 
> A subtlety of this API is that if the @nbytes region traverses a
> page boundary, the next __xdr_commit_encode will shift the data item
> in the XDR encode buffer. This makes the returned pointer point to
> something else, leading to unexpected behavior.
> 
> There are a few cases where the caller saves the returned pointer
> and then later uses it to insert a computed value into an earlier
> part of the stream. This can be safe only if either:
> 
>  - the data item is guaranteed to be in the XDR buffer's head, and
>    thus is not ever going to be near a page boundary, or
>  - the data item is no larger than 4 octets, since XDR alignment
>    rules require all data items to start on 4-octet boundaries
> 
> But that safety is only an artifact of the current implementation.
> It would be less brittle if these "safe" uses were eventually
> replaced.
> 
> Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
> ---
>  net/sunrpc/xdr.c | 6 ++++++
>  1 file changed, 6 insertions(+)
> 
> diff --git a/net/sunrpc/xdr.c b/net/sunrpc/xdr.c
> index 62e07c330a66..4e003cb516fe 100644
> --- a/net/sunrpc/xdr.c
> +++ b/net/sunrpc/xdr.c
> @@ -1097,6 +1097,12 @@ static noinline __be32 *xdr_get_next_encode_buffer(struct xdr_stream *xdr,
>   * Checks that we have enough buffer space to encode 'nbytes' more
>   * bytes of data. If so, update the total xdr_buf length, and
>   * adjust the length of the current kvec.
> + *
> + * The returned pointer is valid only until the next call to
> + * xdr_reserve_space() or xdr_commit_encode() on @xdr. The current
> + * implementation of this API guarantees that space reserved for a
> + * four-byte data item remains valid until @xdr is destroyed, but
> + * that might not always be true in the future.
>   */
>  __be32 * xdr_reserve_space(struct xdr_stream *xdr, size_t nbytes)
>  {

I agree with Neil that this API could do with less footguns, but this
does seem to be an improvement.

Reviewed-by: Jeff Layton <jlayton@kernel.org>

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v4 0/9] Fix XDR encoding near page boundaries
  2024-12-31  0:28 [PATCH v4 0/9] Fix XDR encoding near page boundaries cel
                   ` (8 preceding siblings ...)
  2024-12-31  0:29 ` [PATCH v4 9/9] SUNRPC: Document validity guarantees of the pointer returned by reserve_space cel
@ 2025-01-02 13:23 ` Jeff Layton
  9 siblings, 0 replies; 14+ messages in thread
From: Jeff Layton @ 2025-01-02 13:23 UTC (permalink / raw)
  To: cel, Neil Brown, Olga Kornievskaia, Dai Ngo, Tom Talpey
  Cc: linux-nfs, Rick Macklem, j.david.lists, Chuck Lever

On Mon, 2024-12-30 at 19:28 -0500, cel@kernel.org wrote:
> From: Chuck Lever <chuck.lever@oracle.com>
> 
> Refresh the patch series to address the longstanding bug pointed out
> by J David and Rick Macklem.
> 
> Changes since v2:
> - Address same issue in NFSv4 READLINK and SECINFO operations
> - Update kdoc comment for xdr_reserve_space()
> 
> Chuck Lever (9):
>   NFSD: Encode COMPOUND operation status on page boundaries
>   NFSD: Insulate nfsd4_encode_read() from page boundaries in the encode
>     buffer
>   NFSD: Insulate nfsd4_encode_read_plus() from page boundaries in the
>     encode buffer
>   NFSD: Insulate nfsd4_encode_read_plus_data() from page boundaries in
>     the encode buffer
>   NFSD: Insulate nfsd4_encode_fattr4() from page boundaries in the
>     encode buffer
>   NFSD: Insulate nfsd4_encode_readlink() from page boundaries in the
>     encode buffer
>   NFSD: Refactor nfsd4_do_encode_secinfo() again
>   NFSD: Insulate nfsd4_encode_secinfo() from page boundaries in the
>     encode buffer
>   SUNRPC: Document validity guarantees of the pointer returned by
>     reserve_space
> 
>  fs/nfsd/nfs4xdr.c | 214 +++++++++++++++++++++++++---------------------
>  net/sunrpc/xdr.c  |   6 ++
>  2 files changed, 122 insertions(+), 98 deletions(-)
> 

Reviewed-by: Jeff Layton <jlayton@kernel.org>

^ permalink raw reply	[flat|nested] 14+ messages in thread

end of thread, other threads:[~2025-01-02 13:23 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-12-31  0:28 [PATCH v4 0/9] Fix XDR encoding near page boundaries cel
2024-12-31  0:28 ` [PATCH v4 1/9] NFSD: Encode COMPOUND operation status on " cel
2024-12-31  0:28 ` [PATCH v4 2/9] NFSD: Insulate nfsd4_encode_read() from page boundaries in the encode buffer cel
2024-12-31  0:28 ` [PATCH v4 3/9] NFSD: Insulate nfsd4_encode_read_plus() " cel
2024-12-31  0:28 ` [PATCH v4 4/9] NFSD: Insulate nfsd4_encode_read_plus_data() " cel
2024-12-31  0:28 ` [PATCH v4 5/9] NFSD: Insulate nfsd4_encode_fattr4() " cel
2024-12-31  0:28 ` [PATCH v4 6/9] NFSD: Insulate nfsd4_encode_readlink() " cel
2024-12-31  0:28 ` [PATCH v4 7/9] NFSD: Refactor nfsd4_do_encode_secinfo() again cel
2024-12-31  0:28 ` [PATCH v4 8/9] NFSD: Insulate nfsd4_encode_secinfo() from page boundaries in the encode buffer cel
2024-12-31  0:29 ` [PATCH v4 9/9] SUNRPC: Document validity guarantees of the pointer returned by reserve_space cel
2025-01-01 21:49   ` NeilBrown
2025-01-01 23:09     ` Chuck Lever
2025-01-02 13:21   ` Jeff Layton
2025-01-02 13:23 ` [PATCH v4 0/9] Fix XDR encoding near page boundaries Jeff Layton

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox