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

nfsd: Incoming xdr_bufs may have content in tail buffer

Since the beginning, svcsock has built a received RPC Call message
by populating the xdr_buf's head, then placing the remaining
message bytes in the xdr_buf's page list. The xdr_buf's tail is
never populated.

This means that an NFSv4 COMPOUND containing an NFS WRITE operation
plus trailing operations has a page list that contains the WRITE
data payload followed by the trailing operations. NFSv4 XDR decoders
will not look in the xdr_buf's tail, ever, because svcsock never put
anything there.

To support transports that can pass the write payload in the
xdr_buf's pagelist and trailing content in the xdr_buf's tail,
introduce logic in READ_BUF that switches to the xdr_buf's tail vec
when the decoder runs out of content in rq_arg.pages.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Signed-off-by: J. Bruce Fields <bfields@redhat.com>

authored by

Chuck Lever and committed by
J. Bruce Fields
eae03e2a 0828170f

+21
+20
fs/nfsd/nfs4xdr.c
··· 159 159 */ 160 160 unsigned int avail = (char *)argp->end - (char *)argp->p; 161 161 __be32 *p; 162 + 163 + if (argp->pagelen == 0) { 164 + struct kvec *vec = &argp->rqstp->rq_arg.tail[0]; 165 + 166 + if (!argp->tail) { 167 + argp->tail = true; 168 + avail = vec->iov_len; 169 + argp->p = vec->iov_base; 170 + argp->end = vec->iov_base + avail; 171 + } 172 + 173 + if (avail < nbytes) 174 + return NULL; 175 + 176 + p = argp->p; 177 + argp->p += XDR_QUADLEN(nbytes); 178 + return p; 179 + } 180 + 162 181 if (avail + argp->pagelen < nbytes) 163 182 return NULL; 164 183 if (avail + PAGE_SIZE < nbytes) /* need more than a page !! */ ··· 4490 4471 args->end = rqstp->rq_arg.head[0].iov_base + rqstp->rq_arg.head[0].iov_len; 4491 4472 args->pagelist = rqstp->rq_arg.pages; 4492 4473 args->pagelen = rqstp->rq_arg.page_len; 4474 + args->tail = false; 4493 4475 args->tmpp = NULL; 4494 4476 args->to_free = NULL; 4495 4477 args->ops = args->iops;
+1
fs/nfsd/xdr4.h
··· 615 615 __be32 * end; 616 616 struct page ** pagelist; 617 617 int pagelen; 618 + bool tail; 618 619 __be32 tmp[8]; 619 620 __be32 * tmpp; 620 621 struct svcxdr_tmpbuf *to_free;