FAILED: patch "[PATCH] net/smc: fix use-after-free in smc_rx_pipe_buf_release()" failed to apply to 5.15-stable tree
[email protected]
maintainerThe patch below does not apply to the 5.15-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.
To reproduce the conflict and resubmit, you may use the following commands:
git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-5.15.y
git checkout FETCH_HEAD
git cherry-pick -x c924884743e948e25625b7fbf3ee2a9325a204a7
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<[email protected]>' --in-reply-to '2026090344-judgingly-mace-1efd@gregkh' --subject-prefix 'PATCH 5.15.y' 'HEAD^..'
Possible dependencies:
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From c924884743e948e25625b7fbf3ee2a9325a204a7 Mon Sep 17 00:00:00 2001
From: Hidayath Khan <[email protected]>
Date: Thu, 20 Aug 2026 09:46:42 +0200
Subject: [PATCH] net/smc: fix use-after-free in smc_rx_pipe_buf_release()
smc_rx_splice() hands RMB pages to a pipe and takes a socket reference per entry so the smc_sock stays alive until the reader finishes. The connection does not: a concurrent close runs smc_conn_free(), which releases the receive buffer back to the link group pool.
smc_rx_pipe_buf_release() tests sk_state before taking the socket lock. The state can change between the test and the lock, and smc_rx_update_cons() then dereferences conn->rmb_desc and walks conn->lgr, which smc_conn_free() has already released. On the is_reg_err path smcr_buf_unuse() frees the descriptor outright, so this is a use-after-free.
Take the socket lock first and test conn->freed instead. smc_conn_free() sets that flag before releasing anything, and every caller holds the socket lock. The two paths exclude each other: either the pipe release runs first with everything valid, or it sees the flag and skips the update.
net/smc/smc_rx.c +5 -6
@@ -115,16 +115,15 @@ static void smc_rx_pipe_buf_release(struct pipe_inode_info *pipe, struct pipe_buffer *buf) {struct smc_spd_priv *priv = (struct smc_spd_priv *)buf->private; +struct smc_connection *conn = &priv->smc->conn;struct smc_sock *smc = priv->smc; -struct smc_connection *conn;struct sock *sk = &smc->sk; -if (sk->sk_state ==SMC_CLOSED || - sk->sk_state ==SMC_PEERFINCLOSEWAIT || - sk->sk_state ==SMC_APPFINCLOSEWAIT ) - goto out; - conn = &smc->conn;lock_sock (sk); +if (conn->freed) { +release_sock (sk); + goto out; + }smc_rx_update_cons (smc, priv->len);release_sock (sk);if (atomic_sub_and_test (priv->len, &conn->splice_pending))
Sasha Levin
maintainerPATCH 1/2 net/smc: Resolve the race between link group access and termination
From: Wen Gu <[email protected]>
[ Upstream commit 61f434b0280ed65495831f1b6e1a5c21a90f47c6 ]
We encountered some crashes caused by the race between the access and the termination of link groups.
Here are some of panic stacks we met:
1) Race between smc_clc_wait_msg() and __smc_lgr_terminate()
BUG: kernel NULL pointer dereference, address: 00000000000002f0 Workqueue: smc_hs_wq smc_listen_work [smc] RIP: 0010:smc_clc_wait_msg+0x3eb/0x5c0 [smc] Call Trace: <TASK> ? smc_clc_send_accept+0x45/0xa0 [smc] ? smc_clc_send_accept+0x45/0xa0 [smc] smc_listen_work+0x783/0x1220 [smc] ? finish_task_switch+0xc4/0x2e0 ? process_one_work+0x1ad/0x3c0 process_one_work+0x1ad/0x3c0 worker_thread+0x4c/0x390 ? rescuer_thread+0x320/0x320 kthread+0x149/0x190 ? set_kthread_struct+0x40/0x40 ret_from_fork+0x1f/0x30 </TASK>
smc_listen_work() abnormal case like port error
---------------------------------------------------------------
| __smc_lgr_terminate()
| |- smc_conn_kill()
| |- smc_lgr_unregister_conn()
| |- set conn->lgr = NULL
smc_clc_wait_msg() |
|- access conn->lgr (panic) |2) Race between smc_setsockopt() and __smc_lgr_terminate()
BUG: kernel NULL pointer dereference, address: 00000000000002e8 RIP: 0010:smc_setsockopt+0x17a/0x280 [smc] Call Trace: <TASK> __sys_setsockopt+0xfc/0x190 __x64_sys_setsockopt+0x20/0x30 do_syscall_64+0x34/0x90 entry_SYSCALL_64_after_hwframe+0x44/0xae </TASK>
smc_setsockopt() abnormal case like port error
--------------------------------------------------------------
| __smc_lgr_terminate()
| |- smc_conn_kill()
| |- smc_lgr_unregister_conn()
| |- set conn->lgr = NULL
mod_delayed_work() |
|- access conn->lgr (panic) |There are some other panic places and they are caused by the similar reason as described above, which is accessing link group after termination, thus getting a NULL pointer or invalid resource.
Currently, there seems to be no synchronization between the link group access and a sudden termination of it. This patch tries to fix this by introducing reference count of link group and not freeing link group until reference count is zero.
Link group might be referred to by links or smc connections. So the operation to the link group reference count can be concluded as follows:
object [hold or initialized as 1] [put]
-------------------------------------------------------------------
link group smc_lgr_create() smc_lgr_free() connections smc_conn_create() smc_conn_free() links smcr_link_init() smcr_link_clear()
Througth this way, we extend the life cycle of link group and ensure it is longer than the life cycle of connections and links above it, so that avoid invalid access to link group after its termination.
Keep only the connection teardown state needed by target commit c924884743e948e25625b7fbf3ee2a9325a204a7 ("net/smc: fix use-after-free in smc_rx_pipe_buf_release()") for this 5.15 dependency backport.
Add conn->freed, reset it when creating a connection, and set it before smc_conn_free() releases connection resources. Also set it when smc_lgr_unregister_conn() detaches the connection: on this stable tree, forced termination may unregister and free the link group without first calling smc_conn_free(). The target can then use the flag under the socket lock to avoid accessing either reused buffers or a detached link group.
Drop the upstream link-group reference-counting changes and their new helper functions. They are unnecessary for the target's teardown check and conflict with the older link initialization and link-group cleanup code, which lacks smc_wr_free_lgr_mem(). Preserve the existing 5.15 link-group lifetime and conn->lgr clearing behavior.
[ sashal: Reduced backport -- upstream 61f434b0280ed touches 3 file(s), this
backport carries 2. Not backported here:
net/smc/smc_core.h
This note is generated from the file lists only; see the resolution record for the reasoning. ]
Stable-dep-of: c924884743e9 ("net/smc: fix use-after-free in smc_rx_pipe_buf_release()")
--- net/smc/smc.h | 1 + net/smc/smc_core.c | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-)
net/smc/smc.h +1 -0
@@ -217,6 +217,7 @@ struct smc_connection {*/ u64 peer_token;/* SMC-D token of peer */ u8 killed : 1; /* abnormal termination */ + u8 freed : 1; /* connection released */ u8 out_of_sync : 1; /* out of sync with peer */ };
net/smc/smc_core.c +5 -1
@@ -217,6 +217,8 @@ static void smc_lgr_unregister_conn(struct smc_connection *conn)__smc_lgr_unregister_conn (conn); }write_unlock_bh (&lgr->conns_lock); +/* Termination can unregister a connection without smc_conn_free(). */ + conn->freed =1 ; conn->lgr =NULL ; }
@@ -1049,8 +1051,9 @@ void smc_conn_free(struct smc_connection *conn) {struct smc_link_group *lgr = conn->lgr; -if (!lgr) +if (!lgr || conn->freed)return ; + conn->freed =1 ;if (lgr->is_smcd) {if (!list_empty (&lgr->list))smc_ism_unset_conn (conn);
@@ -1740,6 +1743,7 @@ int smc_conn_create(struct smc_sock *smc, struct smc_init_info *ini) goto out; } } + conn->freed =0 ; conn->local_tx_ctrl.common.type =SMC_CDC_MSG_TYPE ; conn->local_tx_ctrl.len =SMC_WR_TX_SIZE ; conn->urg_state =SMC_URG_READ ;
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 2/2 net/smc: fix use-after-free in smc_rx_pipe_buf_release()
From: Hidayath Khan <[email protected]>
[ Upstream commit c924884743e948e25625b7fbf3ee2a9325a204a7 ]
smc_rx_splice() hands RMB pages to a pipe and takes a socket reference per entry so the smc_sock stays alive until the reader finishes. The connection does not: a concurrent close runs smc_conn_free(), which releases the receive buffer back to the link group pool.
smc_rx_pipe_buf_release() tests sk_state before taking the socket lock. The state can change between the test and the lock, and smc_rx_update_cons() then dereferences conn->rmb_desc and walks conn->lgr, which smc_conn_free() has already released. On the is_reg_err path smcr_buf_unuse() frees the descriptor outright, so this is a use-after-free.
Take the socket lock first and test conn->freed instead. smc_conn_free() sets that flag before releasing anything, and every caller holds the socket lock. The two paths exclude each other: either the pipe release runs first with everything valid, or it sees the flag and skips the update.
--- net/smc/smc_rx.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-)
net/smc/smc_rx.c +5 -6
@@ -110,16 +110,15 @@ static void smc_rx_pipe_buf_release(struct pipe_inode_info *pipe, struct pipe_buffer *buf) {struct smc_spd_priv *priv = (struct smc_spd_priv *)buf->private; +struct smc_connection *conn = &priv->smc->conn;struct smc_sock *smc = priv->smc; -struct smc_connection *conn;struct sock *sk = &smc->sk; -if (sk->sk_state ==SMC_CLOSED || - sk->sk_state ==SMC_PEERFINCLOSEWAIT || - sk->sk_state ==SMC_APPFINCLOSEWAIT ) - goto out; - conn = &smc->conn;lock_sock (sk); +if (conn->freed) { +release_sock (sk); + goto out; + }smc_rx_update_cons (smc, priv->len);release_sock (sk);if (atomic_sub_and_test (priv->len, &conn->splice_pending))
Signature · 1 line
2.53.0