SUNRPC: Fix a re-entrancy bug in xs_tcp_read_calldir()
authorTrond Myklebust <Trond.Myklebust@netapp.com>
Wed, 16 Jun 2010 17:57:32 +0000 (13:57 -0400)
committerTrond Myklebust <Trond.Myklebust@netapp.com>
Tue, 22 Jun 2010 17:21:18 +0000 (13:21 -0400)
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>
Cc: stable@kernel.org
net/sunrpc/xprtsock.c

index 2a9675136c68e487adcf02849c54680ed7fd0e98..7ca65c7005ea5d2445ddddd617017ab3e6539f29 100644 (file)
@@ -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;
        }