From: Bryam Vargas conn->sndbuf_space can run past the send buffer. After an SMC-R link failover, a late completion from the old link can move tx_curs_fin back, and the next completion credits the same bytes again; across the ring end, smc_curs_diff() credits the step back itself as more than a whole ring, so even with a conforming peer sndbuf_space can reach three times the buffer. The failover catch-up and SMC-D DMB merge (only the loopback device today) also credit it from the peer's consumer cursor without a bound. smc_tx_sendmsg() takes it as the write space, and the second chunk of its copy writes past the send buffer. Clamp writespace, which sizes the copy, to sndbuf_desc->len, negative values included, so the copy stays in the send buffer. Fixes: c6f02ebeea3a ("net/smc: switch connections to alternate link") Fixes: cc0ab806fc52 ("net/smc: adapt cursor update when sndbuf and peer DMB are merged") Cc: stable@vger.kernel.org Signed-off-by: Bryam Vargas Reviewed-by: Dust Li Reviewed-by: Sidraya Jayagond --- This keeps the copy inside the send buffer but does not undo the over-credit, so after such a failover send() can still overwrite data in the buffer that the peer has not received yet (by inspection; not tested). Fixing the credit in smc_cdc_tx_handler() needs a cursor compare that survives the u16 wrap rollover, which smc_curs_comp() does not do, so it is left out here. A negative value maps to len, not 0, since 0 would make the send loop spin. --- net/smc/smc_tx.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/net/smc/smc_tx.c b/net/smc/smc_tx.c index 0f0c6df3bf2c..3913ef103b35 100644 --- a/net/smc/smc_tx.c +++ b/net/smc/smc_tx.c @@ -233,6 +233,14 @@ int smc_tx_sendmsg(struct smc_sock *smc, struct msghdr *msg, size_t len) /* initialize variables for 1st iteration of subsequent loop */ /* could be just 1 byte, even after smc_tx_wait above */ writespace = atomic_read(&conn->sndbuf_space); + /* sndbuf_space can be out of range: late completions of an + * old SMC-R link credit it again after a failover, and with + * SMC-D DMB merge it follows the peer's consumer cursor. + * Bound writespace, which sizes the copy; a negative value + * would become huge in the size_t min_t() below. + */ + if (writespace < 0 || writespace > conn->sndbuf_desc->len) + writespace = conn->sndbuf_desc->len; /* not more than what user space asked for */ copylen = min_t(size_t, send_remaining, writespace); /* determine start of sndbuf */ @@ -278,7 +286,7 @@ int smc_tx_sendmsg(struct smc_sock *smc, struct msghdr *msg, size_t len) /* increased in send tasklet smc_cdc_tx_handler() */ smp_mb__before_atomic(); atomic_sub(copylen, &conn->sndbuf_space); - /* guarantee 0 <= sndbuf_space <= sndbuf_desc->len */ + /* can be out of range, see writespace above */ smp_mb__after_atomic(); /* since we just produced more new data into sndbuf, * trigger sndbuf consumer: RDMA write into peer RMBE and CDC -- 2.56.0