-
Notifications
You must be signed in to change notification settings - Fork 565
virtio-devices: refactor VSOCK "connect" parsing #7310
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
virtio-devices: refactor VSOCK "connect" parsing #7310
Conversation
ed2ddcf to
d4ccb3e
Compare
d4ccb3e to
4fda2ab
Compare
1ee095d to
00db0c4
Compare
rbradford
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@alyssais Would you like to take a look at this as our vsock expert :-)
|
Happy to but won't get a chance until next week at the earliest as I'm at KVM Forum |
00db0c4 to
0849796
Compare
| // to read past the `\n` character which might swallow actual application data | ||
| // alternative: the bytes that might have been read beyond `\n` would need | ||
| // to be sent somehow via `MuxerConnection` prior to reading from `stream` again | ||
| let read_bytes = stream |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A more practical eventual alternative would be to use the UnixStream::peak API, assuming it stabilizes. We could link to rust-lang/rust#76923 and then it would be obvious when this can be improved.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(Just to be clear: the reason I mention this is because I think the comment should mention it.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added the link :)
|
Maybe simplify it all by making |
0849796 to
9c94e62
Compare
|
@alyssais I adjusted the code with your suggestions. Regarding the constants, I hope to have found the middle ground between your comment and what @RuoqingHe said. Having the strings referenced as @rbradford Can you please sketch how that would work? |
a8af51c to
0acd943
Compare
Right, but don't we know that for the first read we can block until we get the connect message? |
Only when we can trust a client to send the |
| // to read past the `\n` character which might swallow actual application data | ||
| // alternative: the bytes that might have been read beyond `\n` would need | ||
| // to be sent somehow via `MuxerConnection` prior to reading from `stream` again | ||
| let read_bytes = stream |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(Just to be clear: the reason I mention this is because I think the comment should mention it.)
| LocalStream(UnixStream), | ||
| } | ||
|
|
||
| const PARTIALLY_READ_COMMAND_BUF_SIZE: usize = 32; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know this predates this change, but I wonder why the buffer is so big. The maximum possible command is 19 bytes long if I've counted correctly, so we shouldn't ever need to store more than that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Correct. My math is:
> len("CONNECT 4294967296\n")
> 19
However: With 19 byte we would require the client to send exactly this string, with some head space we would allow for some stray whitespaces, i.e. "CONNECT 4294967296\n".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've got to wonder how stray whitespaces would end up in the string from a client, and whether we want to allow that…
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can only assume some strange client code implementation.
However the current code does support e.g. CONNECT 1337\n.
I did not want to break client implementations with this PR.
Is the firecracker protocol defined somewhere, ideally as BNF?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I doubt it.
34ac0e6 to
a124595
Compare
The function `read_local_stream_port` had no proper handling for unexpected or incomplete input. When the control socket of the VSOCK device was closed without sending the expected `CONNECT <PORT>\n` statement completely, the thread got stuck in an infinite loop as it attempted to read from a closed socket over and over again which never returned any data. This resulted in the thread responsible for `epoll` being completely blocked. New VSOCK connections could not be established and existing ones became defunct, effectively leading to a Denial of Service of the entire VSOCK device. The issue can be reproduced by opening a socket and immediately closing it. ``` socat - UNIX-CONNECT:/socket.vsock <Ctrl-C> ``` Instead of applying a quick fix by handling the `EPOLLHUP` event before reading, the function is refactored to remove the error-prone `while` loop and multiple `read`s. Notably, we now check if the number of bytes read is zero, which occurs when `event_set == EPOLLHUP | EPOLLIN`, indicating that the socket has been closed by the client. Additionally, the actual parsing code is now extracted into a dedicated function that is tested. Fixes: cloud-hypervisor#6798 Signed-off-by: Maximilian Güntner <[email protected]>
a124595 to
e428f89
Compare
|
Thanks for the review, @alyssais :) |
RuoqingHe
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @mguentner for your patience, the changes look good to me
|
Can someone with the necessary rights please retry the failing CI step. It failed already 2 times due to a timeout, I do not see vsock being involved in the last tests. Random failure? |
d28d9eb
The function
read_local_stream_porthad no proper handling for unexpected or incomplete input.When the control socket of the VSOCK device was closed without sending the expected
CONNECT <PORT>\nstatement completely, the thread got stuck in an infinite loop as it attempted to read from a closed socket over and over again which never returned any data.This resulted in the thread responsible for
epollbeing completely blocked. New VSOCK connections could not be established and existing ones became defunct, effectively leading to a Denial of Service of the entire VSOCK device.The issue can be reproduced by opening a socket and immediately closing it.
Instead of applying a quick fix by handling the
EPOLLHUPevent before reading, the function is refactored to remove the error-pronewhileloop and multiplereads.Notably, we now check if the number of bytes read is zero, which occurs when
event_set == EPOLLHUP | EPOLLIN, indicating that the socket has been closed by the client.Additionally, the actual parsing code is now extracted into a dedicated function that is tested.
Fixes: #6798