From nobody Sat Jul 25 15:53:53 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 4FF6A42A17D; Thu, 16 Jul 2026 13:50:47 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784209847; cv=none; b=ZkTQe2N33WYfj8CQpi7gHUzgMozICTXp3cmVPcmogMdmY92vcB6rcWYp6qRdbxg4evSlfGKJchoDNaRlsEdl5j5EpgCKv1xPxy3//Xz36bvNKu3lpoDUKVlDkoSQl4NA7OIzehEnWiyoRG9uShKHkAml1LBQzbDu8rSlXzROc8k= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784209847; c=relaxed/simple; bh=UzpmWiDSqqP+iFkA0R8h+A1fwholsVskgVIc/9kF5Vw=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:To:Cc; b=N1z9Etd42cmlAgpQ9lAgCGkAW6FnBGFRqW/y5QKUMq45sAmqx6gP7s48EQkG8pCUdRibJF8yDRywgoIAspiWt4Wy0gZV4fv8faGs73cik3uFRziiTb8jSuoSlh2zh0kPDQ4qnnD8Q1Avyin2JDzI35dcB0n2FigFjgtk1uhTUJ0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=DqH+9tSA; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="DqH+9tSA" Received: by smtp.kernel.org (Postfix) with ESMTPS id DD414C2BCC9; Thu, 16 Jul 2026 13:50:46 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1784209846; bh=UzpmWiDSqqP+iFkA0R8h+A1fwholsVskgVIc/9kF5Vw=; h=From:Date:Subject:To:Cc:Reply-To:From; b=DqH+9tSASMiBBT8QaBwrJ1N16EKSOEY5CdaDQLN0pbSkXZV/RlJIqZ6E/d2kq8Cz3 wI6cmtMqKQXyL4RTP4dUz1j6Ywt9jrosIBXGuVgEcPgj7aJlyv304WAYAOQcJP8fuU zy1Mjgo1FCF6D1CVKbXyUvvMkQkmnaV5VZwl0q3LIbY+4/a9CZ3tQerILQUVrhhNxo YbAXbyugDIqzyV5NrhFkMHFCOIesrAHkBoqxNu4KKrb74tcNSpKJph0+8fMv2uEk3B ISNTr7RS8iLHl2CupY/lWyy/M7PI8dRBsgrRh7y8lXAOTOEnw+j/dn5S9iS+uldJu1 HIUlCpH3gs0xQ== Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 9B64AC4450A; Thu, 16 Jul 2026 13:50:46 +0000 (UTC) From: Bryam Vargas via B4 Relay Date: Thu, 16 Jul 2026 08:50:45 -0500 Subject: [PATCH net v3] net/smc: order the CDC receive path against buffer publication Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260716-b4-disp-aa52955a-v3-1-03a4411a7549@proton.me> X-B4-Tracking: v=1; b=H4sIALThWGoC/x3MPQqAMAxA4atIZgP9sRW9ijhEjZqlllZEEO9uc fyG9x7InIQz9NUDiS/JcoQCW1cw7xQ2RlmKwSjjVas9Tg0ukiMSOdM5R+i8WnU3N5YMQcli4lX ufzlA4BPG9/0AAXzEXWcAAAA= To: "D. Wythe" , Paolo Abeni , Jakub Kicinski , Eric Dumazet , Mahanta Jambigi , Dust Li , Wen Gu , "David S. Miller" , Sidraya Jayagond , Wenjia Zhang , Tony Lu Cc: netdev@vger.kernel.org, Simon Horman , linux-s390@vger.kernel.org, linux-rdma@vger.kernel.org, linux-kernel@vger.kernel.org X-Mailer: b4 0.13.0 X-Developer-Signature: v=1; a=ed25519-sha256; t=1784209844; l=9556; i=hexlabsecurity@proton.me; s=proton; h=from:subject:message-id; bh=8rdrQeoqAP//SANKUMiO9RanFoUJmkH6UG4jV/txETM=; b=WGVanU1Q5UWJ0VRvNyWdONdPuWe9Xe37CaN5HmENvtKiROmzY1mWgJbjTt8OaCpH4IYzvwtfY 0YsU/ji30l+DzxmUSROprHb3c8ZKYjXfm/f/zOr+dIS3pJrXjbJzgIt X-Developer-Key: i=hexlabsecurity@proton.me; a=ed25519; pk=dmppBMZNLLoPzxHi9l8tZDzEZUunPbgsYqIZYXeUrL0= X-Endpoint-Received: by B4 Relay for hexlabsecurity@proton.me/proton with auth_id=814 X-Original-From: Bryam Vargas Reply-To: hexlabsecurity@proton.me From: Bryam Vargas The SMC CDC receive handlers dereference conn->rmb_desc, and on the SMC-D DMB-nocopy path conn->sndbuf_desc, but both are published after the connection is already reachable to a peer: rmb_desc once the connection is in the link group's token tree, the nocopy ghost sndbuf_desc later still, in smcd_buf_attach() after the ISM receive tasklet is armed. A CDC in that window hits a handler with the buffer unset -- a NULL dereference and host DoS -- or, on a weakly ordered CPU, non-NULL but not yet initialised. Both are also published before the receive state (bytes_to_rcv, sndbuf_space), so an early CDC's accounting can be overwritten by setup. Initialise the receive state first and publish both buffers last with smp_store_release(), consuming them with smp_load_acquire() and bailing while unset, as the handlers already do for a killed connection. Gate the whole sndbuf consumer trigger on the send buffer, not just the nocopy accounting: smc_tx_prepared_sends() and smc_tx_pending() dereference it too. Conforming peers are unaffected. Closes: https://sashiko.dev/#/patchset/20260714-b4-disp-835288a6-v2-1-58155= 5ef2145@proton.me?part=3D1 Cc: stable@vger.kernel.org Signed-off-by: Bryam Vargas --- v3: - Publish rmb_desc and the ghost sndbuf_desc after the receive state is initialised, not before. The earlier revision released the pointer first, which let an early CDC's accounting be overwritten by setup. - Gate the whole sndbuf consumer trigger on sndbuf_desc, not only the noco= py accounting: smc_tx_prepared_sends() and smc_tx_pending() dereference it = too. The v2 review raised both. v2: https://lore.kernel.org/all/20260714-b4-disp-835288a6-v2-1-581555ef2145= @proton.me/ v1: https://lore.kernel.org/all/20260711-b4-disp-c36a9798-v1-1-340b0c6053fb= @proton.me/ herd7 models both orderings. Plain accesses allow the "pointer published, b= uffer stale" outcome and flag a data race; release/acquire forbid it. A publish-o= rder litmus shows the lost update is allowed with the store released first and n= ever with it released last. af_smc runs over an RDMA fabric or an ISM device, so= the weak-memory arm is model-level; litmus tests and reproducer on request. No Fixes: tag -- the rmb_desc ordering is foundational (predates the git hi= story here); the sndbuf_desc hunks additionally cover the later SMC-D DMB-nocopy = path. Happy to split rmb/sndbuf for a cleaner stable backport. --- net/smc/smc_cdc.c | 50 +++++++++++++++++++++++++++++++++++++++++--------- net/smc/smc_core.c | 26 ++++++++++++++++++++++---- 2 files changed, 63 insertions(+), 13 deletions(-) diff --git a/net/smc/smc_cdc.c b/net/smc/smc_cdc.c index 32d6d03df321..ea61b1e75c72 100644 --- a/net/smc/smc_cdc.c +++ b/net/smc/smc_cdc.c @@ -332,8 +332,20 @@ static void smc_cdc_msg_recv_action(struct smc_sock *s= mc, { union smc_host_cursor cons_old, prod_old; struct smc_connection *conn =3D &smc->conn; + struct smc_buf_desc *sndbuf_desc; int diff_cons, diff_prod, diff_tx; =20 + /* Acquire the send buffer once, pairing with the smp_store_release() in + * __smc_buf_create()/smcd_buf_attach(). On the SMC-D DMB-nocopy path + * the ghost sndbuf_desc is attached only after the connection is already + * reachable to the ISM device, so it can still be unset here; every + * sndbuf_desc consumer below (the nocopy accounting and the sndbuf + * consumer trigger, which dereferences it via smc_tx_prepared_sends()) + * is skipped while it is NULL to avoid a NULL deref and a load of an + * uninitialised buffer. + */ + sndbuf_desc =3D smp_load_acquire(&conn->sndbuf_desc); + smc_curs_copy(&prod_old, &conn->local_rx_ctrl.prod, conn); smc_curs_copy(&cons_old, &conn->local_rx_ctrl.cons, conn); smc_cdc_msg_to_host(&conn->local_rx_ctrl, cdc, conn); @@ -351,14 +363,17 @@ static void smc_cdc_msg_recv_action(struct smc_sock *= smc, =20 /* if local sndbuf shares the same memory region with * peer RMB, then update tx_curs_fin and sndbuf_space - * here since peer has already consumed the data. + * here since peer has already consumed the data. The ghost + * sndbuf_desc (acquired above) may still be unset in the SMC-D + * DMB-nocopy setup window, so skip the update while it is NULL. */ if (conn->lgr->is_smcd && - smc_ism_support_dmb_nocopy(conn->lgr->smcd)) { + smc_ism_support_dmb_nocopy(conn->lgr->smcd) && + sndbuf_desc) { /* Calculate consumed data and * increment free send buffer space. */ - diff_tx =3D smc_curs_diff(conn->sndbuf_desc->len, + diff_tx =3D smc_curs_diff(sndbuf_desc->len, &conn->tx_curs_fin, &conn->local_rx_ctrl.cons); /* increase local sndbuf space and fin_curs */ @@ -391,10 +406,15 @@ static void smc_cdc_msg_recv_action(struct smc_sock *= smc, conn->urg_state =3D SMC_URG_NOTYET; } =20 - /* trigger sndbuf consumer: RDMA write into peer RMBE and CDC */ - if ((diff_cons && smc_tx_prepared_sends(conn)) || - conn->local_rx_ctrl.prod_flags.cons_curs_upd_req || - conn->local_rx_ctrl.prod_flags.urg_data_pending) { + /* trigger sndbuf consumer: RDMA write into peer RMBE and CDC. + * smc_tx_prepared_sends() and smc_tx_pending() dereference sndbuf_desc, + * so skip the whole trigger while it is unset (the SMC-D DMB-nocopy + * setup window): there is nothing to send without a send buffer. + */ + if (sndbuf_desc && + ((diff_cons && smc_tx_prepared_sends(conn)) || + conn->local_rx_ctrl.prod_flags.cons_curs_upd_req || + conn->local_rx_ctrl.prod_flags.urg_data_pending)) { if (!sock_owned_by_user(&smc->sk)) smc_tx_pending(conn); else @@ -443,13 +463,21 @@ static void smcd_cdc_rx_tsklet(struct tasklet_struct = *t) { struct smc_connection *conn =3D from_tasklet(conn, t, rx_tsklet); struct smcd_cdc_msg *data_cdc; + struct smc_buf_desc *rmb_desc; struct smcd_cdc_msg cdc; struct smc_sock *smc; =20 if (!conn || conn->killed) return; + /* Pair with smp_store_release() in __smc_buf_create(): the connection + * is published before its RMB is allocated, so bail while rmb_desc is + * unset to avoid a NULL deref and a load of an uninitialised buffer. + */ + rmb_desc =3D smp_load_acquire(&conn->rmb_desc); + if (!rmb_desc) + return; =20 - data_cdc =3D (struct smcd_cdc_msg *)conn->rmb_desc->cpu_addr; + data_cdc =3D (struct smcd_cdc_msg *)rmb_desc->cpu_addr; smcd_curs_copy(&cdc.prod, &data_cdc->prod, conn); smcd_curs_copy(&cdc.cons, &data_cdc->cons, conn); smc =3D container_of(conn, struct smc_sock, conn); @@ -483,7 +511,11 @@ static void smc_cdc_rx_handler(struct ib_wc *wc, void = *buf) lgr =3D smc_get_lgr(link); read_lock_bh(&lgr->conns_lock); conn =3D smc_lgr_find_conn(ntohl(cdc->token), lgr); - if (!conn || conn->out_of_sync) { + /* Pair with smp_store_release() in __smc_buf_create(): bail while the + * RMB is unset (smc_cdc_msg_recv_action() dereferences it) to avoid a + * NULL deref and a stale-buffer read in the connection setup window. + */ + if (!conn || conn->out_of_sync || !smp_load_acquire(&conn->rmb_desc)) { read_unlock_bh(&lgr->conns_lock); return; } diff --git a/net/smc/smc_core.c b/net/smc/smc_core.c index cf6b620fef05..0561f83be327 100644 --- a/net/smc/smc_core.c +++ b/net/smc/smc_core.c @@ -2499,15 +2499,26 @@ static int __smc_buf_create(struct smc_sock *smc, b= ool is_smcd, bool is_rmb) } =20 if (is_rmb) { - conn->rmb_desc =3D buf_desc; conn->rmbe_size_comp =3D bufsize_comp; smc->sk.sk_rcvbuf =3D bufsize * 2; atomic_set(&conn->bytes_to_rcv, 0); conn->rmbe_update_limit =3D smc_rmb_wnd_update_limit(buf_desc->len); + /* Publish the receive buffer last, with release semantics: the + * connection is already in the link group's token tree, so a + * concurrent CDC receive handler must observe the fully + * initialised receive state above (and the buffer) once it sees + * a non-NULL rmb_desc. Pairs with the smp_load_acquire() in the + * CDC receive path. + */ + smp_store_release(&conn->rmb_desc, buf_desc); if (is_smcd) smc_ism_set_conn(conn); /* map RMB/smcd_dev to conn */ } else { + /* Plain store: this send-buffer pass runs before the RMB pass, + * whose smp_store_release(&conn->rmb_desc) then publishes this + * store too, and the CDC receive path is gated on rmb_desc. + */ conn->sndbuf_desc =3D buf_desc; smc->sk.sk_sndbuf =3D bufsize * 2; atomic_set(&conn->sndbuf_space, bufsize); @@ -2599,9 +2610,16 @@ int smcd_buf_attach(struct smc_sock *smc) buf_desc->cpu_addr =3D (u8 *)buf_desc->cpu_addr + sizeof(struct smcd_cdc_msg); buf_desc->len -=3D sizeof(struct smcd_cdc_msg); - conn->sndbuf_desc =3D buf_desc; - conn->sndbuf_desc->used =3D 1; - atomic_set(&conn->sndbuf_space, conn->sndbuf_desc->len); + buf_desc->used =3D 1; + atomic_set(&conn->sndbuf_space, buf_desc->len); + /* Publish the ghost send buffer last, with release semantics: the + * connection is already reachable to the ISM device (smc_ism_set_conn() + * ran in __smc_buf_create()), so the CDC receive tasklet must observe + * the fully initialised ghost buffer once it sees a non-NULL + * sndbuf_desc. Pairs with smp_load_acquire() in + * smc_cdc_msg_recv_action(). + */ + smp_store_release(&conn->sndbuf_desc, buf_desc); return 0; =20 free: --- base-commit: 3f1f755366687d051174739fb99f7d560202f60b change-id: 20260716-b4-disp-aa52955a-560f19c43a2a Best regards, --=20 Bryam Vargas