提交 f539bfdf 编写于 作者: D David Howells 提交者: Jialin Zhang

rxrpc: Fix race between conn bundle lookup and bundle removal [ZDI-CAN-15975]

stable inclusion
from stable-v5.10.157
commit 3535c632e6d16c98f76e615da8dc0cb2750c66cc
category: bugfix
bugzilla: https://gitee.com/src-openeuler/kernel/issues/I6VK2H
CVE: CVE-2023-2006

Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=3535c632e6d16c98f76e615da8dc0cb2750c66cc

--------------------------------

[ Upstream commit 3bcd6c7e ]

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: 245500d8 ("rxrpc: Rewrite the client connection manager")
Reported-by: zdi-disclosures@trendmicro.com # ZDI-CAN-15975
Signed-off-by: NDavid Howells <dhowells@redhat.com>
Tested-by: zdi-disclosures@trendmicro.com
cc: Marc Dionne <marc.dionne@auristor.com>
cc: linux-afs@lists.infradead.org
Signed-off-by: NDavid S. Miller <davem@davemloft.net>

Conflicts:
	net/rxrpc/ar-internal.h
	net/rxrpc/conn_client.c
Signed-off-by: NWang Yufen <wangyufen@huawei.com>
Reviewed-by: NYue Haibing <yuehaibing@huawei.com>
Reviewed-by: NWang Weiyang <wangweiyang2@huawei.com>
Signed-off-by: NJialin Zhang <zhangjialin11@huawei.com>
上级 771a10a7
...@@ -392,6 +392,7 @@ enum rxrpc_conn_proto_state { ...@@ -392,6 +392,7 @@ enum rxrpc_conn_proto_state {
struct rxrpc_bundle { struct rxrpc_bundle {
struct rxrpc_conn_parameters params; struct rxrpc_conn_parameters params;
atomic_t usage; atomic_t usage;
atomic_t active; /* Number of active users */
unsigned int debug_id; unsigned int debug_id;
bool try_upgrade; /* True if the bundle is attempting upgrade */ bool try_upgrade; /* True if the bundle is attempting upgrade */
bool alloc_conn; /* True if someone's getting a conn */ bool alloc_conn; /* True if someone's getting a conn */
......
...@@ -40,6 +40,8 @@ __read_mostly unsigned long rxrpc_conn_idle_client_fast_expiry = 2 * HZ; ...@@ -40,6 +40,8 @@ __read_mostly unsigned long rxrpc_conn_idle_client_fast_expiry = 2 * HZ;
DEFINE_IDR(rxrpc_client_conn_ids); DEFINE_IDR(rxrpc_client_conn_ids);
static DEFINE_SPINLOCK(rxrpc_conn_id_lock); static DEFINE_SPINLOCK(rxrpc_conn_id_lock);
static void rxrpc_deactivate_bundle(struct rxrpc_bundle *bundle);
/* /*
* Get a connection ID and epoch for a client connection from the global pool. * Get a connection ID and epoch for a client connection from the global pool.
* The connection struct pointer is then recorded in the idr radix tree. The * 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, ...@@ -123,6 +125,7 @@ static struct rxrpc_bundle *rxrpc_alloc_bundle(struct rxrpc_conn_parameters *cp,
bundle->params = *cp; bundle->params = *cp;
rxrpc_get_peer(bundle->params.peer); rxrpc_get_peer(bundle->params.peer);
atomic_set(&bundle->usage, 1); atomic_set(&bundle->usage, 1);
atomic_set(&bundle->active, 1);
spin_lock_init(&bundle->channel_lock); spin_lock_init(&bundle->channel_lock);
INIT_LIST_HEAD(&bundle->waiting_calls); INIT_LIST_HEAD(&bundle->waiting_calls);
} }
...@@ -341,6 +344,7 @@ static struct rxrpc_bundle *rxrpc_look_up_bundle(struct rxrpc_conn_parameters *c ...@@ -341,6 +344,7 @@ static struct rxrpc_bundle *rxrpc_look_up_bundle(struct rxrpc_conn_parameters *c
rxrpc_free_bundle(candidate); rxrpc_free_bundle(candidate);
found_bundle: found_bundle:
rxrpc_get_bundle(bundle); rxrpc_get_bundle(bundle);
atomic_inc(&bundle->active);
spin_unlock(&local->client_bundles_lock); spin_unlock(&local->client_bundles_lock);
_leave(" = %u [found]", bundle->debug_id); _leave(" = %u [found]", bundle->debug_id);
return bundle; return bundle;
...@@ -438,6 +442,7 @@ static void rxrpc_add_conn_to_bundle(struct rxrpc_bundle *bundle, gfp_t gfp) ...@@ -438,6 +442,7 @@ static void rxrpc_add_conn_to_bundle(struct rxrpc_bundle *bundle, gfp_t gfp)
if (old) if (old)
trace_rxrpc_client(old, -1, rxrpc_client_replace); trace_rxrpc_client(old, -1, rxrpc_client_replace);
candidate->bundle_shift = shift; candidate->bundle_shift = shift;
atomic_inc(&bundle->active);
bundle->conns[i] = candidate; bundle->conns[i] = candidate;
for (j = 0; j < RXRPC_MAXCALLS; j++) for (j = 0; j < RXRPC_MAXCALLS; j++)
set_bit(shift + j, &bundle->avail_chans); set_bit(shift + j, &bundle->avail_chans);
...@@ -728,6 +733,7 @@ int rxrpc_connect_call(struct rxrpc_sock *rx, ...@@ -728,6 +733,7 @@ int rxrpc_connect_call(struct rxrpc_sock *rx,
smp_rmb(); smp_rmb();
out_put_bundle: out_put_bundle:
rxrpc_deactivate_bundle(bundle);
rxrpc_put_bundle(bundle); rxrpc_put_bundle(bundle);
out: out:
_leave(" = %d", ret); _leave(" = %d", ret);
...@@ -903,9 +909,8 @@ void rxrpc_disconnect_client_call(struct rxrpc_bundle *bundle, struct rxrpc_call ...@@ -903,9 +909,8 @@ void rxrpc_disconnect_client_call(struct rxrpc_bundle *bundle, struct rxrpc_call
static void rxrpc_unbundle_conn(struct rxrpc_connection *conn) static void rxrpc_unbundle_conn(struct rxrpc_connection *conn)
{ {
struct rxrpc_bundle *bundle = conn->bundle; struct rxrpc_bundle *bundle = conn->bundle;
struct rxrpc_local *local = bundle->params.local;
unsigned int bindex; unsigned int bindex;
bool need_drop = false, need_put = false; bool need_drop = false;
int i; int i;
_enter("C=%x", conn->debug_id); _enter("C=%x", conn->debug_id);
...@@ -924,15 +929,22 @@ static void rxrpc_unbundle_conn(struct rxrpc_connection *conn) ...@@ -924,15 +929,22 @@ static void rxrpc_unbundle_conn(struct rxrpc_connection *conn)
} }
spin_unlock(&bundle->channel_lock); spin_unlock(&bundle->channel_lock);
/* If there are no more connections, remove the bundle */ if (need_drop) {
if (!bundle->avail_chans) { rxrpc_deactivate_bundle(bundle);
_debug("maybe unbundle"); rxrpc_put_connection(conn);
spin_lock(&local->client_bundles_lock); }
}
for (i = 0; i < ARRAY_SIZE(bundle->conns); i++) /*
if (bundle->conns[i]) * Drop the active count on a bundle.
break; */
if (i == ARRAY_SIZE(bundle->conns) && !bundle->params.exclusive) { static void rxrpc_deactivate_bundle(struct rxrpc_bundle *bundle)
{
struct rxrpc_local *local = bundle->params.local;
bool need_put = false;
if (atomic_dec_and_lock(&bundle->active, &local->client_bundles_lock)) {
if (!bundle->params.exclusive) {
_debug("erase bundle"); _debug("erase bundle");
rb_erase(&bundle->local_node, &local->client_bundles); rb_erase(&bundle->local_node, &local->client_bundles);
need_put = true; need_put = true;
...@@ -942,10 +954,6 @@ static void rxrpc_unbundle_conn(struct rxrpc_connection *conn) ...@@ -942,10 +954,6 @@ static void rxrpc_unbundle_conn(struct rxrpc_connection *conn)
if (need_put) if (need_put)
rxrpc_put_bundle(bundle); rxrpc_put_bundle(bundle);
} }
if (need_drop)
rxrpc_put_connection(conn);
_leave("");
} }
/* /*
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册