Skip to content

Commit f49b68d

Browse files
chuckleverJ. Bruce Fields
authored andcommitted
SUNRPC: xdr_stream_subsegment() must handle non-zero page_bases
xdr_stream_subsegment() was introduced in commit c1346a1 ("NFSD: Replace the internals of the READ_BUF() macro"). There are two call sites for xdr_stream_subsegment(). One is nfsd4_decode_write(), and the other is nfsd4_decode_setxattr(). Currently neither of these call sites calls this API when xdr_buf::page_base is a non-zero value. However, I'm about to add a case where page_base will sometimes not be zero when nfsd4_decode_write() invokes this API. Replace the logic in xdr_stream_subsegment() that advances to the next data item in the xdr_stream with something more generic in order to handle this new use case. Signed-off-by: Chuck Lever <[email protected]> Signed-off-by: J. Bruce Fields <[email protected]>
1 parent 8e70bf2 commit f49b68d

File tree

1 file changed

+17
-15
lines changed

1 file changed

+17
-15
lines changed

net/sunrpc/xdr.c

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1633,7 +1633,7 @@ EXPORT_SYMBOL_GPL(xdr_buf_subsegment);
16331633
* Sets up @subbuf to represent a portion of @xdr. The portion
16341634
* starts at the current offset in @xdr, and extends for a length
16351635
* of @nbytes. If this is successful, @xdr is advanced to the next
1636-
* position following that portion.
1636+
* XDR data item following that portion.
16371637
*
16381638
* Return values:
16391639
* %true: @subbuf has been initialized, and @xdr has been advanced.
@@ -1642,29 +1642,31 @@ EXPORT_SYMBOL_GPL(xdr_buf_subsegment);
16421642
bool xdr_stream_subsegment(struct xdr_stream *xdr, struct xdr_buf *subbuf,
16431643
unsigned int nbytes)
16441644
{
1645-
unsigned int remaining, offset, len;
1645+
unsigned int start = xdr_stream_pos(xdr);
1646+
unsigned int remaining, len;
16461647

1647-
if (xdr_buf_subsegment(xdr->buf, subbuf, xdr_stream_pos(xdr), nbytes))
1648+
/* Extract @subbuf and bounds-check the fn arguments */
1649+
if (xdr_buf_subsegment(xdr->buf, subbuf, start, nbytes))
16481650
return false;
16491651

1650-
if (subbuf->head[0].iov_len)
1651-
if (!__xdr_inline_decode(xdr, subbuf->head[0].iov_len))
1652-
return false;
1653-
1654-
remaining = subbuf->page_len;
1655-
offset = subbuf->page_base;
1656-
while (remaining) {
1657-
len = min_t(unsigned int, remaining, PAGE_SIZE) - offset;
1658-
1652+
/* Advance @xdr by @nbytes */
1653+
for (remaining = nbytes; remaining;) {
16591654
if (xdr->p == xdr->end && !xdr_set_next_buffer(xdr))
16601655
return false;
1661-
if (!__xdr_inline_decode(xdr, len))
1662-
return false;
16631656

1657+
len = (char *)xdr->end - (char *)xdr->p;
1658+
if (remaining <= len) {
1659+
xdr->p = (__be32 *)((char *)xdr->p +
1660+
(remaining + xdr_pad_size(nbytes)));
1661+
break;
1662+
}
1663+
1664+
xdr->p = (__be32 *)((char *)xdr->p + len);
1665+
xdr->end = xdr->p;
16641666
remaining -= len;
1665-
offset = 0;
16661667
}
16671668

1669+
xdr_stream_set_pos(xdr, start + nbytes);
16681670
return true;
16691671
}
16701672
EXPORT_SYMBOL_GPL(xdr_stream_subsegment);

0 commit comments

Comments
 (0)