dtolnay / ryu

Fast floating point to string conversion

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Miri reports stacked borrowing error in unsafe cast from element to array

brson opened this issue · comments

While testing tikv with miri I came across a report about a stacked borrowing violation in ryu, which we use via serde_json.

The error can be triggered against commit 90e4688 with miri from nightly-2020-04-17 by running:

cargo +nightly-2020-04-17 miri test

which outputs the error

running 12 tests
error: Undefined Behavior: no item granting write access to tag <untagged> found in borrow stack.
    --> /home/ubuntu/.rustup/toolchains/nightly-2020-04-17-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libcore/intrinsics.rs:2001:5
     |
2001 |     copy_nonoverlapping(src, dst, count)
     |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no item granting write access to tag <untagged> found in borrow stack.
     |
     = help: this indicates a potential bug in the program: it performed an invalid operation, but the rules it violated are still experimenta
l
     = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information

     = note: inside `std::intrinsics::copy_nonoverlapping::<u8>` at /home/ubuntu/.rustup/toolchains/nightly-2020-04-17-x86_64-unknown-linux-gn
u/lib/rustlib/src/rust/src/libcore/intrinsics.rs:2001:5
     = note: inside `ryu::raw::format64` at /home/ubuntu/ryu/src/pretty/mod.rs:69:9
     = note: inside `<f64 as ryu::buffer::Sealed>::write_to_ryu_buffer` at /home/ubuntu/ryu/src/buffer/mod.rs:197:9
     = note: inside `ryu::Buffer::format_finite::<f64>` at /home/ubuntu/ryu/src/buffer/mod.rs:88:21
     = note: inside `ryu::Buffer::format::<f64>` at /home/ubuntu/ryu/src/buffer/mod.rs:65:13

The cause seems to be this code:

     #[inline]
     #[cfg(maybe_uninit)]
     fn first_byte_pointer_mut(&mut self) -> *mut u8 {
        self.bytes[0].as_mut_ptr()
     }

which takes a pointer to the first element of an array, then later treats it as a pointer to an array.

I am not sure whether this is truly UB, but this diff silences miri:

     #[inline]
     #[cfg(maybe_uninit)]
     fn first_byte_pointer_mut(&mut self) -> *mut u8 {
-        self.bytes[0].as_mut_ptr()
+        self.bytes.as_mut_ptr() as *mut u8
     }

Unfortunately, after the patch miri fails to terminate.

I am unsure if this is a bug or not. cc @RalfJung

Fixed in 1.0.4.

Regarding failing to terminate, I think our tests are just slow.

I am not sure whether this is truly UB

Well, it violates our experimental aliasing model. This particular concern (whether that kind of restriction for raw pointers is desirable) is tracked at rust-lang/unsafe-code-guidelines#134.

Unfortunately, after the patch miri fails to terminate.

Yeah, Miri is really slow, so that's probably it. I often have to reduce iteration counts with cfg(miri) drastically when adding Miri support to a test suite.