From: Bryam Vargas With DMB nocopy (the dibs loopback device) the peer writes into the RMB through its own attachment, and it can attach late. smc_buf_unuse() puts the DMB back in the link group's pool with its token still registered while the old peer can still write to it: the next connection that takes it gets the old peer's CDCs, and a peer that attaches after the other end gave up lands in another connection's DMB. Unregister a nocopy DMB when the connection ends instead of pooling it. The next connection gets a new DMB and token, a peer still attached keeps the old DMB to itself until it detaches, and a later attach fails. Clear conn->rmb_desc, which a connect() retried on the same socket would otherwise use after the free. Devices without nocopy keep their pool. Fixes: ae2be35cbed2 ("net/smc: {at|de}tach sndbuf to peer DMB if supported") Cc: stable@vger.kernel.org Signed-off-by: Bryam Vargas --- net/smc/smc_core.c | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/net/smc/smc_core.c b/net/smc/smc_core.c index a3164201c142..88855d9f6617 100644 --- a/net/smc/smc_core.c +++ b/net/smc/smc_core.c @@ -1243,6 +1243,22 @@ static void smcd_buf_detach(struct smc_connection *conn) kfree(buf_desc); } +/* With DMB nocopy the peer writes into the RMB through its own + * attachment, which can outlive this connection: unregister the DMB + * instead of pooling it, so the next connection gets a new one. + */ +static void smcd_rmb_drop(struct smc_connection *conn, + struct smc_link_group *lgr) +{ + struct smc_buf_desc *rmb_desc = conn->rmb_desc; + + down_write(&lgr->rmbs_lock); + smc_lgr_buf_list_del(lgr, true, rmb_desc); + up_write(&lgr->rmbs_lock); + WRITE_ONCE(conn->rmb_desc, NULL); + smc_buf_free(lgr, true, rmb_desc); +} + static void smc_buf_unuse(struct smc_connection *conn, struct smc_link_group *lgr) { @@ -1266,8 +1282,13 @@ static void smc_buf_unuse(struct smc_connection *conn, smcr_buf_unuse(conn->rmb_desc, true, lgr); } else { bufsize += sizeof(struct smcd_cdc_msg); - memzero_explicit(conn->rmb_desc->cpu_addr, bufsize); - WRITE_ONCE(conn->rmb_desc->used, 0); + if (smc_ism_support_dmb_nocopy(lgr->smcd)) { + smcd_rmb_drop(conn, lgr); + } else { + memzero_explicit(conn->rmb_desc->cpu_addr, + bufsize); + WRITE_ONCE(conn->rmb_desc->used, 0); + } } SMC_STAT_RMB_SIZE(smc, is_smcd, true, false, bufsize); } -- 2.56.0