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

Is there a way to use potentially panicking code in a #[no_panic] function #16

Closed
KizzyCode opened this issue Apr 9, 2020 · 3 comments

Comments

@KizzyCode
Copy link

KizzyCode commented Apr 9, 2020

While this sounds contra-intuitive, there are std/core-library functions where I can/have to assume that they are correct and won't panic (e.g. str::from_utf8). However since the compiler is unable to prove for some functions that they are panic-free, I cannot use them.

Is there a way to "trust" such a function that it will never panic? Since due to the huge amounts of unsafe code in std/core, I have to rely on their correctness anyway; so it would make no difference for my crate's correctness assumptions…

@KizzyCode
Copy link
Author

KizzyCode commented Apr 9, 2020

Ah, I found a way:

#[inline(never)]
unsafe extern "C" fn is_utf8(ptr: *const u8, len: usize) -> bool {
    let slice = slice::from_raw_parts(ptr, len);
    str::from_utf8(slice).is_ok()
}

fn from_utf8(data: &[u8]) -> Result<&str, ()> {
    match unsafe{ is_utf8(data.as_ptr(), data.len()) } {
        true => Ok(unsafe{ str::from_utf8_unchecked(s) }),
        false => Err(()),
    }
}

This is ugly as hell and uses unsafe, but it allows me to use the function without giving up on #[no_panic]. If you know any better way, please let me know; if not please feel free to close this issue 😊

And thank you for no-panic!!

@dtolnay dtolnay closed this as completed in b6ab335 Apr 9, 2020
@dtolnay
Copy link
Owner

dtolnay commented Apr 9, 2020

I don't know a better way, but I've added your trick to the readme. Thanks!

@KizzyCode
Copy link
Author

KizzyCode commented Apr 16, 2020

For future readers: if you don't use no_std, you can also catch any panic in your unsafe extern "C" function – and you can use Rust-slices as arguments which also makes unsafe unnecessary 🙈.

This means, in std the code above could be replaced with

/// Checks if a string is UTF-8
#[inline(never)]
extern "C" fn is_utf8(slice: &[u8]) -> bool {
    std::panic::catch_unwind(|| str::from_utf8(slice).is_ok()).unwrap_or(false)
}

which is neither unsafe nor does it allow a panic to bubble across FFI-boundaries and invoke UB.

Repository owner locked and limited conversation to collaborators Jun 25, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants