Skip to content

Commit

Permalink
Replace linux_rustix opt-in backend with linux_raw
Browse files Browse the repository at this point in the history
  • Loading branch information
newpavlov committed Dec 23, 2024
1 parent 9b902af commit ca5c981
Show file tree
Hide file tree
Showing 9 changed files with 168 additions and 48 deletions.
26 changes: 25 additions & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ jobs:
RUSTFLAGS: -Dwarnings --cfg getrandom_backend="linux_getrandom"
run: cargo build --target=${{ matrix.target }} --features=std
- env:
RUSTFLAGS: -Dwarnings --cfg getrandom_backend="linux_rustix"
RUSTFLAGS: -Dwarnings --cfg getrandom_backend="linux_raw"
run: cargo build --target=${{ matrix.target }} --features=std
- env:
RUSTFLAGS: -Dwarnings --cfg getrandom_test_linux_fallback
Expand All @@ -131,6 +131,30 @@ jobs:
RUSTFLAGS: -Dwarnings --cfg getrandom_backend="rdrand"
run: cargo build --features=std

linux-raw:
name: Build Raw Linux
runs-on: ubuntu-24.04
strategy:
matrix:
target: [
arm-unknown-linux-gnueabihf,
aarch64-unknown-linux-gnu,
loongarch64-unknown-linux-gnu,
riscv32gc-unknown-linux-gnu,
riscv64gc-unknown-linux-gnu,
i686-unknown-linux-gnu,
x86_64-unknown-linux-gnu,
x86_64-unknown-linux-gnux32,
]
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@nightly
with:
targets: ${{ matrix.target }}
- env:
RUSTFLAGS: -Dwarnings --cfg getrandom_backend="linux_raw"
run: cargo build -Zbuild-std=core --target=${{ matrix.target }}

web:
name: ${{ matrix.target.description }} ${{ matrix.feature.description }} ${{ matrix.atomic.description }}
runs-on: ubuntu-24.04
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/nopanic.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,11 @@ jobs:
- name: Check (linux_android.rs)
run: (exit $( grep -c panic target/release/libgetrandom_wrapper.so ))

- name: Build (linux_rustix.rs)
- name: Build (linux_raw.rs)
env:
RUSTFLAGS: -Dwarnings --cfg getrandom_backend="linux_rustix"
RUSTFLAGS: -Dwarnings --cfg getrandom_backend="linux_raw"
run: cargo build --release
- name: Check (linux_rustix.rs)
- name: Check (linux_raw.rs)
run: (exit $( grep -c panic target/release/libgetrandom_wrapper.so ))

- name: Build (rdrand.rs)
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ jobs:
RUSTFLAGS: -Dwarnings --cfg getrandom_backend="linux_getrandom"
run: cargo test --target=${{ matrix.target }} --features=std
- env:
RUSTFLAGS: -Dwarnings --cfg getrandom_backend="linux_rustix"
RUSTFLAGS: -Dwarnings --cfg getrandom_backend="linux_raw"
run: cargo test --target=${{ matrix.target }} --features=std
- env:
RUSTFLAGS: -Dwarnings --cfg getrandom_test_linux_fallback
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/workspace.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,9 @@ jobs:
env:
RUSTFLAGS: -Dwarnings --cfg getrandom_backend="linux_getrandom"
run: cargo clippy --target x86_64-unknown-linux-gnu
- name: Linux (linux_rustix.rs)
- name: Linux (linux_raw.rs)
env:
RUSTFLAGS: -Dwarnings --cfg getrandom_backend="linux_rustix"
RUSTFLAGS: -Dwarnings --cfg getrandom_backend="linux_raw"
run: cargo clippy --target x86_64-unknown-linux-gnu
- name: Linux (linux_android_with_fallback.rs)
run: cargo clippy --target x86_64-unknown-linux-gnu
Expand Down
6 changes: 1 addition & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,6 @@ core = { version = "1.0", optional = true, package = "rustc-std-workspace-core"
[target.'cfg(all(any(target_os = "linux", target_os = "android"), not(any(target_env = "", getrandom_backend = "linux_rustix", getrandom_backend = "custom"))))'.dependencies]
libc = { version = "0.2.154", default-features = false }

# linux_rustix
[target.'cfg(all(any(target_os = "linux", target_os = "android"), any(target_env = "", getrandom_backend = "linux_rustix")))'.dependencies]
rustix = { version = "0.38.38", default-features = false, features = ["rand"] }

# apple-other
[target.'cfg(any(target_os = "ios", target_os = "visionos", target_os = "watchos", target_os = "tvos"))'.dependencies]
libc = { version = "0.2.154", default-features = false }
Expand Down Expand Up @@ -80,7 +76,7 @@ rustc-dep-of-std = ["dep:compiler_builtins", "dep:core"]
[lints.rust.unexpected_cfgs]
level = "warn"
check-cfg = [
'cfg(getrandom_backend, values("custom", "rdrand", "rndr", "linux_getrandom", "linux_rustix", "wasm_js", "esp_idf"))',
'cfg(getrandom_backend, values("custom", "rdrand", "rndr", "linux_getrandom", "linux_raw", "wasm_js", "esp_idf"))',
'cfg(getrandom_msan)',
'cfg(getrandom_test_linux_fallback)',
'cfg(getrandom_test_netbsd_fallback)',
Expand Down
9 changes: 8 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ of randomness based on their specific needs:
| Backend name | Target | Target Triple | Implementation
| ----------------- | -------------------- | ------------------------ | --------------
| `linux_getrandom` | Linux, Android | `*‑linux‑*` | [`getrandom`][1] system call (without `/dev/urandom` fallback). Bumps minimum supported Linux kernel version to 3.17 and Android API level to 23 (Marshmallow).
| `linux_rustix` | Linux, Android | `*‑linux‑*` | Same as `linux_getrandom`, but uses [`rustix`] instead of `libc`.
| `linux_raw` | Linux, Android | `*‑linux‑*` | Same as `linux_getrandom`, but uses raw `asm!`-based syscalls instead of `libc`.
| `rdrand` | x86, x86-64 | `x86_64-*`, `i686-*` | [`RDRAND`] instruction
| `rndr` | AArch64 | `aarch64-*` | [`RNDR`] register
| `esp_idf` | ESP-IDF | `*‑espidf` | [`esp_fill_random`]. WARNING: can return low-quality entropy without proper hardware configuration!
Expand All @@ -106,6 +106,13 @@ WILL NOT have any effect on its downstream users.

[`.cargo/config.toml`]: https://doc.rust-lang.org/cargo/reference/config.html

### Raw Linux syscall support

Currently the `linux_raw` backend supports only targets with stabilized `asm!` macro, i.e. `arm`, `aarch64`, `loongarch64`, `riscv32`, `riscv64`, `x86`, and `x86_64`.

Note that on `x86` we use the famously slow `int 0x80` to perform syscall.
We recommend to avoid `linux_raw` on this target arch.

### WebAssembly support

This crate fully supports the [WASI] and [Emscripten] targets. However,
Expand Down
6 changes: 3 additions & 3 deletions src/backends.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ cfg_if! {
} else if #[cfg(getrandom_backend = "linux_getrandom")] {
mod linux_android;
pub use linux_android::*;
} else if #[cfg(getrandom_backend = "linux_rustix")] {
mod linux_rustix;
pub use linux_rustix::*;
} else if #[cfg(getrandom_backend = "linux_raw")] {
mod linux_raw;
pub use linux_raw::*;
} else if #[cfg(getrandom_backend = "rdrand")] {
mod rdrand;
pub use rdrand::*;
Expand Down
125 changes: 125 additions & 0 deletions src/backends/linux_raw.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
//! Implementation for Linux / Android without `/dev/urandom` fallback
use crate::{Error, MaybeUninit};
// use rustix::rand::{getrandom_uninit, GetRandomFlags};

pub use crate::util::{inner_u32, inner_u64};

#[cfg(not(any(target_os = "android", target_os = "linux")))]
compile_error!("`linux_raw` backend can be enabled only for Linux/Android targets!");

#[allow(non_upper_case_globals)]
unsafe fn getrandom_syscall(buf: *mut u8, buflen: usize, flags: u32) -> isize {
let r0;

// Based on `rustix` and `linux-raw-sys` code.
cfg_if! {
if #[cfg(target_arch = "arm")] {
// In thumb-mode, r7 is the frame pointer and is not permitted to be used in
// an inline asm operand, so we have to use a different register and copy it
// into r7 inside the inline asm.
// Theoretically, we could detect thumb mode in the build script, but several
// register moves are cheap enough compared to the syscall cost, so we do not
// bother with it.
core::arch::asm!(
"mov {tmp}, r7",
"mov r7, #384", // __NR_getrandom = 384
"svc 0",
"mov r7, {tmp}",
tmp = out(reg) _,
inlateout("r0") buf => r0,
in("r1") buflen,
in("r2") flags,
options(nostack, preserves_flags)
);
} else if #[cfg(target_arch = "aarch64")] {
const __NR_getrandom: u32 = 278;
core::arch::asm!(
"svc 0",
in("x8") __NR_getrandom,
inlateout("x0") buf => r0,
in("x1") buflen,
in("x2") flags,
options(nostack, preserves_flags)
);
} else if #[cfg(target_arch = "loongarch64")] {
const __NR_getrandom: u32 = 278;
core::arch::asm!(
"syscall 0",
in("$a7") __NR_getrandom,
inlateout("$a0") buf => r0,
in("$a1") buflen,
in("$a2") flags,
options(nostack, preserves_flags)
);
} else if #[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))] {
const __NR_getrandom: u32 = 278;
core::arch::asm!(
"ecall",
in("a7") __NR_getrandom,
inlateout("a0") buf => r0,
in("a1") buflen,
in("a2") flags,
options(nostack, preserves_flags)
);
} else if #[cfg(target_arch = "x86")] {
const __NR_getrandom: isize = 355;
// `int 0x80` is famously slow, but implementing vDSO is too complex
// and `sysenter`/`syscall` have their own portability issues,
// so we use the simple "legacy" way of doing syscalls.
core::arch::asm!(
"int $$0x80",
inlateout("eax") __NR_getrandom => r0,
in("ebx") buf,
in("ecx") buflen,
in("edx") flags,
options(nostack, preserves_flags)
);
} else if #[cfg(target_arch = "x86_64")] {
#[cfg(target_pointer_width = "64")]
const __NR_getrandom: isize = 318;
#[cfg(target_pointer_width = "32")]
const __NR_getrandom: isize = 1073742142;

core::arch::asm!(
"syscall",
inlateout("rax") __NR_getrandom => r0,
in("rdi") buf,
in("rsi") buflen,
in("rdx") flags,
lateout("rcx") _,
lateout("r11") _,
options(nostack, preserves_flags)
);
} else {
compile_error!("`linux_raw` backend does not support this target arch");
}
}

r0
}

pub fn fill_inner(mut dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
// Value of this error code is stable across all target arches.
const EINTR: isize = -4;

loop {
let ret = unsafe { getrandom_syscall(dest.as_mut_ptr().cast(), dest.len(), 0) };
match usize::try_from(ret) {
Ok(0) => return Err(Error::UNEXPECTED),
Ok(len) => {
dest = dest.get_mut(len..).ok_or(Error::UNEXPECTED)?;
if dest.is_empty() {
return Ok(());
}
}
Err(_) if ret == EINTR => continue,
Err(_) => {
let code: u32 = ret
.wrapping_neg()
.try_into()
.map_err(|_| Error::UNEXPECTED)?;
return Err(Error::from_os_error(code));
}
}
}
}
32 changes: 0 additions & 32 deletions src/backends/linux_rustix.rs

This file was deleted.

0 comments on commit ca5c981

Please sign in to comment.