Skip to content

Commit 6365de1

Browse files
committed
rxrpc: Fix race between conn bundle lookup and bundle removal [ZDI-CAN-15975]
jira LE-1907 Rebuild_History Non-Buildable kernel-5.14.0-284.30.1.el9_2 commit-author David Howells <[email protected]> commit 3bcd6c7 After rxrpc_unbundle_conn() has removed a connection from a bundle, it checks to see if there are any conns with available channels and, if not, removes and attempts to destroy the bundle. Whilst it does check after grabbing client_bundles_lock that there are no connections attached, this races with rxrpc_look_up_bundle() retrieving the bundle, but not attaching a connection for the connection to be attached later. There is therefore a window in which the bundle can get destroyed before we manage to attach a new connection to it. Fix this by adding an "active" counter to struct rxrpc_bundle: (1) rxrpc_connect_call() obtains an active count by prepping/looking up a bundle and ditches it before returning. (2) If, during rxrpc_connect_call(), a connection is added to the bundle, this obtains an active count, which is held until the connection is discarded. (3) rxrpc_deactivate_bundle() is created to drop an active count on a bundle and destroy it when the active count reaches 0. The active count is checked inside client_bundles_lock() to prevent a race with rxrpc_look_up_bundle(). (4) rxrpc_unbundle_conn() then calls rxrpc_deactivate_bundle(). Fixes: 245500d ("rxrpc: Rewrite the client connection manager") Reported-by: [email protected] # ZDI-CAN-15975 Signed-off-by: David Howells <[email protected]> Tested-by: [email protected] cc: Marc Dionne <[email protected]> cc: [email protected] Signed-off-by: David S. Miller <[email protected]> (cherry picked from commit 3bcd6c7) Signed-off-by: Jonathan Maple <[email protected]>
1 parent 4a7885d commit 6365de1

File tree

2 files changed

+24
-15
lines changed

2 files changed

+24
-15
lines changed

net/rxrpc/ar-internal.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -405,6 +405,7 @@ enum rxrpc_conn_proto_state {
405405
struct rxrpc_bundle {
406406
struct rxrpc_conn_parameters params;
407407
refcount_t ref;
408+
atomic_t active; /* Number of active users */
408409
unsigned int debug_id;
409410
bool try_upgrade; /* True if the bundle is attempting upgrade */
410411
bool alloc_conn; /* True if someone's getting a conn */

net/rxrpc/conn_client.c

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ __read_mostly unsigned long rxrpc_conn_idle_client_fast_expiry = 2 * HZ;
4040
DEFINE_IDR(rxrpc_client_conn_ids);
4141
static DEFINE_SPINLOCK(rxrpc_conn_id_lock);
4242

43+
static void rxrpc_deactivate_bundle(struct rxrpc_bundle *bundle);
44+
4345
/*
4446
* Get a connection ID and epoch for a client connection from the global pool.
4547
* The connection struct pointer is then recorded in the idr radix tree. The
@@ -123,6 +125,7 @@ static struct rxrpc_bundle *rxrpc_alloc_bundle(struct rxrpc_conn_parameters *cp,
123125
bundle->params = *cp;
124126
rxrpc_get_peer(bundle->params.peer);
125127
refcount_set(&bundle->ref, 1);
128+
atomic_set(&bundle->active, 1);
126129
spin_lock_init(&bundle->channel_lock);
127130
INIT_LIST_HEAD(&bundle->waiting_calls);
128131
}
@@ -149,7 +152,7 @@ void rxrpc_put_bundle(struct rxrpc_bundle *bundle)
149152

150153
dead = __refcount_dec_and_test(&bundle->ref, &r);
151154

152-
_debug("PUT B=%x %d", d, r);
155+
_debug("PUT B=%x %d", d, r - 1);
153156
if (dead)
154157
rxrpc_free_bundle(bundle);
155158
}
@@ -338,6 +341,7 @@ static struct rxrpc_bundle *rxrpc_look_up_bundle(struct rxrpc_conn_parameters *c
338341
rxrpc_free_bundle(candidate);
339342
found_bundle:
340343
rxrpc_get_bundle(bundle);
344+
atomic_inc(&bundle->active);
341345
spin_unlock(&local->client_bundles_lock);
342346
_leave(" = %u [found]", bundle->debug_id);
343347
return bundle;
@@ -436,6 +440,7 @@ static void rxrpc_add_conn_to_bundle(struct rxrpc_bundle *bundle, gfp_t gfp)
436440
if (old)
437441
trace_rxrpc_client(old, -1, rxrpc_client_replace);
438442
candidate->bundle_shift = shift;
443+
atomic_inc(&bundle->active);
439444
bundle->conns[i] = candidate;
440445
for (j = 0; j < RXRPC_MAXCALLS; j++)
441446
set_bit(shift + j, &bundle->avail_chans);
@@ -726,6 +731,7 @@ int rxrpc_connect_call(struct rxrpc_sock *rx,
726731
smp_rmb();
727732

728733
out_put_bundle:
734+
rxrpc_deactivate_bundle(bundle);
729735
rxrpc_put_bundle(bundle);
730736
out:
731737
_leave(" = %d", ret);
@@ -901,9 +907,8 @@ void rxrpc_disconnect_client_call(struct rxrpc_bundle *bundle, struct rxrpc_call
901907
static void rxrpc_unbundle_conn(struct rxrpc_connection *conn)
902908
{
903909
struct rxrpc_bundle *bundle = conn->bundle;
904-
struct rxrpc_local *local = bundle->params.local;
905910
unsigned int bindex;
906-
bool need_drop = false, need_put = false;
911+
bool need_drop = false;
907912
int i;
908913

909914
_enter("C=%x", conn->debug_id);
@@ -922,15 +927,22 @@ static void rxrpc_unbundle_conn(struct rxrpc_connection *conn)
922927
}
923928
spin_unlock(&bundle->channel_lock);
924929

925-
/* If there are no more connections, remove the bundle */
926-
if (!bundle->avail_chans) {
927-
_debug("maybe unbundle");
928-
spin_lock(&local->client_bundles_lock);
930+
if (need_drop) {
931+
rxrpc_deactivate_bundle(bundle);
932+
rxrpc_put_connection(conn);
933+
}
934+
}
929935

930-
for (i = 0; i < ARRAY_SIZE(bundle->conns); i++)
931-
if (bundle->conns[i])
932-
break;
933-
if (i == ARRAY_SIZE(bundle->conns) && !bundle->params.exclusive) {
936+
/*
937+
* Drop the active count on a bundle.
938+
*/
939+
static void rxrpc_deactivate_bundle(struct rxrpc_bundle *bundle)
940+
{
941+
struct rxrpc_local *local = bundle->params.local;
942+
bool need_put = false;
943+
944+
if (atomic_dec_and_lock(&bundle->active, &local->client_bundles_lock)) {
945+
if (!bundle->params.exclusive) {
934946
_debug("erase bundle");
935947
rb_erase(&bundle->local_node, &local->client_bundles);
936948
need_put = true;
@@ -940,10 +952,6 @@ static void rxrpc_unbundle_conn(struct rxrpc_connection *conn)
940952
if (need_put)
941953
rxrpc_put_bundle(bundle);
942954
}
943-
944-
if (need_drop)
945-
rxrpc_put_connection(conn);
946-
_leave("");
947955
}
948956

949957
/*

0 commit comments

Comments
 (0)