diff --git a/.github/workflows/nopanic.yaml b/.github/workflows/nopanic.yaml new file mode 100644 index 00000000..a4c24173 --- /dev/null +++ b/.github/workflows/nopanic.yaml @@ -0,0 +1,72 @@ +name: No panic + +on: + push: + branches: master + pull_request: + branches: master + +permissions: + contents: read + +defaults: + run: + working-directory: nopanic_check + +env: + RUSTFLAGS: "-Dwarnings" + +jobs: + linux: + name: Linux + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@master + with: + # We need Nightly for the rust-std component for wasm32-wasip2 + toolchain: nightly-2024-10-14 + targets: wasm32-wasip1, wasm32-wasip2 + + - name: Build (linux_android_with_fallback.rs) + run: cargo build --release + - name: Check (linux_android_with_fallback.rs) + run: ret=$(grep panic target/release/libgetrandom_wrapper.so; echo $?); [ $ret -eq 1 ] + + - name: Build (linux_android.rs) + env: + RUSTFLAGS: -Dwarnings --cfg getrandom_backend="linux_getrandom" + run: cargo build --release + - name: Check (linux_android.rs) + run: ret=$(grep panic target/release/libgetrandom_wrapper.so; echo $?); [ $ret -eq 1 ] + + - name: Build (rdrand.rs) + env: + RUSTFLAGS: -Dwarnings --cfg getrandom_backend="rdrand" + run: cargo build --release + - name: Check (rdrand.rs) + run: ret=$(grep panic target/release/libgetrandom_wrapper.so; echo $?); [ $ret -eq 1 ] + + - name: Build (wasi.rs, preview 1) + run: cargo build --release --target wasm32-wasip1 + - name: Check (wasi.rs, preview 1) + run: ret=$(grep panic target/wasm32-wasip1/release/getrandom_wrapper.wasm; echo $?); [ $ret -eq 1 ] + + - name: Build (wasi.rs, preview 2) + run: cargo build --release --target wasm32-wasip2 + - name: Check (wasi.rs, preview 2) + run: ret=$(grep panic target/wasm32-wasip2/release/getrandom_wrapper.wasm; echo $?); [ $ret -eq 1 ] + + macos: + name: macOS + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@master + with: + toolchain: stable + + - name: Build (getentropy.rs) + run: cargo build --release + - name: Check (getentropy.rs) + run: ret=$(grep panic target/release/libgetrandom_wrapper.so; echo $?); [ $ret -eq 1 ] diff --git a/nopanic_check/Cargo.toml b/nopanic_check/Cargo.toml new file mode 100644 index 00000000..11b51024 --- /dev/null +++ b/nopanic_check/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "nopanic_check" +description = "Helper crate for checking that getrandom implementation does not contain potential panics" +version = "0.1.0" +edition = "2021" +publish = false + +[lib] +name = "getrandom_wrapper" +crate-type = ["cdylib"] + +[dependencies] +getrandom = { path = ".." } + +[profile.release] +panic = "abort" diff --git a/nopanic_check/src/lib.rs b/nopanic_check/src/lib.rs new file mode 100644 index 00000000..0c0a7a0d --- /dev/null +++ b/nopanic_check/src/lib.rs @@ -0,0 +1,18 @@ +// WASI preview 2 requires enabled std +#![cfg_attr(not(all(target_arch = "wasm32", target_env = "p2")), no_std)] + +#[cfg(not(any(test, all(target_arch = "wasm32", target_env = "p2"))))] +#[panic_handler] +fn panic(_info: &core::panic::PanicInfo) -> ! { + extern "C" { + fn panic_nonexistent() -> !; + } + unsafe { panic_nonexistent() } +} + +#[no_mangle] +pub extern "C" fn getrandom_wrapper(buf_ptr: *mut u8, buf_len: usize) -> u32 { + let buf = unsafe { core::slice::from_raw_parts_mut(buf_ptr.cast(), buf_len) }; + let res = getrandom::getrandom_uninit(buf).map(|_| ()); + unsafe { core::mem::transmute(res) } +} diff --git a/src/lib.rs b/src/lib.rs index 005c7a75..67a4addc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -195,6 +195,15 @@ //! on every call to `getrandom`, hence after the first successful call one //! can be reasonably confident that no errors will occur. //! +//! ## Panic handling +//! +//! We strive to eliminate all potential panics from our implementation. +//! In other words, when compiled with enabled optimizations, generated +//! binary code for `getrandom` functions should not contain any panic +//! branches. Even if platform misbiheaves and returns an unexpected +//! result, our code should correctly handle it and return an error like +//! [`Error::UNEXPECTED`]. +//! //! [1]: https://manned.org/getrandom.2 //! [2]: https://manned.org/urandom.4 //! [3]: https://www.unix.com/man-page/mojave/2/getentropy/