From: Mahanta Jambigi <mjambigi@linux.ibm.com>
To: sashiko-reviews@lists.linux.dev
Cc: linux-s390@vger.kernel.org,
Alexander Gordeev <agordeev@linux.ibm.com>,
Christian Borntraeger <borntraeger@linux.ibm.com>,
linux-rdma@vger.kernel.org, Vasily Gorbik <gor@linux.ibm.com>,
Heiko Carstens <hca@linux.ibm.com>,
netdev@vger.kernel.org
Subject: Re: [PATCH net v3] net/smc: fix lgr/lnk lifetime vs diag reader race
Date: Thu, 10 Sep 2026 11:24:10 +0530 [thread overview]
Message-ID: <5ab3dcd5-34e7-41f8-98a6-5af1bd1fd7b5@linux.ibm.com> (raw)
In-Reply-To: <20260909092315.64CC41F00A3A@smtp.kernel.org>
On 09/09/26 2:53 pm, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
>
> New issues:
> - [Medium] Premature unhashing of SMC sockets breaks netlink diagnostics for fallback and aborted connections.
>
> Pre-existing issues:
> - [High] Concurrent link failover clears link memory while diagnostic reader accesses it, causing a NULL pointer dereference.
> --
>
> commit c0cbdd828a851e80ad0c79e6d2ccf5b0b5d7e16e
> Author: Mahanta Jambigi <mjambigi@linux.ibm.com>
>
> net/smc: fix lgr/lnk lifetime vs diag reader race
>
> This commit aims to resolve a race condition between the SMC diagnostic dump
> reader and connection teardown. It introduces a mechanism to ensure sockets
> are unhashed before their link group and link references are released in
> smc_conn_free(), preventing the diagnostic reader from accessing freed
> structures.
>
>> The diag path therefore reduces to:
>> hold hash read_lock -> read conn->lgr -> if non-NULL, dereference -> done
>> with no new lock, no extra reference count, and no trylock.
>
> [Severity: High]
> This isn't a bug introduced by this patch, but does this approach still leave
> the link memory vulnerable to concurrent clearance during a link failover?
>
> When __smc_diag_dump() in net/smc/smc_diag.c iterates over the hash table, it
> extracts link = smc->conn.lnk. Concurrently, a link failure could trigger
> smcr_link_down() -> smc_switch_conns() -> smc_switch_link_and_count().
>
> The link is swapped and the old link's reference is dropped locklessly with
> respect to the dump:
>
> net/smc/smc_core.c:smc_switch_link_and_count() {
> ...
> smcr_link_put(conn->lnk);
> conn->lnk = to_lnk;
> ...
> }
>
> Next, smcr_link_down() calls smcr_link_clear(). When the reference count hits
> zero, __smcr_link_clear() zeroes out the link memory:
>
> net/smc/smc_core.c:__smcr_link_clear() {
> ...
> memset(lnk, 0, sizeof(struct smc_link));
> ...
> }
>
> The diagnostic reader then evaluates link->smcibdev->ibdev->name. Since the
> link was zeroed, link->smcibdev is NULL, leading to a fatal dereference.
> Can the diagnostic reader safely dereference link->smcibdev without holding
> any connection-level lock that protects the link from concurrent clearance?
This is a false positive. The key is the link refcount accounting.
A link starts with refcnt = 1 (set in smcr_link_init()). Each
connection on that link holds an additional ref taken in
smc_conn_create() via smcr_link_hold(), paired with the
smcr_link_put() in smc_conn_free(). So for a link with N active
connections, refcnt = 1 + N.
smc_switch_link_and_count() drops only the *per-connection* ref
for the migrated connection:
smcr_link_put(conn->lnk); /* refcnt: (1+N) -> (1+N-1) */
For refcount_dec_and_test() to return true and trigger
__smcr_link_clear() -> memset(), the refcount would need to reach
zero. But the base ref of 1 (set at link init) is still live, so the
count never hits zero from this put.
The base ref is dropped only inside smcr_link_clear() at line 1381:
smcr_link_put(lnk); /* theoretically last link_put */
smcr_link_clear() is called from smcr_link_down() *after*
smc_switch_conns() has already migrated all connections away. By
the time the base ref is dropped and __smcr_link_clear() fires,
no connection points to the old link anymore -- so the diag reader
cannot reach a being-zeroed link through conn->lnk.
The ordering in smcr_link_down() is:
1. smc_switch_conns() <- all conn->lnk updated, per-conn puts done
2. smcr_link_clear() <- base ref dropped, memset runs
Step 2 cannot interleave with step 1; memset is only reachable after
all connection refs have been dropped. The diag reader's conn->lnk
dereference is therefore safe.
>
>> diff --git a/net/smc/af_smc.c b/net/smc/af_smc.c
>> index e9f93b3ab435b..8c781a4a4485b 100644
>> --- a/net/smc/af_smc.c
>> +++ b/net/smc/af_smc.c
>> @@ -310,7 +310,15 @@ static int __smc_release(struct smc_sock *smc)
> [ ... ]
>> - sk->sk_prot->unhash(sk);
>> + /* Fallback sockets never call smc_conn_free(), so unhash directly.
>> + * Non-fallback sockets use smc_conn_unhash() so that the conn->unhashed
>> + * flag keeps the unhash exactly once even when smc_conn_free() already ran
>> + * first (e.g. via smc_conn_kill()).
>> + */
>
> [Severity: Medium]
> Is the assumption that fallback sockets never call smc_conn_free() accurate?
>
> If a late fallback occurs, it invokes smc_conn_abort(), which calls
> smc_conn_free() before switching to TCP fallback:
>
> net/smc/af_smc.c:smc_listen_decline() {
> ...
> smc_conn_abort(new_smc, local_first);
> if (reason_code < 0 ||
> smc_switch_to_fallback(new_smc, reason_code)) {
> ...
> }
>
> Does this mean fallback sockets can traverse the smc_conn_free() path?
>
>> + if (smc->use_fallback)
>> + sk->sk_prot->unhash(sk);
>> + else
>> + smc_conn_unhash(&smc->conn);
>>
> [ ... ]
>> diff --git a/net/smc/smc_core.c b/net/smc/smc_core.c
>> index 04aedd957543a..e302221c35e3f 100644
>> --- a/net/smc/smc_core.c
>> +++ b/net/smc/smc_core.c
>> @@ -1263,6 +1277,11 @@ void smc_conn_free(struct smc_connection *conn)
>> return;
>>
>> conn->freed = 1;
>> + /* Unhash before dropping lgr/lnk refs so the diag reader, which
>> + * iterates under the socket hash read_lock, cannot see a connection whose
>> + * lgr or lnk is being freed concurrently.
>> + */
>> + smc_conn_unhash(conn);
>
> [Severity: Medium]
> Will this prematurely unhash SMC sockets during connection aborts and late
> fallback transitions?
>
> Since smc_conn_free() is called by smc_conn_abort(), and late fallback calls
> smc_conn_abort() (e.g., smc_listen_decline() -> smc_conn_abort() ->
> smc_conn_free()), the socket is removed from the SMC hash table despite
> remaining an active, functioning fallback socket.
>
> Could this hide fallback and aborted sockets from diagnostic monitoring tools
> that rely on the hash table (using SMC_DIAG_MODE_FALLBACK_TCP), breaking
> observability for these sockets?
You are right. smc_conn_abort() calls smc_conn_free() while
use_fallback is still false, so placing smc_conn_unhash() inside
smc_conn_free() prematurely removes the socket from the hash before
smc_switch_to_fallback() runs — making live fallback sockets
invisible to smcss. I'll fix it in the next version.
next prev parent reply other threads:[~2026-09-10 5:54 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-08 9:22 [PATCH net v3] net/smc: fix lgr/lnk lifetime vs diag reader race Mahanta Jambigi
2026-09-09 9:23 ` sashiko-bot
2026-09-10 5:54 ` Mahanta Jambigi [this message]
2026-09-11 0:24 ` netdev-bot+sashiko
2026-09-11 8:10 ` Mahanta Jambigi
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=5ab3dcd5-34e7-41f8-98a6-5af1bd1fd7b5@linux.ibm.com \
--to=mjambigi@linux.ibm.com \
--cc=agordeev@linux.ibm.com \
--cc=borntraeger@linux.ibm.com \
--cc=gor@linux.ibm.com \
--cc=hca@linux.ibm.com \
--cc=linux-rdma@vger.kernel.org \
--cc=linux-s390@vger.kernel.org \
--cc=netdev@vger.kernel.org \
--cc=sashiko-reviews@lists.linux.dev \
/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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox