diff options
author | Trond Myklebust <Trond.Myklebust@netapp.com> | 2010-06-16 13:57:32 -0400 |
---|---|---|
committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2010-06-22 13:21:18 -0400 |
commit | b76ce56192bcf618013fb9aecd83488cffd645cc (patch) | |
tree | 1a071da8b3e4fcc1fbfe67c99cc622372502edb2 | |
parent | f799bdb355edaabd81b778087613409a8932fbe9 (diff) |
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: Trond Myklebust <Trond.Myklebust@netapp.com>
Cc: stable@kernel.org
-rw-r--r-- | net/sunrpc/xprtsock.c | 38 |
1 files changed, 22 insertions, 16 deletions
diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c index 2a9675136c68..7ca65c7005ea 100644 --- a/net/sunrpc/xprtsock.c +++ b/net/sunrpc/xprtsock.c | |||
@@ -210,7 +210,8 @@ struct sock_xprt { | |||
210 | * State of TCP reply receive | 210 | * State of TCP reply receive |
211 | */ | 211 | */ |
212 | __be32 tcp_fraghdr, | 212 | __be32 tcp_fraghdr, |
213 | tcp_xid; | 213 | tcp_xid, |
214 | tcp_calldir; | ||
214 | 215 | ||
215 | u32 tcp_offset, | 216 | u32 tcp_offset, |
216 | tcp_reclen; | 217 | tcp_reclen; |
@@ -927,7 +928,7 @@ static inline void xs_tcp_read_calldir(struct sock_xprt *transport, | |||
927 | { | 928 | { |
928 | size_t len, used; | 929 | size_t len, used; |
929 | u32 offset; | 930 | u32 offset; |
930 | __be32 calldir; | 931 | char *p; |
931 | 932 | ||
932 | /* | 933 | /* |
933 | * We want transport->tcp_offset to be 8 at the end of this routine | 934 | * 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, | |||
936 | * transport->tcp_offset is 4 (after having already read the xid). | 937 | * transport->tcp_offset is 4 (after having already read the xid). |
937 | */ | 938 | */ |
938 | offset = transport->tcp_offset - sizeof(transport->tcp_xid); | 939 | offset = transport->tcp_offset - sizeof(transport->tcp_xid); |
939 | len = sizeof(calldir) - offset; | 940 | len = sizeof(transport->tcp_calldir) - offset; |
940 | dprintk("RPC: reading CALL/REPLY flag (%Zu bytes)\n", len); | 941 | dprintk("RPC: reading CALL/REPLY flag (%Zu bytes)\n", len); |
941 | used = xdr_skb_read_bits(desc, &calldir, len); | 942 | p = ((char *) &transport->tcp_calldir) + offset; |
943 | used = xdr_skb_read_bits(desc, p, len); | ||
942 | transport->tcp_offset += used; | 944 | transport->tcp_offset += used; |
943 | if (used != len) | 945 | if (used != len) |
944 | return; | 946 | return; |
945 | transport->tcp_flags &= ~TCP_RCV_READ_CALLDIR; | 947 | transport->tcp_flags &= ~TCP_RCV_READ_CALLDIR; |
946 | transport->tcp_flags |= TCP_RCV_COPY_CALLDIR; | ||
947 | transport->tcp_flags |= TCP_RCV_COPY_DATA; | ||
948 | /* | 948 | /* |
949 | * We don't yet have the XDR buffer, so we will write the calldir | 949 | * We don't yet have the XDR buffer, so we will write the calldir |
950 | * out after we get the buffer from the 'struct rpc_rqst' | 950 | * out after we get the buffer from the 'struct rpc_rqst' |
951 | */ | 951 | */ |
952 | if (ntohl(calldir) == RPC_REPLY) | 952 | switch (ntohl(transport->tcp_calldir)) { |
953 | case RPC_REPLY: | ||
954 | transport->tcp_flags |= TCP_RCV_COPY_CALLDIR; | ||
955 | transport->tcp_flags |= TCP_RCV_COPY_DATA; | ||
953 | transport->tcp_flags |= TCP_RPC_REPLY; | 956 | transport->tcp_flags |= TCP_RPC_REPLY; |
954 | else | 957 | break; |
958 | case RPC_CALL: | ||
959 | transport->tcp_flags |= TCP_RCV_COPY_CALLDIR; | ||
960 | transport->tcp_flags |= TCP_RCV_COPY_DATA; | ||
955 | transport->tcp_flags &= ~TCP_RPC_REPLY; | 961 | transport->tcp_flags &= ~TCP_RPC_REPLY; |
956 | dprintk("RPC: reading %s CALL/REPLY flag %08x\n", | 962 | break; |
957 | (transport->tcp_flags & TCP_RPC_REPLY) ? | 963 | default: |
958 | "reply for" : "request with", calldir); | 964 | dprintk("RPC: invalid request message type\n"); |
965 | xprt_force_disconnect(&transport->xprt); | ||
966 | } | ||
959 | xs_tcp_check_fraghdr(transport); | 967 | xs_tcp_check_fraghdr(transport); |
960 | } | 968 | } |
961 | 969 | ||
@@ -975,12 +983,10 @@ static inline void xs_tcp_read_common(struct rpc_xprt *xprt, | |||
975 | /* | 983 | /* |
976 | * Save the RPC direction in the XDR buffer | 984 | * Save the RPC direction in the XDR buffer |
977 | */ | 985 | */ |
978 | __be32 calldir = transport->tcp_flags & TCP_RPC_REPLY ? | ||
979 | htonl(RPC_REPLY) : 0; | ||
980 | |||
981 | memcpy(rcvbuf->head[0].iov_base + transport->tcp_copied, | 986 | memcpy(rcvbuf->head[0].iov_base + transport->tcp_copied, |
982 | &calldir, sizeof(calldir)); | 987 | &transport->tcp_calldir, |
983 | transport->tcp_copied += sizeof(calldir); | 988 | sizeof(transport->tcp_calldir)); |
989 | transport->tcp_copied += sizeof(transport->tcp_calldir); | ||
984 | transport->tcp_flags &= ~TCP_RCV_COPY_CALLDIR; | 990 | transport->tcp_flags &= ~TCP_RCV_COPY_CALLDIR; |
985 | } | 991 | } |
986 | 992 | ||