I'm struggeling to reason about a error caused by Miri:
--> src/spawner.rs:25:17
|
25 | / futures::select! {
26 | | x = fut.fuse() => Ok(x),
27 | | _ = self.abort.cancellation().fuse() => Err(Aborted)
28 | | }
| | ^
| | |
| |_________________trying to retag from <602679> for Unique permission at alloc249941[0x50], but that tag does not exist in the borrow stack for this location
| this error occurs as part of retag at alloc249941[0x50..0x51]
|
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental
= help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information
The entire code however doesn't make any use of unsafe and the stripped-down version only includes three very common crates.
[dependencies]
futures = "0.3.31"
tokio = { version = "1.44.2", features = ["macros", "rt", "time"] }
uuid = { version = "1.16.0", features = ["v4"] }
The error only occurs, if all tests run together... They all work individually. But it's always the same testcase which breaks, and this is reproducable on linux and windows in different versions (cargo +nightly miri test).
The entire code can be found in the following Playground and it includes Testcases:
Despite the location of the error, it seems unrelated to futures:0.3, as I reimplemented an inefficient, but simple async channel with std::sync::Mutex (again without any unsafe code) and it failed on the same line.
I don't really know where to take it from here, as for me it is unclear wether it's a Error in miri, rust itself or one of the dependencies. In any case, my code without unsafe shouldn't be able to cause such behaviour. Right? Help would be very appreciated
Yes. But there is unsafe code in futures::select! to pin the futures it polls, and tokio to manage task execution.
This may be a problem like UB in the batch_semaphore linked list · Issue #3399 · tokio-rs/tokio · GitHub — where the problem is that Rust doesn’t really have a fully correct way for Future implementers (that aren’t async blocks) to actually take advantage of what Pin is supposed to allow. So, Miri flags UB because those futures are doing something that they technically don’t have license to do. This will be fixed by UnsafePinned, but that work is still in progress.
Thank you for the hint. I tried to wrap couple of futures in Pin<Box<_>> before select! and the MIRI-Error vanished... I later changed from futures::select! to tokio::select! and it did the trick too... The change was necessary in the following lines:
I just don't really have a lot of confidence in the code. Do you have any Idea, why this UB was only triggered when running all Tests and not on each separate?
Do you think, that this Issue should be filed in the futures crate? tokio::select obviously found a solution which is safe for my use-case and Macros should really go all the way to avoid introducing ub... Of course, I laso changed the fact that the futures was passed as mutable reference. But if I replace tokio::select! with futures::select! it still compiles, but fails again...
Probably just some kind of influence on the timing of events that affects when the rejected access happens. If the tests were sharing a tokio runtime, I would say the tokio task scheduler is involved, but they don’t.
futures is doing a very straightforward pinning. I expect that tokio is much more responsible here. As per my previous message, I expect that there isn’t actually anything tokio could do better here until UnsafePinned comes along, but I could be wrong, and there could be an actual bug here.