aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorDavid Howells <dhowells@redhat.com>2019-03-08 07:48:39 -0500
committerDavid S. Miller <davem@davemloft.net>2019-03-08 14:26:16 -0500
commit930c9f9125c85b5134b3e711bc252ecc094708e3 (patch)
tree804a83b96e57ca02eb07a35e6c06440f5e94e13d /net
parent2e990dfd13974d9eae493006f42ffb48707970ef (diff)
rxrpc: Fix client call connect/disconnect race
rxrpc_disconnect_client_call() reads the call's connection ID protocol value (call->cid) as part of that function's variable declarations. This is bad because it's not inside the locked section and so may race with someone granting use of the channel to the call. This manifests as an assertion failure (see below) where the call in the presumed channel (0 because call->cid wasn't set when we read it) doesn't match the call attached to the channel we were actually granted (if 1, 2 or 3). Fix this by moving the read and dependent calculations inside of the channel_lock section. Also, only set the channel number and pointer variables if cid is not zero (ie. unset). This problem can be induced by injecting an occasional error in rxrpc_wait_for_channel() before the call to schedule(). Make two further changes also: (1) Add a trace for wait failure in rxrpc_connect_call(). (2) Drop channel_lock before BUG'ing in the case of the assertion failure. The failure causes a trace akin to the following: rxrpc: Assertion failed - 18446612685268945920(0xffff8880beab8c00) == 18446612685268621312(0xffff8880bea69800) is false ------------[ cut here ]------------ kernel BUG at net/rxrpc/conn_client.c:824! ... RIP: 0010:rxrpc_disconnect_client_call+0x2bf/0x99d ... Call Trace: rxrpc_connect_call+0x902/0x9b3 ? wake_up_q+0x54/0x54 rxrpc_new_client_call+0x3a0/0x751 ? rxrpc_kernel_begin_call+0x141/0x1bc ? afs_alloc_call+0x1b5/0x1b5 rxrpc_kernel_begin_call+0x141/0x1bc afs_make_call+0x20c/0x525 ? afs_alloc_call+0x1b5/0x1b5 ? __lock_is_held+0x40/0x71 ? lockdep_init_map+0xaf/0x193 ? lockdep_init_map+0xaf/0x193 ? __lock_is_held+0x40/0x71 ? yfs_fs_fetch_data+0x33b/0x34a yfs_fs_fetch_data+0x33b/0x34a afs_fetch_data+0xdc/0x3b7 afs_read_dir+0x52d/0x97f afs_dir_iterate+0xa0/0x661 ? iterate_dir+0x63/0x141 iterate_dir+0xa2/0x141 ksys_getdents64+0x9f/0x11b ? filldir+0x111/0x111 ? do_syscall_64+0x3e/0x1a0 __x64_sys_getdents64+0x16/0x19 do_syscall_64+0x7d/0x1a0 entry_SYSCALL_64_after_hwframe+0x49/0xbe Fixes: 45025bceef17 ("rxrpc: Improve management and caching of client connection objects") Signed-off-by: David Howells <dhowells@redhat.com> Reviewed-by: Marc Dionne <marc.dionne@auristor.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r--net/rxrpc/conn_client.c20
1 files changed, 15 insertions, 5 deletions
diff --git a/net/rxrpc/conn_client.c b/net/rxrpc/conn_client.c
index b2adfa825363..f307a05076e1 100644
--- a/net/rxrpc/conn_client.c
+++ b/net/rxrpc/conn_client.c
@@ -704,6 +704,7 @@ int rxrpc_connect_call(struct rxrpc_sock *rx,
704 704
705 ret = rxrpc_wait_for_channel(call, gfp); 705 ret = rxrpc_wait_for_channel(call, gfp);
706 if (ret < 0) { 706 if (ret < 0) {
707 trace_rxrpc_client(call->conn, ret, rxrpc_client_chan_wait_failed);
707 rxrpc_disconnect_client_call(call); 708 rxrpc_disconnect_client_call(call);
708 goto out; 709 goto out;
709 } 710 }
@@ -774,16 +775,22 @@ static void rxrpc_set_client_reap_timer(struct rxrpc_net *rxnet)
774 */ 775 */
775void rxrpc_disconnect_client_call(struct rxrpc_call *call) 776void rxrpc_disconnect_client_call(struct rxrpc_call *call)
776{ 777{
777 unsigned int channel = call->cid & RXRPC_CHANNELMASK;
778 struct rxrpc_connection *conn = call->conn; 778 struct rxrpc_connection *conn = call->conn;
779 struct rxrpc_channel *chan = &conn->channels[channel]; 779 struct rxrpc_channel *chan = NULL;
780 struct rxrpc_net *rxnet = conn->params.local->rxnet; 780 struct rxrpc_net *rxnet = conn->params.local->rxnet;
781 unsigned int channel = -1;
782 u32 cid;
781 783
784 spin_lock(&conn->channel_lock);
785
786 cid = call->cid;
787 if (cid) {
788 channel = cid & RXRPC_CHANNELMASK;
789 chan = &conn->channels[channel];
790 }
782 trace_rxrpc_client(conn, channel, rxrpc_client_chan_disconnect); 791 trace_rxrpc_client(conn, channel, rxrpc_client_chan_disconnect);
783 call->conn = NULL; 792 call->conn = NULL;
784 793
785 spin_lock(&conn->channel_lock);
786
787 /* Calls that have never actually been assigned a channel can simply be 794 /* Calls that have never actually been assigned a channel can simply be
788 * discarded. If the conn didn't get used either, it will follow 795 * discarded. If the conn didn't get used either, it will follow
789 * immediately unless someone else grabs it in the meantime. 796 * immediately unless someone else grabs it in the meantime.
@@ -807,7 +814,10 @@ void rxrpc_disconnect_client_call(struct rxrpc_call *call)
807 goto out; 814 goto out;
808 } 815 }
809 816
810 ASSERTCMP(rcu_access_pointer(chan->call), ==, call); 817 if (rcu_access_pointer(chan->call) != call) {
818 spin_unlock(&conn->channel_lock);
819 BUG();
820 }
811 821
812 /* If a client call was exposed to the world, we save the result for 822 /* If a client call was exposed to the world, we save the result for
813 * retransmission. 823 * retransmission.