SmallFloat Deref impl violates aliasing rules
SmallFloat::deref()
always calls SmallFloat::update_d()
, which writes to the value of self.inner.d
using an UnsafeCell
. However, this write invalidates any &Float
references previously returned from SmallFloat::deref()
, since Float
is defined as a mpfr_t
with no interior mutability allowed. To illustrate, this program results in an error on Miri:
// cargo +nightly miri run
use rug::float::SmallFloat;
fn main() {
let small = SmallFloat::new();
let big = &*small;
let _ = &*small;
let _ = &*big;
}
error: Undefined Behavior: trying to retag from <2561> for SharedReadOnly permission at alloc1306[0x18], but that tag does not exist in the borrow stack for this location
--> src/main.rs:8:13
|
8 | let _ = &*big;
| ^^^^^
| |
| trying to retag from <2561> for SharedReadOnly permission at alloc1306[0x18], but that tag does not exist in the borrow stack for this location
| this error occurs as part of retag at alloc1306[0x0..0x20]
|
= 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: <2561> was created by a SharedReadOnly retag at offsets [0x0..0x20]
--> src/main.rs:6:15
|
6 | let big = &*small;
| ^^^^^^^
help: <2561> was later invalidated at offsets [0x18..0x20] by a write access
--> src/main.rs:7:14
|
7 | let _ = &*small;
| ^^^^^^
= note: BACKTRACE (of the first span):
= note: inside `main` at src/main.rs:8:13: 8:18
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
To fix this, either Float
must be defined to allow interior mutability, or the call to SmallFloat::update_d()
must be delayed until no more references from SmallFloat::deref()
are live. Unfortunately this issue can't be fixed without a breaking change: the former change would require making Float
!Sync
, and the latter (as far as I am aware) would either require a &mut SmallFloat
to obtain a &Float
, or require the &Float
to be scoped to a callback.