-
Notifications
You must be signed in to change notification settings - Fork 348
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
Miri reports "unsupported operation: unable to turn pointer into raw bytes" in futures-lite-1.12.0 #2068
Comments
https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=d70742104058788577a5ec1a412c31c0 is a minimal reproduction of this with only futures_core and no unsafe code. (I'll see if I can get it failing using AsyncIterator since apparently we have that in std) This seems very cursed because the order the tests in Edit: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=7e3c1c0b3d66d398b3b28676cc9c1286 is a reproduction using AsyncIterator and no external crates. Still fails, same error. Edit: Not an issue with AsyncIterator, it's an issue with.... I don't even know. But I got a reproduction that errors depending on the type of an unused field: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=8472d9fc6cd72bf8f3dd7ab2514b2139 Edit: Okay, the unused field is a red herring, it's just that if you have two different types with the same size but different generic parameters, they're confused with each other. See this example with only one field: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=6881cba8cb2e0e70f9f9217f05eb5802 |
I don't pretend to understand this, but we've tried a few things. |
This might be an instance of rust-lang/rust#87184? It would be the first instance of that Miri limitation in the real world. |
Specifically it seems like a read of type Arguably, under strict provenance, that read is UB since it constitutes a ptr-to-int transmutation. |
I suspect this is something about the two local variables in async {
LastFuture { last: &0u32 }.await;
LastFuture { last: Option::<u32>::None }.await;
} being stored in overlapping parts of the generator, and somehow the initialization with |
This example shows what I would expect Miri to do in that case. That's still surprising, but note that you only get an uninit error when looking at the bytes of In the |
Hm, no, a partial overwrite of a pointer is not happening in this example. Odd. |
Okay I think what actually happens is that the fields storing the It also looks like those things are initialized in-place (rather than constructed elsewhere and copied into the generator), but this is the part I am least sure about. Now the following happens: when the first future is created, the underlying allocation looks like this:
The first byte is the discriminant, then padding, then the pointer of type Now we in-place initialize a
Finally, we read an What I am not sure about is why the |
Resolving rust-lang/rust#96158 will fix this problem, since then Miri will no longer (incorrectly) try to use the more efficient representation for types like |
Ah, I figured out the puzzle with the in-place init as well: it's not in-place init, it's that writing an 'uninit' If it had cleared the provenance, the unsupported error wouldn't have happened, and it would have just treated that memory as uninitialized. Here's the stand-alone reproducer for that (needs use std::mem::MaybeUninit;
fn main() { unsafe {
let mut x = [MaybeUninit::<i32>::zeroed(); 3];
// Put in a ptr into the last 8 bytes.
x.as_mut_ptr().offset(1).cast::<&i32>().write_unaligned(&0);
// Overwrite parts of that pointer with 'uninit' through a Scalar.
let ptr = x.as_mut_ptr().offset(1).cast::<i32>();
*ptr = MaybeUninit::uninit().assume_init();
// Reading this back should hence work fine.
let _c = *ptr;
} } |
This is a nice find -- it uncovered two different rather subtle bugs, each one of which would have masked the other if it had been fixed at some point. Also, sorry for the many notifications in this thread.^^ I am done here for now. ;) |
you should have seen me absolutely losing it in the rust lang community discord for a good 2 hours trying to minimize this and work out what was going on it was great fun, if being absolutely bewildered is your idea of fun, which mine is :D |
@5225225 your minimized example was extremely helpful for me to further narrow this down, so thanks a ton for doing that legwork! I can only imagine how crazy this behavior must have seemed without a clear mental model of what happens inside Miri. (Also I am not sure if my explanation of the problem above made much sense. I'm happy to explain more of you are curious, but we should probably take that to Zulip.) |
interpret: Fix writing uninit to an allocation When calling `mark_init`, we need to also be mindful of what happens with the relocations! Specifically, when we de-init memory, we need to clear relocations in that range as well or else strange things will happen (and printing will not show the de-init, since relocations take precedence there). Fixes rust-lang/miri#2068. Here's the Miri testcase that this fixes (requires `-Zmiri-disable-validation`): ```rust use std::mem::MaybeUninit; fn main() { unsafe { let mut x = MaybeUninit::<i64>::uninit(); // Put in a ptr. x.as_mut_ptr().cast::<&i32>().write_unaligned(&0); // Overwrite parts of that pointer with 'uninit' through a Scalar. let ptr = x.as_mut_ptr().cast::<i32>(); *ptr = MaybeUninit::uninit().assume_init(); // Reading this back should hence work fine. let _c = *ptr; } } ``` Previously this failed with ``` error: unsupported operation: unable to turn pointer into raw bytes --> ../miri/uninit.rs:11:14 | 11 | let _c = *ptr; | ^^^^ unable to turn pointer into raw bytes | = help: this is likely not a bug in the program; it indicates that the program performed an operation that the interpreter does not support = note: inside `main` at ../miri/uninit.rs:11:14 ```
I'm very lost here. The only hypothesis I can come up with is that a dependency has written pointer bytes into that
Option<i32>
, but even if that is the case it doesn't make sense that Miri would report an unsupported operation. This should be a type validation failure, right?The text was updated successfully, but these errors were encountered: