From: Mahanta Jambigi <mjambigi@linux.ibm.com>
To: andrew+netdev@lunn.ch, davem@davemloft.net, edumazet@google.com,
kuba@kernel.org, pabeni@redhat.com, alibuda@linux.alibaba.com,
dust.li@linux.alibaba.com, sidraya@linux.ibm.com
Cc: pasic@linux.ibm.com, horms@kernel.org, tonylu@linux.alibaba.com,
guwen@linux.alibaba.com, hidayath@linux.ibm.com,
stable@vger.kernel.org, netdev@vger.kernel.org,
linux-s390@vger.kernel.org, linux-rdma@vger.kernel.org,
Mahanta Jambigi <mjambigi@linux.ibm.com>
Subject: [PATCH net v4] net/smc: fix lgr/lnk lifetime vs diag reader race
Date: Fri, 11 Sep 2026 11:09:06 +0200 [thread overview]
Message-ID: <20260911090906.1949163-1-mjambigi@linux.ibm.com> (raw)
The diag dump walks the socket hash table under a read_lock and dereferences
conn->lgr and conn->lnk. Two terminal teardown paths drop those references via
smc_conn_free() while the socket is still hashed:
- smc_conn_kill() -> smc_close_active_abort() -> smc_conn_free()
- smc_close_passive_work() -> smc_conn_free()
This allows the diag reader to dereference a freed lgr or lnk.
Fix it by unhashing the socket before smc_conn_free() is called at each of these
two sites. Any socket visible to the diag reader under the hash read_lock then
has valid conn->lgr and conn->lnk pointers.
Fixes: f16a7dd5cf27 ("smc: netlink interface for SMC sockets")
Fixes: 9dbe086c69b8 ("net/smc: fix invalid link access in dumping SMC-R connections")
Signed-off-by: Mahanta Jambigi <mjambigi@linux.ibm.com>
---
Changes in v4:
- dropped smc_conn_unhash() wrapper, conn->unhashed flag, and all
changes to af_smc.c, smc.h, smc_core.c and smc_core.h; smc_unhash_sk()
is already idempotent via sk_hashed(), so direct calls at the two
teardown sites in smc_close.c are sufficient
- dropped the __smc_release() hunk: it needs no change since the
subsequent unhash there is already a safe no-op
- fixed premature-unhash issue present in v3: smc_conn_free() must not
unhash because smc_conn_abort() calls it before smc_switch_to_fallback()
in both smc_listen_decline() and smc_connect_rdma() error paths;
unhashing there would make live fallback sockets invisible to smcss
- likewise, the ISM/RDMA retry loops (smc_find_ism_v2_device_serv(),
smc_find_rdma_v2_device_serv()) call smc_conn_abort() on a failed
attempt and then smc_conn_create() on the next device; unhashing in
smc_conn_free() would permanently hide the established connection from
smc_diag since smc_conn_create() does not re-hash the socket
Changes in v3:
- redesigned as a single patch; dropped the lgr_lnk_lock spinlock
approach and the 2-patch split
- fix is now at the socket hash layer: introduce smc_conn_unhash() with
a per-connection unhashed flag; smc_conn_free() unhashes before dropping
lgr/lnk refs, so any socket visible to the diag reader under the hash
read_lock has valid conn->lgr and conn->lnk pointers
- __smc_release() updated to call smc_conn_unhash() for non-fallback
sockets so the flag is honoured when smc_conn_free() already ran first
- smc_diag.c needs no changes; the hash read_lock invariant is
sufficient without any per-connection lock in the dump path
- dropped the clcsock/mutex_trylock fix as that will be addressed separately
Changes in v2:
- this is v2 of the 2-patch series; the earlier submission was mislabelled
[PATCH v3] but was in fact the first version sent to the list
- split into a 2-patch series; patch 1/2 adds per-connection lgr_lnk_lock
infrastructure to smc_core, patch 2/2 fixes the diag dump path using it
- dropped lock_sock()/release_sock() from __smc_diag_dump(); v1 held the
socket lock across all lgr/lnk dereferences, requiring the hash read_lock
to be dropped and re-acquired around each socket
- dropped the restart-from-head loop in smc_diag_dump_proto(); the new
design does not drop the hash read_lock mid-walk so the hlist truncation
concern no longer applies
- dropped refcount_inc_not_zero() socket pinning from the dump loop for the
same reason: the hash read_lock is now held for the full walk
- added per-connection lgr_lnk_lock spinlock to struct smc_connection;
conn->lgr and conn->lnk are NULLed under this lock in smc_conn_free()
before borrowed references are released, establishing the invariant: a
non-NULL conn->lgr seen under lgr_lnk_lock guarantees the lgr is alive
- added lgr_lnk_lock to smc_switch_link_and_count() to protect the conn->lnk
pointer swap from concurrent diag readers
- replaced mutex_lock() on clcsock_release_lock in smc_diag_msg_common_fill()
with mutex_trylock(); mutex_lock() was valid in v1 because the hash
spinlock had been dropped, but the new design holds the hash read_lock
throughout so only a non-sleeping trylock is safe; a failed trylock leaves
address fields zeroed, which is acceptable for a monitoring tool
- all conn->lgr and conn->lnk accesses in __smc_diag_dump() use a
snapshot-then-use pattern: fields are copied into local stack variables
under lgr_lnk_lock and nla_put() is called after releasing the lock,
avoiding any sleeping operation under the spinlock
net/smc/smc_close.c | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/net/smc/smc_close.c b/net/smc/smc_close.c
index bb0313ef5f7c..9e4b4e9b9d08 100644
--- a/net/smc/smc_close.c
+++ b/net/smc/smc_close.c
@@ -128,6 +128,11 @@ void smc_close_active_abort(struct smc_sock *smc)
struct sock *sk = &smc->sk;
bool release_clcsock = false;
+ /*
+ * Unhash before any branch calls smc_conn_free() and drops lgr/lnk refs;
+ * the diag reader must not see a hashed socket with freed lgr/lnk.
+ */
+ sk->sk_prot->unhash(sk);
if (sk->sk_state != SMC_INIT && smc->clcsock && smc->clcsock->sk) {
sk->sk_err = ECONNABORTED;
if (smc->clcsock && smc->clcsock->sk)
@@ -432,7 +436,12 @@ static void smc_close_passive_work(struct work_struct *work)
if (old_state != sk->sk_state) {
sk->sk_state_change(sk);
if ((sk->sk_state == SMC_CLOSED) &&
(sock_flag(sk, SOCK_DEAD) || !sk->sk_socket)) {
+ /*
+ * Unhash before smc_conn_free() drops lgr/lnk refs so the diag
+ * reader cannot see a hashed socket with freed lgr/lnk.
+ */
+ sk->sk_prot->unhash(sk);
smc_conn_free(conn);
if (smc->clcsock)
release_clcsock = true;
--
2.43.7
next reply other threads:[~2026-09-11 9:09 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-11 9:09 Mahanta Jambigi [this message]
2026-09-11 9:30 ` [PATCH net v4] net/smc: fix lgr/lnk lifetime vs diag reader race sashiko-bot
2026-09-15 15:13 ` Dust Li
2026-09-16 8:34 ` Mahanta Jambigi
2026-09-16 15:24 ` Dust Li
2026-09-17 7:45 ` Mahanta Jambigi
2026-09-17 15:53 ` Dust Li
2026-09-18 7:27 ` Mahanta Jambigi
2026-09-21 9:56 ` Dust Li
2026-09-22 13:27 ` Mahanta Jambigi
2026-09-22 16:01 ` Dust Li
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260911090906.1949163-1-mjambigi@linux.ibm.com \
--to=mjambigi@linux.ibm.com \
--cc=alibuda@linux.alibaba.com \
--cc=andrew+netdev@lunn.ch \
--cc=davem@davemloft.net \
--cc=dust.li@linux.alibaba.com \
--cc=edumazet@google.com \
--cc=guwen@linux.alibaba.com \
--cc=hidayath@linux.ibm.com \
--cc=horms@kernel.org \
--cc=kuba@kernel.org \
--cc=linux-rdma@vger.kernel.org \
--cc=linux-s390@vger.kernel.org \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
--cc=pasic@linux.ibm.com \
--cc=sidraya@linux.ibm.com \
--cc=stable@vger.kernel.org \
--cc=tonylu@linux.alibaba.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.