[go: up one dir, main page]

Skip to content

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.