From: Dust Li <dust.li@linux.alibaba.com>
To: Mahanta Jambigi <mjambigi@linux.ibm.com>,
andrew+netdev@lunn.ch, davem@davemloft.net, edumazet@google.com,
kuba@kernel.org, pabeni@redhat.com, alibuda@linux.alibaba.com,
sidraya@linux.ibm.com, hidayath@linux.ibm.com
Cc: pasic@linux.ibm.com, horms@kernel.org, tonylu@linux.alibaba.com,
guwen@linux.alibaba.com, stable@vger.kernel.org,
netdev@vger.kernel.org, linux-s390@vger.kernel.org,
linux-rdma@vger.kernel.org
Subject: Re: [PATCH net v2 0/2] net/smc: fix diag dump lifetime races
Date: Fri, 4 Sep 2026 23:21:49 +0800 [thread overview]
Message-ID: <apriDec7yHBkQgNv@linux.alibaba.com> (raw)
In-Reply-To: <899816c9-c908-4d64-8f5c-ba35619f78f2@linux.ibm.com>
On 2026-09-03 13:26:30, Mahanta Jambigi wrote:
> On 02/09/26 5:46 pm, Dust Li wrote:
> > I think we can close this window by holding the SMC hash table lock in
> > smcr_link_clear()? [...]
>
> Hi Dust,
>
> After debugging further, I believe that __smcr_link_clear() cannot be
> called from smc_switch_link_and_count() because
> refcount_dec_and_test(&lnk->refcnt) is always false on that path — the
> initial ref (set in smcr_link_init(), released only in
> smcr_link_clear()) is always held while the switch executes. So there is
> no UAF from that path. The only consequence is that the diag reader may
> observe a stale conn->lnk pointer value — pointing to the old link which
> is still fully live — and read the old link's ibport/link_id/ibname from it.
Hi Mahanta,
Sorry for the late reply. I've done some more thinking on this topic.
That's true, but strictly I think there is still a small gap. The old link is
memset later, in smcr_link_clear() after all connections have migrated, so a
reader that loaded conn->lnk just before its own connection was switched has a
few-instruction window before it dereferences. In practice this window is
really narrow: the clear path runs the whole switch + LLC + QP teardown, orders
of magnitude longer than the reader's load+deref.
> The real UAF is via smc_conn_free(): it drops both the connection-owned
> conn->lnk reference via smcr_link_put() and the connection-owned
> conn->lgr reference via smc_lgr_put(), while the socket is still visible
> in the hash table. [...] The current patch tries to fix this using
> lgr_lnk_lock. Since you suggested we fix it with *unhash-before-free*, I
> thought about it and here is my proposal.
>
> Proposed fix (high level):
>
> Introduce a smc_conn_unhash() helper with a per-connection unhashed flag
> that ensures the socket is removed from the hash table exactly once.
> Call it at the top of smc_conn_free(), before any lgr/lnk references are
> dropped.
>
> This establishes the invariant: any socket still visible to the diag
> reader under read_lock(hash->lock) has valid conn->lgr and conn->lnk
> pointers. [...]
>
> Does this design look reasonable to you?
Yes, I think it's the right fix and we can go ahead and fix it this way first.
> There is a separate minor issue — smc_switch_link_and_count() updates
> conn->lnk under send_lock while the diag reader loads it without any
> lock, which can cause stale ibport/link_id/ibname in *smcss -R* during a
> failover event. This is a data race but not a safety issue since the
> link struct is always live at that point.
>
> This race is very rare in practice [...] Even when it occurs, the effect
> is transient: one dump may show the old link's ibport/link_id/ibname, and
> the next dump will show the correct values.
I think this is acceptable.
---
I've been re-thinking this a bit more. We've been plagued by SMC's tangled
locks and ad-hoc lifetime handling for years — every fix adds another lock or
another ordering rule. I think it's time to step back and refactor this area as
a whole, and set up some rules for how we use locks/refcounts in SMC, instead of
keeping patching individual races. Below are some rough thoughts.
1. Object layering
SMC really has three lifetime tiers, and the top one has to be split the way
TCP splits struct socket from struct sock:
- the file (struct socket) — lifetime tied to fput;
- the connection sock (smc_sock, with conn embedded) — lifetime tied to
sk_refcnt; like a TCP sock it can be orphaned and outlive the file. On an
active close, close(fd) orphans it first, but it stays alive — still bound to
the transport — to finish the close handshake;
- the shared transport (lgr / link / device) — lifetime by refcount, multiplexed
across connections.
It's the socket/sock split, plus one more tier because our transport is shared
(TCP's sock is 1:1, our lgr/link is not). Today smc_conn_free() mixes all three
and is called from the transport layer while the socket is still hashed —
that's exactly where these races come from.
With the tiers separated, teardown becomes two independent, idempotent steps
rather than one:
- orphan (file <-> sock): at fput, via sock_orphan();
- transport-detach (sock <-> transport): the connection drops its lgr/link
refs. On an active close this happens later, after the handshake completes;
on a transport fault it happens immediately.
The sock is freed only once it is orphaned, transport-detached, and the last
sock_put lands; the order of the two steps just depends on who initiates
teardown (app close vs. transport fault).
2. Lifetime & boundary contract
Give each tier the right tool, and a clear boundary between them:
- ownership by kref; traversal by RCU (conn->lgr / conn->lnk become RCU
pointers, the lgr is freed with kfree_rcu); fd-visible objects (clcsock)
released only after the last fput; and no in-place mutation of a published
object (stop memset()ing a link — mark it dead and reclaim it with the lgr
after a grace period);
- boundary rule: on a transport fault the lower layer only signals and
transport-detaches; it never orphans, unhashes, or frees the sock.
- signal = wake the app with an error (sk_err + wakeup);
- transport-detach = publish rcu_assign_pointer(conn->lgr/lnk, NULL), then
drop the usage references (smc_lgr_put / smcr_link_put). The transport
object itself is reclaimed later by its own refcount + kfree_rcu; the sock
is left untouched.
Handle-side teardown (orphan, unhash, clcsock release, final sock_put)
belongs to the connection/file side and is driven by close — never by the
transport layer.
With that, a diag reader under rcu_read_lock is guaranteed the link/lgr
outlives its critical section, so the per-connection lgr_lnk_lock is no longer
needed and smc_conn_unhash() can be retired too. (clcsock_release_lock goes
away separately, via the clcsock lifetime series.) It's a larger, mostly
mechanical change and would take a lot of careful rework, so I'd do it as a
follow-up. Your smc_conn_unhash() is the right fix to take now (and for
stable); the rework, if we agree on this direction, would retire it afterwards.
Best regards,
Dust
prev parent reply other threads:[~2026-09-04 15:21 UTC|newest]
Thread overview: 13+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-28 6:54 [PATCH net v2 0/2] net/smc: fix diag dump lifetime races Mahanta Jambigi
2026-08-28 6:54 ` [PATCH net v2 1/2] net/smc: add connection lifetime infrastructure for diag Mahanta Jambigi
2026-08-29 6:55 ` sashiko-bot
2026-08-28 6:54 ` [PATCH net v2 2/2] net/smc: fix races in smc_diag dump path Mahanta Jambigi
2026-08-29 6:55 ` sashiko-bot
2026-08-31 13:42 ` [PATCH net v2 0/2] net/smc: fix diag dump lifetime races Dust Li
2026-08-31 14:08 ` Mahanta Jambigi
2026-08-31 14:57 ` Mahanta Jambigi
2026-09-01 13:02 ` Dust Li
2026-09-01 15:13 ` Mahanta Jambigi
2026-09-02 12:16 ` Dust Li
2026-09-03 7:56 ` Mahanta Jambigi
2026-09-04 15:21 ` Dust Li [this message]
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=apriDec7yHBkQgNv@linux.alibaba.com \
--to=dust.li@linux.alibaba.com \
--cc=alibuda@linux.alibaba.com \
--cc=andrew+netdev@lunn.ch \
--cc=davem@davemloft.net \
--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=mjambigi@linux.ibm.com \
--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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox