smc_rx_recvmsg() offers a chunk of the RMB to splice_to_pipe() and then credits the whole chunk regardless of what was taken. splice_to_pipe() takes only what the pipe has room for. A short count and a zero count are both non-negative, so both fall through as success: the call returns more than it delivered, and the consumer cursor and bytes_to_rcv are advanced by copylen rather than by what the reader received. The skipped bytes are never handed to anyone. A pipe that is not empty is enough to hit this; SPLICE_F_NONBLOCK on a full pipe produces the zero case. Account what splice_to_pipe() actually took, and stop there rather than continuing to the second chunk of a wrapped read. When it took nothing, return -EAGAIN instead of reporting a length that was not delivered. Fixes: 9014db202cb7 ("smc: add support for splice()") Cc: stable@vger.kernel.org Reviewed-by: Sidraya Jayagond Signed-off-by: Hidayath Khan --- net/smc/smc_rx.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/net/smc/smc_rx.c b/net/smc/smc_rx.c index 5c9e4d8b57de..d951dbe8eec3 100644 --- a/net/smc/smc_rx.c +++ b/net/smc/smc_rx.c @@ -488,6 +488,24 @@ int smc_rx_recvmsg(struct smc_sock *smc, struct msghdr *msg, read_done = -EFAULT; goto out; } + /* splice_to_pipe() takes only what the pipe + * has room for, which may be less than was + * offered, or nothing. Account what it took + * and stop: crediting the whole chunk would + * advance the consumer past data the reader + * never received. + */ + if (!msg && rc < chunk_len) { + if (!rc) { + if (!read_done) + read_done = -EAGAIN; + goto out; + } + copylen = chunk_len_sum - chunk_len + rc; + read_remaining -= rc; + read_done += rc; + break; + } } read_remaining -= chunk_len; read_done += chunk_len; base-commit: d3083202a78cd0040be6a88953f728b0d3db1990 -- 2.52.0