From ec8fceb4a6a537c4d838287d47edc2c156379b4e Mon Sep 17 00:00:00 2001 From: Alyssa Ross Date: Mon, 14 Jul 2025 18:19:42 +0200 Subject: [PATCH] virtio-devices: stop corrupting vsock commands MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The read_exact() call was introduced in 82ac114b8 ("virtio-devices: vsock: handle short read in muxer") to solve a crash when a connection disconnected without sending any data, but it introduced a problem of its own: because the socket is non-blocking, read_exact() may read some data, then return ErrorKind::WouldBlock. In that case, the data it read will be discarded. So for example if it read "CONNECT ", and then nothing else was available to read yet, "CONNECT " would be discarded, and so the next time this function was called, when epoll triggered again for the socket, only the following data would end up in command.buf, causing an error due to just a port number being an invalid command. Contrary to that commit message, this code was actually designed to handle short reads just fine — in the case of a short read, it stores the data it has read in command, and returns Error::UnixRead(ErrorKind::WouldBlock), which is ignored by the caller, and the function gets called again when there is more data to read, building up command potentially over the course of several reads. The only thing it didn't handle correctly, as far as I can tell, was a 0-byte read, which happens when a client disconnects from the socket without writing anything. All that's needed to fix this is to avoid an invalid subtraction in that case, so this change reverts 82ac114b8, fixing the issue with partial commands being discarded, and instead handles the 0-byte read by using slice::get, and treating an empty command as an incomplete command, which of course it is. Fixes: 82ac114b8 ("virtio-devices: vsock: handle short read in muxer") Signed-off-by: Alyssa Ross --- virtio-devices/src/vsock/unix/muxer.rs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/virtio-devices/src/vsock/unix/muxer.rs b/virtio-devices/src/vsock/unix/muxer.rs index 55e819d4b..ebe0dc723 100644 --- a/virtio-devices/src/vsock/unix/muxer.rs +++ b/virtio-devices/src/vsock/unix/muxer.rs @@ -493,15 +493,18 @@ impl VsockMuxer { const MIN_COMMAND_LEN: usize = 10; // Bring in the minimum number of bytes that we should be able to read. - stream - .read_exact(&mut command.buf[command.len..MIN_COMMAND_LEN]) - .map_err(Error::UnixRead)?; - command.len = MIN_COMMAND_LEN; + if command.len < MIN_COMMAND_LEN { + command.len += stream + .read(&mut command.buf[command.len..MIN_COMMAND_LEN]) + .map_err(Error::UnixRead)?; + } // Now, finish reading the destination port number, by bringing in one byte at a time, // until we reach an EOL terminator (or our buffer space runs out). Yeah, not // particularly proud of this approach, but it will have to do for now. - while command.buf[command.len - 1] != b'\n' && command.len < command.buf.len() { + while command.len.checked_sub(1).map(|n| command.buf[n]) != Some(b'\n') + && command.len < command.buf.len() + { command.len += stream .read(&mut command.buf[command.len..=command.len]) .map_err(Error::UnixRead)?;