I'm using the FFI to access an API that requires us to supply a callback. However, many of the API functions I want to use inside the callback also trigger the callback (luckily, this is all single-threaded). While doing this, we also need to manage mutable state. This, however, means we risk violating the aliasing rules if, for example, both the top level and the recursive call create a mutable reference to the mutable state. I've included a simplified example below. How can I use this API from Rust without UB?
use std::sync::atomic::{AtomicPtr, Ordering};
static HANDLER: AtomicPtr<i32> = AtomicPtr::new(std::ptr::null_mut());
enum Op {
Inc,
IncRecursive,
}
fn f(x: &mut i32, op: Op) {
match op {
Op::Inc => *x += 1,
Op::IncRecursive => {
// Uncommenting this (which should be a no-op) makes it pass MIRI:
// HANDLER.store(x, Ordering::Relaxed);
g(Op::Inc);
}
}
}
// This would be the callback handler passed to the API.
fn g(op: Op) {
let x = HANDLER.load(Ordering::Relaxed);
f(unsafe { &mut *x }, op);
}
fn main() {
let mut x = 0;
HANDLER.store(&mut x, Ordering::Relaxed);
// Simulate a callback invocation.
g(Op::IncRecursive);
}
Errors:
Compiling playground v0.0.1 (/playground)
Finished dev [unoptimized + debuginfo] target(s) in 0.43s
Running `/playground/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/bin/cargo-miri runner target/miri/x86_64-unknown-linux-gnu/debug/playground`
error: Undefined Behavior: not granting access to tag <2855> because that would remove [Unique for <2880>] which is protected because it is an argument of call 836
--> src/main.rs:23:16
|
23 | f(unsafe { &mut *x }, op);
| ^^^^^^^ not granting access to tag <2855> because that would remove [Unique for <2880>] which is protected because it is an argument of call 836
|
= 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
help: <2855> was created by a SharedReadWrite retag at offsets [0x0..0x4]
--> src/main.rs:28:19
|
28 | HANDLER.store(&mut x, Ordering::Relaxed);
| ^^^^^^
help: <2880> is this argument
--> src/main.rs:10:6
|
10 | fn f(x: &mut i32, op: Op) {
| ^
= note: BACKTRACE:
= note: inside `g` at src/main.rs:23:16
note: inside `f` at src/main.rs:16:13
--> src/main.rs:16:13
|
16 | g(Op::Inc);
| ^^^^^^^^^^
note: inside `g` at src/main.rs:23:5
--> src/main.rs:23:5
|
23 | f(unsafe { &mut *x }, op);
| ^^^^^^^^^^^^^^^^^^^^^^^^^
note: inside `main` at src/main.rs:29:5
--> src/main.rs:29:5
|
29 | g(Op::IncRecursive);
| ^^^^^^^^^^^^^^^^^^^
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
error: aborting due to previous error