-
Notifications
You must be signed in to change notification settings - Fork 69
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
ByteValued implementation for VhostUserInflight is UB #215
Comments
I still do not understand why we need this to be 0 initialized. At least from a Rust perspective we should be fine... We will read "any" padding, but it should not be undefined behaviour. When reading from volatile memory we only care that we write valid values to the type. If the padding gets fixed to an int type, any value is valid. So IMHO we can just add the padding field and are fine... |
Yep, I agree on that. |
Why not?, it's not a performance-critical message (I guess), and it's not only Rust also C++ (although it has special rules for |
This is not correct it definitely is UB, adding the explicit padding probably will work in the current rustc implementation for this specific architecture, but is not part of the language for rust language type reading uninit memory is instant UB even if you cheat the compiler
uninit (i.e., undef/poison) is not a valid value for any type, but volatile memory has nothing to do with uninit memory
|
Maybe we can just forget about qemu(*), and just add the explicit padding for this crate implementation of the frontend, but the backend will always skip the padding, so we need 2 definitions of the struct with/without the padding. I'm afraid the code will not be pretty :| (*) I still think that qemu should be fix making that struct packed |
I do not think this is true. I understand that there is undefined behaviour if a struct is not fully assigned. I also understand that we need to add the padding. But we do not need to mandate a value. Regardless of the padding being initialized or not when put into shared-memory, we DO initialize it in Rust. Maybe it was not initialized in C - thats a problem when QEMU access it of course - but we do not care. We read Or stated differently: As long as whatever implements ByteValued is packed and only contains integer fields, the ByteValued abstraction should be safe. So: Could you elaborate where you think the struct may not be fully assigned? 🤔 |
I think we need to split the issue in 2 small issues and address them separated:
For 1, we should provide a fix that can work with any QEMU/VMM, also old ones. For 2, we need to update the spec and add a feature for that. I'm not sure if it is worth it, but I can't see how we can change that (adding packed, or adding a payload) without breaking compatibility. |
Maybe I didn't explain myself correctly, it's UB only if there is unit mem, whether the padding is implicit or explicit. If we read 4 bytes as
I agree |
The thing that's not clear to me is why only the padding has to be Also unclear to me is why if the remote application sending bytes puts the padding at 0 it's not UB, while if it puts random values in it is UB. |
This is the same concern that I have and what @germag and me have been haggling about :)
This is true for what we read from sockets. But it should not really matter. The story should be the same for reading from shared memory. We may read arbitrary data, but we never leave anything uninitialized. Ultimately, the bug that we have is that The best fix - as you suggested - is of course to fix the ambiguity in the spec. |
rust-vmm/vm-memory#246 would help with these kind of issues... |
The
VhostUserInflight
struct implementsByteValued
, but it has 4 bytes of padding at the end, so reading that uninitialized memory is UB, making the API unsoundthe problem is basically reading that uninitialized memory as
&[u8]
, since type reading uinit memory is UB and a reference to uninit mem is also UB.Possible solution
Although the right thing to do is to fix qemu and have this struct be packed, it will be difficult not to break backward compatibility.
Perhaps, we can fix this while keeping the backwards compatibility:
Make
VhostUserInflight
#[repr(C, packed)]
, after readingVhostUserInflight
, readMaybeUninit<u32>
, we can add a new methodEndpoint<R>::skip(len: usize)
. But, we need to skip it at the beginning ofBackendReqHandler::handle_request()
Fix qemu (and frontend) adding an explicit padding field guaranteeing that it will be initialized to
0
:but this forces us to have two
VhostUserInflight
definitions, one for the backend and one for the frontend, because the frontend must continue sending the unpacked version with padding.This issue is to continue the discussion started on #208
The text was updated successfully, but these errors were encountered: