From: Bryam Vargas On SMC-R link failover, smc_switch_cursor() rewinds the send and producer cursors to their values at the last completed CDC send, then advances them by however far the peer's consumer cursor is ahead. That cursor comes from the wire, so a forged one adds an arbitrary amount to sndbuf_space and to the cursors. smc_curs_add() wraps them only once, so the next RDMA write can take its source past the send buffer and its destination past the peer's RMB. A conforming peer keeps its cursor count inside its RMB and cannot consume past the data in the send buffer. Advance only for such a count, and no further than tx_curs_prep. The pre-failover tx_curs_sent cannot be the bound: an earlier failover may have rewound it below the peer's cursor. Fixes: c6f02ebeea3a ("net/smc: switch connections to alternate link") Cc: stable@vger.kernel.org Signed-off-by: Bryam Vargas --- Bounding the copy in smc_tx_sendmsg() (3/4) does not cover this: the RDMA write takes its offsets from the cursors. --- net/smc/smc_core.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/net/smc/smc_core.c b/net/smc/smc_core.c index 8829517299de..36aee89e5f5f 100644 --- a/net/smc/smc_core.c +++ b/net/smc/smc_core.c @@ -1034,7 +1034,7 @@ static int smc_switch_cursor(struct smc_sock *smc, struct smc_cdc_tx_pend *pend, struct smc_wr_buf *wr_buf) { struct smc_connection *conn = &smc->conn; - union smc_host_cursor cons, fin; + union smc_host_cursor cons, fin, prep; int rc = 0; int diff; @@ -1043,12 +1043,18 @@ static int smc_switch_cursor(struct smc_sock *smc, struct smc_cdc_tx_pend *pend, /* set prod cursor to old state, enforce tx_rdma_writes() */ smc_curs_copy(&conn->local_tx_ctrl.prod, &fin, conn); smc_curs_copy(&cons, &conn->local_rx_ctrl.cons, conn); + smc_curs_copy(&prep, &conn->tx_curs_prep, conn); - if (smc_curs_comp(conn->peer_rmbe_size, &cons, &fin) < 0) { + if (cons.count < conn->peer_rmbe_size && + smc_curs_comp(conn->peer_rmbe_size, &cons, &fin) < 0) { /* cons cursor advanced more than fin, and prod was set - * fin above, so now prod is smaller than cons. Fix that. + * fin above, so now prod is smaller than cons. cons comes + * off the wire: catch up only for a count inside the peer's + * RMB and no further than the data in the send buffer. */ - diff = smc_curs_diff(conn->peer_rmbe_size, &fin, &cons); + diff = min(smc_curs_diff(conn->peer_rmbe_size, &fin, &cons), + smc_curs_diff(conn->sndbuf_desc->len, + &conn->tx_curs_sent, &prep)); smc_curs_add(conn->sndbuf_desc->len, &conn->tx_curs_sent, diff); smc_curs_add(conn->sndbuf_desc->len, -- 2.56.0