| From b76ce56192bcf618013fb9aecd83488cffd645cc Mon Sep 17 00:00:00 2001 |
| From: Trond Myklebust <Trond.Myklebust@netapp.com> |
| Date: Wed, 16 Jun 2010 13:57:32 -0400 |
| Subject: SUNRPC: Fix a re-entrancy bug in xs_tcp_read_calldir() |
| |
| From: Trond Myklebust <Trond.Myklebust@netapp.com> |
| |
| commit b76ce56192bcf618013fb9aecd83488cffd645cc upstream. |
| |
| 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: Trond Myklebust <Trond.Myklebust@netapp.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> |
| |
| --- |
| net/sunrpc/xprtsock.c | 38 ++++++++++++++++++++++---------------- |
| 1 file changed, 22 insertions(+), 16 deletions(-) |
| |
| --- a/net/sunrpc/xprtsock.c |
| +++ b/net/sunrpc/xprtsock.c |
| @@ -224,7 +224,8 @@ struct sock_xprt { |
| * State of TCP reply receive |
| */ |
| __be32 tcp_fraghdr, |
| - tcp_xid; |
| + tcp_xid, |
| + tcp_calldir; |
| |
| u32 tcp_offset, |
| tcp_reclen; |
| @@ -947,7 +948,7 @@ static inline void xs_tcp_read_calldir(s |
| { |
| size_t len, used; |
| u32 offset; |
| - __be32 calldir; |
| + char *p; |
| |
| /* |
| * We want transport->tcp_offset to be 8 at the end of this routine |
| @@ -956,26 +957,33 @@ static inline void xs_tcp_read_calldir(s |
| * 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); |
| } |
| |
| @@ -995,12 +1003,10 @@ static inline void xs_tcp_read_common(st |
| /* |
| * 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; |
| } |
| |