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
This commit is contained in:
parent
f799bdb355
commit
b76ce56192
1 changed files with 22 additions and 16 deletions
|
@ -210,7 +210,8 @@ struct sock_xprt {
|
||||||
* State of TCP reply receive
|
* State of TCP reply receive
|
||||||
*/
|
*/
|
||||||
__be32 tcp_fraghdr,
|
__be32 tcp_fraghdr,
|
||||||
tcp_xid;
|
tcp_xid,
|
||||||
|
tcp_calldir;
|
||||||
|
|
||||||
u32 tcp_offset,
|
u32 tcp_offset,
|
||||||
tcp_reclen;
|
tcp_reclen;
|
||||||
|
@ -927,7 +928,7 @@ static inline void xs_tcp_read_calldir(struct sock_xprt *transport,
|
||||||
{
|
{
|
||||||
size_t len, used;
|
size_t len, used;
|
||||||
u32 offset;
|
u32 offset;
|
||||||
__be32 calldir;
|
char *p;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We want transport->tcp_offset to be 8 at the end of this routine
|
* 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).
|
* transport->tcp_offset is 4 (after having already read the xid).
|
||||||
*/
|
*/
|
||||||
offset = transport->tcp_offset - sizeof(transport->tcp_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);
|
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;
|
transport->tcp_offset += used;
|
||||||
if (used != len)
|
if (used != len)
|
||||||
return;
|
return;
|
||||||
transport->tcp_flags &= ~TCP_RCV_READ_CALLDIR;
|
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
|
* 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'
|
* 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;
|
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;
|
transport->tcp_flags &= ~TCP_RPC_REPLY;
|
||||||
dprintk("RPC: reading %s CALL/REPLY flag %08x\n",
|
break;
|
||||||
(transport->tcp_flags & TCP_RPC_REPLY) ?
|
default:
|
||||||
"reply for" : "request with", calldir);
|
dprintk("RPC: invalid request message type\n");
|
||||||
|
xprt_force_disconnect(&transport->xprt);
|
||||||
|
}
|
||||||
xs_tcp_check_fraghdr(transport);
|
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
|
* 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,
|
memcpy(rcvbuf->head[0].iov_base + transport->tcp_copied,
|
||||||
&calldir, sizeof(calldir));
|
&transport->tcp_calldir,
|
||||||
transport->tcp_copied += sizeof(calldir);
|
sizeof(transport->tcp_calldir));
|
||||||
|
transport->tcp_copied += sizeof(transport->tcp_calldir);
|
||||||
transport->tcp_flags &= ~TCP_RCV_COPY_CALLDIR;
|
transport->tcp_flags &= ~TCP_RCV_COPY_CALLDIR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue