Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b76ce561 authored by Trond Myklebust's avatar Trond Myklebust
Browse files

SUNRPC: Fix a re-entrancy bug in xs_tcp_read_calldir()

If the attempt to read the calldir fails, then instead of storing the read
bytes, we currently discard them. This leads to a garbage final result when
upon re-entry to the same routine, we read the remaining bytes.

Fixes the regression in bugzilla number 16213. Please see
    https://bugzilla.kernel.org/show_bug.cgi?id=16213



Signed-off-by: default avatarTrond Myklebust <Trond.Myklebust@netapp.com>
Cc: stable@kernel.org
parent f799bdb3
Loading
Loading
Loading
Loading
+22 −16
Original line number Diff line number Diff line
@@ -210,7 +210,8 @@ struct sock_xprt {
	 * State of TCP reply receive
	 */
	__be32			tcp_fraghdr,
				tcp_xid;
				tcp_xid,
				tcp_calldir;

	u32			tcp_offset,
				tcp_reclen;
@@ -927,7 +928,7 @@ static inline void xs_tcp_read_calldir(struct sock_xprt *transport,
{
	size_t len, used;
	u32 offset;
	__be32	calldir;
	char *p;

	/*
	 * We want transport->tcp_offset to be 8 at the end of this routine
@@ -936,26 +937,33 @@ static inline void xs_tcp_read_calldir(struct sock_xprt *transport,
	 * transport->tcp_offset is 4 (after having already read the xid).
	 */
	offset = transport->tcp_offset - sizeof(transport->tcp_xid);
	len = sizeof(calldir) - offset;
	len = sizeof(transport->tcp_calldir) - offset;
	dprintk("RPC:       reading CALL/REPLY flag (%Zu bytes)\n", len);
	used = xdr_skb_read_bits(desc, &calldir, len);
	p = ((char *) &transport->tcp_calldir) + offset;
	used = xdr_skb_read_bits(desc, p, len);
	transport->tcp_offset += used;
	if (used != len)
		return;
	transport->tcp_flags &= ~TCP_RCV_READ_CALLDIR;
	transport->tcp_flags |= TCP_RCV_COPY_CALLDIR;
	transport->tcp_flags |= TCP_RCV_COPY_DATA;
	/*
	 * We don't yet have the XDR buffer, so we will write the calldir
	 * out after we get the buffer from the 'struct rpc_rqst'
	 */
	if (ntohl(calldir) == RPC_REPLY)
	switch (ntohl(transport->tcp_calldir)) {
	case RPC_REPLY:
		transport->tcp_flags |= TCP_RCV_COPY_CALLDIR;
		transport->tcp_flags |= TCP_RCV_COPY_DATA;
		transport->tcp_flags |= TCP_RPC_REPLY;
	else
		break;
	case RPC_CALL:
		transport->tcp_flags |= TCP_RCV_COPY_CALLDIR;
		transport->tcp_flags |= TCP_RCV_COPY_DATA;
		transport->tcp_flags &= ~TCP_RPC_REPLY;
	dprintk("RPC:       reading %s CALL/REPLY flag %08x\n",
			(transport->tcp_flags & TCP_RPC_REPLY) ?
				"reply for" : "request with", calldir);
		break;
	default:
		dprintk("RPC:       invalid request message type\n");
		xprt_force_disconnect(&transport->xprt);
	}
	xs_tcp_check_fraghdr(transport);
}

@@ -975,12 +983,10 @@ static inline void xs_tcp_read_common(struct rpc_xprt *xprt,
		/*
		 * Save the RPC direction in the XDR buffer
		 */
		__be32	calldir = transport->tcp_flags & TCP_RPC_REPLY ?
					htonl(RPC_REPLY) : 0;

		memcpy(rcvbuf->head[0].iov_base + transport->tcp_copied,
			&calldir, sizeof(calldir));
		transport->tcp_copied += sizeof(calldir);
			&transport->tcp_calldir,
			sizeof(transport->tcp_calldir));
		transport->tcp_copied += sizeof(transport->tcp_calldir);
		transport->tcp_flags &= ~TCP_RCV_COPY_CALLDIR;
	}