Merge branch 'net-smc-fix-use-after-free-in-smc_rx_pipe_buf_release'

Hidayath Khan says:

====================
net/smc: fix use-after-free in smc_rx_pipe_buf_release()

smc_rx_pipe_buf_release() tests sk_state before taking the socket lock
and then dereferences conn->rmb_desc and conn->lgr. A concurrent close
runs smc_conn_free() in between, which releases those structures. On the
is_reg_err path smcr_buf_unuse() frees the descriptor outright, so this
is a use-after-free.

Patch 2/2 fixes this by taking the socket lock first and testing
conn->freed instead. smc_conn_free() sets that flag before releasing
anything, under the same lock, so the two paths exclude each other.

Patch 1/2 is a prerequisite. conn->freed shares a byte with killed and
out_of_sync as single-bit bitfields. out_of_sync is written from the
receive tasklet without the socket lock, so a concurrent store to freed
from process context can be lost in the read-modify-write. Patch 1/2
gives each flag its own byte so stores do not interfere.
====================

Link: https://patch.msgid.link/20260820074642.966856-1-hidayath@linux.ibm.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jakub Kicinski 2026-08-24 11:52:02 -07:00
commit c745d091fc
2 changed files with 8 additions and 9 deletions

View File

@ -277,9 +277,9 @@ struct smc_connection {
* 0 for SMC-R, 32 for SMC-D
*/
u64 peer_token; /* SMC-D token of peer */
u8 killed : 1; /* abnormal termination */
u8 freed : 1; /* normal termination */
u8 out_of_sync : 1; /* out of sync with peer */
u8 killed; /* abnormal termination */
u8 freed; /* normal termination */
u8 out_of_sync; /* out of sync with peer */
};
struct smc_sock { /* smc sock container */

View File

@ -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))