Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CString::into_raw() trigger miri #62553

Closed
Stargateur opened this issue Jul 10, 2019 · 12 comments · Fixed by #62610
Closed

CString::into_raw() trigger miri #62553

Stargateur opened this issue Jul 10, 2019 · 12 comments · Fixed by #62610
Labels
A-ffi Area: Foreign Function Interface (FFI) A-miri Area: The miri tool

Comments

@Stargateur
Copy link
Contributor

Stargateur commented Jul 10, 2019

use std::ffi::CString;

fn main() {
    let _hello = CString::new("Hello")
        .expect("CString::new failed")
        .into_raw();
}

This simple code should not trigger any error, except a leak of course. But miri report an error before:

error[E0080]: Miri evaluation error: trying to reborrow for Unique, but parent tag <2145> does not have an appropriate item in the borrow stack
   --> /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/ffi/c_str.rs:605:13
    |
605 |             result
    |             ^^^^^^ Miri evaluation error: trying to reborrow for Unique, but parent tag <2145> does not have an appropriate item in the borrow stack
    |
    = note: inside call to `std::ffi::CString::into_inner` at /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/ffi/c_str.rs:440:23
note: inside call to `std::ffi::CString::into_raw` at src/main.rs:4:18
   --> src/main.rs:4:18
    |
4   |       let _hello = CString::new("Hello")
    |  __________________^
5   | |         .expect("CString::new failed")
6   | |         .into_raw();
    | |___________________^
    = note: inside call to `main` at /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/rt.rs:64:34
    = note: inside call to closure at /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/rt.rs:52:53
    = note: inside call to closure at /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/panicking.rs:294:40
    = note: inside call to `std::panicking::try::do_call::<[closure@DefId(1:5878 ~ std[82ff]::rt[0]::lang_start_internal[0]::{{closure}}[0]) 0:&dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe], i32>` at /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/panicking.rs:290:5
    = note: inside call to `std::panicking::try::<i32, [closure@DefId(1:5878 ~ std[82ff]::rt[0]::lang_start_internal[0]::{{closure}}[0]) 0:&dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe]>` at /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/panic.rs:388:9
    = note: inside call to `std::panic::catch_unwind::<[closure@DefId(1:5878 ~ std[82ff]::rt[0]::lang_start_internal[0]::{{closure}}[0]) 0:&dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe], i32>` at /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/rt.rs:52:25
    = note: inside call to `std::rt::lang_start_internal` at /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/rt.rs:64:5
    = note: inside call to `std::rt::lang_start::<()>`

First, I suspected a miri bug but look like the code of CString could be the problem, I don't really understand the code of the into_inner() call by into_raw().

fn into_inner(self) -> Box<[u8]> {
    unsafe {
        let result = ptr::read(&self.inner);
        mem::forget(self);
        result
    }
}

Is this code correct and it's a miri bug or the code is incorrect ?

@matklad as you write the code maybe you want be ping.

@shepmaster
Copy link
Member

/cc @RalfJung

@shepmaster
Copy link
Member

@Stargateur
Copy link
Contributor Author

fn into_inner(self) -> Box<[u8]> {
    unsafe {
        let result = std::mem::MaybeUninit::new(std::ptr::read(&self.inner));
        std::mem::forget(self);
        result.assume_init()
    }
}

Should be ok for me but it's still trigger miri

@RalfJung
Copy link
Member

On a first glance this looks like an issue I have seen before, where the problem is that mem::forget is a normal function call. So the std::ptr::read(&self.inner) creates a raw pointer and reads from it, but then the original pointer self gets used again, which invalidates the raw pointer. So this is basically the same as

let mut local = 0;
let x = &mut local;
let raw = x as *mut _; // create raw pointer
some_function(x); // use x, re-asserting that x is unique
let _val = *raw; // use raw pointer -- UB because that would violate x's uniqueness

@danielhenrymantilla
Copy link
Contributor

danielhenrymantilla commented Jul 10, 2019

What about:

fn into_inner(self) -> Box<[u8]> {
    use ::std::{mem::MaybeUninit, ptr};
    unsafe {
        type T = Box<[u8]>;
        let inner = ptr::read(&mut self.inner as *mut T as *const MaybeUninit<T>);
        std::mem::forget(self);
        inner.assume_init()
    }
}

@RalfJung
Copy link
Member

That's basically a transmute, but with even fewer compiler-level checks. At that point I'd just recommend transmuting self to Box<[u8]>.

@danielhenrymantilla
Copy link
Contributor

Isn't transmuting T to MaybeUninit<T> always valid?

@RalfJung
Copy link
Member

There's in fact a safe method for it, called MaybeUninit::new. ;)

But my suggestion does not involve MaybeUninit.

// self: CString, which is just a newtype around Box<[u8]>.
fn into_inner(self) -> Box<[u8]> {
    unsafe {
        mem::transmute(self)
    }
}

@Centril
Copy link
Contributor

Centril commented Jul 10, 2019

While using mem::transmute + comment seems like the better option in the interim, my long-term preference here would be to just remove the check that doesn't let you destructure Drop-implementing types as discussed in https://internals.rust-lang.org/t/pre-rfc-destructuring-values-that-impl-drop/10450.

@danielhenrymantilla
Copy link
Contributor

There's in fact a safe method for it, called MaybeUninit::new. ;)

This leads to @Stargateur 's suggestion, which seems to trigger Miri nevertheless.

But my suggestion does not involve MaybeUninit.

This indeed solves the problem here, but I was wondering about the more general pattern (See this URLO post):

what if, for instance, CString had a second non ZST field: needing to extract an unaliased pointer from a struct implementing Drop is actually not that easy to do soundly, once transmuting it entirely is not doable;


Another idea (again, considering that CString may contain a second field that prevents it from being transmuted into the return value directly):

fn into_inner (self) -> Box<[u8]>
{
    use ::core::{mem::MaybeUninit, ptr};
    let this = MaybeUninit::new(self);
    unsafe {
        ptr::read(&mut (*this.as_mut_ptr()).inner)
    }
}

@spunit262
Copy link
Contributor

@danielhenrymantilla
ManuallyDrop would be more appropriate than MaybeUninit as it more correctly documents what you're doing. Also both MaybeUninit and forget use ManuallyDrop internally.

fn into_inner(self) -> Box<[u8]> {
    let this = mem::ManuallyDrop::new(self);
    unsafe {
        ptr::read(&this.inner)
    }
}

@Stargateur
Copy link
Contributor Author

So we have 2 clean solutions that doesn't trigger miri what do we pick ?

bors added a commit that referenced this issue Jul 14, 2019
…r=RalfJung

Fix miri error in into_inner() of CString

Fix #62553

I choice to not transmute because I think it's more unsafe and in case the structure change this code should always work.

r? @RalfJung
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-ffi Area: Foreign Function Interface (FFI) A-miri Area: The miri tool
Projects
None yet
Development

Successfully merging a pull request may close this issue.

7 participants