Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,7 @@ jobs:
targets: riscv32i-unknown-none-elf
- uses: Swatinem/rust-cache@v2
- env:
RUSTFLAGS: -Dwarnings --cfg getrandom_backend="custom"
RUSTFLAGS: -Dwarnings --cfg getrandom_backend="unsupported"
run: cargo build --target riscv32i-unknown-none-elf

unsupported:
Expand Down
14 changes: 9 additions & 5 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,8 @@ jobs:
RUSTDOCFLAGS: -Dwarnings --cfg getrandom_backend="rdrand"
run: cargo test --features=std,sys_rng

extern_item_impls:
name: Extern Item Implementations
custom_impl:
name: Custom and External Implementations
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v6
Expand All @@ -88,9 +88,13 @@ jobs:
toolchain: nightly
- uses: Swatinem/rust-cache@v2
- env:
RUSTFLAGS: -Dwarnings --cfg getrandom_backend="extern_item_impls"
RUSTDOCFLAGS: -Dwarnings --cfg getrandom_backend="extern_item_impls"
run: cargo test --features=std --test mod extern_item_impls
RUSTFLAGS: -Dwarnings --cfg getrandom_backend="custom"
working_directory: custom_impl_test
run: cargo test
- env:
RUSTFLAGS: -Dwarnings --cfg getrandom_backend="extern_impl"
working_directory: custom_impl_test
run: cargo test

ios:
name: iOS Simulator
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/workspace.yml
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ jobs:
- uses: Swatinem/rust-cache@v2
- name: Generate Docs
env:
RUSTDOCFLAGS: "-Dwarnings --cfg docsrs"
RUSTDOCFLAGS: -Dwarnings --cfg docsrs --cfg getrandom_backend="extern_impl"
run: cargo doc --no-deps --features std,sys_rng

typos:
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@
**/*.rs.bk
nopanic_check/Cargo.lock
nopanic_check/target/
custom_impl_test/Cargo.lock
custom_impl_test/target/
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- `RawOsError` type alias [#739]
- `SysRng` behind new feature `sys_rng` [#751]
- WASIp3 support [#779]
- `extern_impl` opt-in backend [#786] [#794]

### Changed
- Use Edition 2024 and MSRV 1.85 [#749]
Expand All @@ -18,6 +19,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
[#749]: https://github.com/rust-random/getrandom/pull/749
[#751]: https://github.com/rust-random/getrandom/pull/751
[#779]: https://github.com/rust-random/getrandom/pull/779
[#786]: https://github.com/rust-random/getrandom/pull/786
[#794]: https://github.com/rust-random/getrandom/pull/794

## [0.3.4] - 2025-10-14

Expand Down
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ exclude = [".*"]

[package.metadata.docs.rs]
features = ["std", "sys_rng"]
rustdoc-args = ["--cfg getrandom_backend=\"extern_impl\""]

[features]
# Implement From<getrandom::Error> for std::io::Error and
Expand Down Expand Up @@ -94,7 +95,7 @@ wasm-bindgen-test = "0.3"
[lints.rust.unexpected_cfgs]
level = "warn"
check-cfg = [
'cfg(getrandom_backend, values("custom", "efi_rng", "rdrand", "rndr", "linux_getrandom", "linux_raw", "windows_legacy", "unsupported", "extern_item_impls"))',
'cfg(getrandom_backend, values("custom", "efi_rng", "rdrand", "rndr", "linux_getrandom", "linux_raw", "windows_legacy", "unsupported", "extern_impl"))',
'cfg(getrandom_msan)',
'cfg(getrandom_test_linux_fallback)',
'cfg(getrandom_test_linux_without_fallback)',
Expand Down
17 changes: 9 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ of randomness based on their specific needs:
| `windows_legacy` | Windows | `*-windows-*` | [`RtlGenRandom`]
| `custom` | All targets | `*` | User-provided custom implementation (see [custom backend])
| `unsupported` | All targets | `*` | Always returns `Err(Error::UNSUPPORTED)` (see [unsupported backend])
| `extern_item_impls` | All targets | `*` | User or library provided custom implementation (see [externally implemented interface])
| `extern_impl` | All targets | `*` | Externally-provided custom implementation (see [externally implemented interface])

Opt-in backends can be enabled using the `getrandom_backend` configuration flag.
The flag can be set either by specifying the `rustflags` field in [`.cargo/config.toml`]:
Expand Down Expand Up @@ -204,17 +204,18 @@ unsafe extern "Rust" fn __getrandom_v03_custom(

### Externally Implemented Interface

Using the nightly-only feature [`extern_item_impls`](https://github.com/rust-lang/rust/issues/125418)
it is possible to provide a custom backend for `getrandom`, even to override
an existing first-party implementation. First, enable the `extern_item_impls`
opt-in backend to allow usage of this nightly feature. Then, you may provide
implementations for `fill_uninit`, `u32`, and/or `u64` with an attribute macro
from the `implementation` module.
Using the nightly-only feature [`extern_item_impls`] it is possible to provide
a custom backend for `getrandom`, even to override an existing first-party implementation.
First, enable the `extern_impl` opt-in backend to allow usage of this nightly feature.
Then, you may provide implementations for `fill_uninit`, `u32`, and/or `u64`
with an attribute macro from the `implementation` module.

[`extern_item_impls`]: https://github.com/rust-lang/rust/issues/125418

```rust
use core::mem::MaybeUninit;

#[cfg(getrandom_backend = "extern_item_impls")]
#[cfg(getrandom_backend = "extern_impl")]
#[getrandom::implementation::fill_uninit]
fn my_fill_uninit_implementation(
dest: &mut [MaybeUninit<u8>]
Expand Down
17 changes: 17 additions & 0 deletions custom_impl_test/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[package]
name = "custom_impl_test"
description = "Helper crate for testing custom implementations"
version = "0.1.0"
edition = "2024"
publish = false

[workspace]

[dependencies]
getrandom = { path = ".." }

[lints.rust.unexpected_cfgs]
level = "warn"
check-cfg = [
'cfg(getrandom_backend, values("custom", "extern_impl"))',
]
101 changes: 101 additions & 0 deletions custom_impl_test/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
use core::mem::MaybeUninit;
use getrandom::Error;

/// Chosen by fair dice roll.
const SEED: u64 = 0x9095_810F_1B2B_E175;

struct Xoshiro128PlusPlus {
s: [u32; 4],
}

impl Xoshiro128PlusPlus {
fn new(mut seed: u64) -> Self {
const PHI: u64 = 0x9e3779b97f4a7c15;
let mut s = [0u32; 4];
for val in s.iter_mut() {
seed = seed.wrapping_add(PHI);
let mut z = seed;
z = (z ^ (z >> 30)).wrapping_mul(0xbf58476d1ce4e5b9);
z = (z ^ (z >> 27)).wrapping_mul(0x94d049bb133111eb);
z = z ^ (z >> 31);
*val = z as u32;
}
Self { s }
}

fn next_u32(&mut self) -> u32 {
let res = self.s[0]
.wrapping_add(self.s[3])
.rotate_left(7)
.wrapping_add(self.s[0]);

let t = self.s[1] << 9;

self.s[2] ^= self.s[0];
self.s[3] ^= self.s[1];
self.s[1] ^= self.s[2];
self.s[0] ^= self.s[3];

self.s[2] ^= t;

self.s[3] = self.s[3].rotate_left(11);

res
}
}

pub fn custom_impl(dst: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
let mut rng = Xoshiro128PlusPlus::new(SEED);

let mut chunks = dst.chunks_exact_mut(4);
for chunk in &mut chunks {
let val = rng.next_u32();
let dst_ptr = chunk.as_mut_ptr().cast::<u32>();
unsafe { core::ptr::write_unaligned(dst_ptr, val) };
}
let rem = chunks.into_remainder();
if !rem.is_empty() {
let val = rng.next_u32();
let src_ptr = &val as *const u32 as *const MaybeUninit<u8>;
assert!(rem.len() <= 4);
unsafe { core::ptr::copy(src_ptr, rem.as_mut_ptr(), rem.len()) };
}
Ok(())
}

#[cfg(getrandom_backend = "custom")]
#[unsafe(no_mangle)]
unsafe extern "Rust" fn __getrandom_v03_custom(dst_ptr: *mut u8, len: usize) -> Result<(), Error> {
let dst = unsafe { core::slice::from_raw_parts_mut(dst_ptr.cast(), len) };
custom_impl(dst)
}

#[cfg(getrandom_backend = "extern_impl")]
#[getrandom::implementation::fill_uninit]
fn my_fill_uninit_implementation(dst: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
custom_impl(dst)
}

#[test]
fn test_custom_fill() {
let mut buf1 = [0u8; 256];
getrandom::fill(&mut buf1).unwrap();

let mut buf2 = [0u8; 256];
custom_impl(unsafe { core::slice::from_raw_parts_mut(buf2.as_mut_ptr().cast(), buf2.len()) })
.unwrap();

assert_eq!(buf1, buf2);
}

#[test]
fn test_custom_u32() {
let res = getrandom::u32().unwrap();
assert_eq!(res, 0xEAD5_840A);
}

#[test]
fn test_custom_u64() {
let res = getrandom::u64().unwrap();
assert_eq!(res, 0xA856_FCC4_EAD5_840A);
}
6 changes: 3 additions & 3 deletions src/backends.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,9 @@ cfg_if! {
} else if #[cfg(getrandom_backend = "unsupported")] {
mod unsupported;
pub use unsupported::*;
} else if #[cfg(getrandom_backend = "extern_item_impls")] {
pub(crate) mod extern_item_impls;
pub use extern_item_impls::*;
} else if #[cfg(getrandom_backend = "extern_impl")] {
pub(crate) mod extern_impl;
pub use extern_impl::*;
} else if #[cfg(all(target_os = "linux", target_env = ""))] {
mod linux_raw;
pub use linux_raw::*;
Expand Down
File renamed without changes.
13 changes: 7 additions & 6 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
#![doc = include_str!("../README.md")]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![cfg_attr(getrandom_backend = "efi_rng", feature(uefi_std))]
#![cfg_attr(getrandom_backend = "extern_item_impls", feature(extern_item_impls))]
#![cfg_attr(getrandom_backend = "extern_impl", feature(extern_item_impls))]

#[macro_use]
extern crate cfg_if;
Expand All @@ -35,18 +35,19 @@ pub use sys_rng::SysRng;

pub use crate::error::{Error, RawOsError};

/// Provides Externally Implementable Interfaces for the core functionality of this crate.
/// Attribute macros for overwriting the core functionality of this crate.
///
/// This allows `getrandom` to provide a default implementation and a common interface
/// for all crates to use, while giving users a safe way to override that default where required.
///
/// Must be enabled via the `extern_item_impls` opt-in backend, as this functionality
/// Must be enabled via the `extern_impl` opt-in backend, as this functionality
/// is currently limited to nightly.
///
/// # Examples
///
/// ```rust
/// # use core::mem::MaybeUninit;
/// # #[cfg(getrandom_backend = "extern_item_impls")]
/// # #[cfg(getrandom_backend = "extern_impl")]
/// #[getrandom::implementation::fill_uninit]
/// fn my_fill_uninit_implementation(
/// dest: &mut [MaybeUninit<u8>]
Expand All @@ -56,9 +57,9 @@ pub use crate::error::{Error, RawOsError};
/// # Err(Error::UNSUPPORTED)
/// }
/// ```
#[cfg(getrandom_backend = "extern_item_impls")]
#[cfg(getrandom_backend = "extern_impl")]
pub mod implementation {
pub use crate::backends::extern_item_impls::{fill_uninit, u32, u64};
pub use crate::backends::extern_impl::{fill_uninit, u32, u64};
}

/// Fill `dest` with random bytes from the system's preferred random number source.
Expand Down
Loading