From 557c77a149870aaf65a1f7b6967a7283530114a7 Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Fri, 23 Jan 2026 14:42:56 +1100 Subject: [PATCH 01/12] Add optional support for extern_item_impls Adds support for the nightly-only feature `extern_item_impls` gated behind a new fRust flag `getrandom_extern_item_impls`. This allows overriding the default backend implementation or providing one where it would otherwise not be available from safe Rust code in a user-friendly and idiomatic way. Offering this PR largely as a hypothetical since there are open questions (e.g., if this was stable, should it be optional or default? should there be a way to prevent overriding the default backend to guard against malicious libraries?, etc.) --- Cargo.toml | 1 + README.md | 26 ++++++++++++++ src/backends.rs | 93 +++++++++++++++++++++++++++++++++++-------------- src/lib.rs | 27 ++++++++++++++ 4 files changed, 120 insertions(+), 27 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d12a113b..1ba23da8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -94,6 +94,7 @@ wasm-bindgen-test = "0.3" [lints.rust.unexpected_cfgs] level = "warn" check-cfg = [ + 'cfg(getrandom_extern_item_impls)', 'cfg(getrandom_backend, values("custom", "efi_rng", "rdrand", "rndr", "linux_getrandom", "linux_raw", "windows_legacy", "unsupported"))', 'cfg(getrandom_msan)', 'cfg(getrandom_test_linux_fallback)', diff --git a/README.md b/README.md index 81db0a89..cc076b4b 100644 --- a/README.md +++ b/README.md @@ -201,6 +201,32 @@ unsafe extern "Rust" fn __getrandom_v03_custom( } ``` +### Externally Implemented Interface (Nightly) + +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 `getrandom_extern_item_impls` +Rust flag 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 `implemtation` module. + +```rust +use core::mem::MaybeUninit; + +#[getrandom::implementation::fill_uninit] +fn my_fill_uninit_implementation( + dest: &mut [MaybeUninit] +) -> Result<(), getrandom::Error> { + // ... + Ok(()) +} +``` + +If `getrandom` is able to provide a backend implemtation, it will be a weak +symbol that can be overridden as above. If no implementation is available, +a compilation error will be raised with instructions for how to provide +an implementation. + ### Unsupported backend In some rare scenarios, you might be compiling this crate for an unsupported diff --git a/src/backends.rs b/src/backends.rs index b5d5bd1d..bfd87622 100644 --- a/src/backends.rs +++ b/src/backends.rs @@ -7,37 +7,72 @@ //! The function MUST NOT ever write uninitialized bytes into `dest`, //! regardless of what value it returns. +/// Declares this function as an external implementation of [`u32`](crate::u32). +#[cfg_attr(getrandom_extern_item_impls, eii(u32))] +pub(crate) fn inner_u32() -> Result { + default_u32() +} + +/// Declares this function as an external implementation of [`u64`](crate::u64). +#[cfg_attr(getrandom_extern_item_impls, eii(u64))] +pub(crate) fn inner_u64() -> Result { + default_u64() +} + +macro_rules! implementation { + () => { + use $crate::util::{inner_u32 as default_u32, inner_u64 as default_u64}; + + /// Declares this function as an external implementation of [`fill_uninit`](crate::fill_uninit). + #[cfg_attr(getrandom_extern_item_impls, eii(fill_uninit))] + pub(crate) fn fill_inner( + dest: &mut [::core::mem::MaybeUninit], + ) -> Result<(), $crate::Error>; + }; + ($backend:ident) => { + use $backend::{inner_u32 as default_u32, inner_u64 as default_u64}; + + /// Declares this function as an external implementation of [`fill_uninit`](crate::fill_uninit). + #[cfg_attr(getrandom_extern_item_impls, eii(fill_uninit))] + pub(crate) fn fill_inner( + dest: &mut [::core::mem::MaybeUninit], + ) -> Result<(), $crate::Error> { + $backend::fill_inner(dest) + } + }; +} + cfg_if! { if #[cfg(getrandom_backend = "custom")] { mod custom; - pub use custom::*; + implementation!(custom); } else if #[cfg(getrandom_backend = "linux_getrandom")] { mod getrandom; - pub use getrandom::*; + implementation!(getrandom); } else if #[cfg(getrandom_backend = "linux_raw")] { mod linux_raw; - pub use linux_raw::*; + implementation!(linux_raw); } else if #[cfg(getrandom_backend = "rdrand")] { mod rdrand; - pub use rdrand::*; + implementation!(rdrand); } else if #[cfg(getrandom_backend = "rndr")] { mod rndr; - pub use rndr::*; + implementation!(rndr); } else if #[cfg(getrandom_backend = "efi_rng")] { mod efi_rng; - pub use efi_rng::*; + implementation!(efi_rng); } else if #[cfg(getrandom_backend = "windows_legacy")] { mod windows_legacy; - pub use windows_legacy::*; + implementation!(windows_legacy); } else if #[cfg(getrandom_backend = "unsupported")] { mod unsupported; - pub use unsupported::*; + implementation!(unsupported); } else if #[cfg(all(target_os = "linux", target_env = ""))] { mod linux_raw; - pub use linux_raw::*; + implementation!(linux_raw); } else if #[cfg(target_os = "espidf")] { mod esp_idf; - pub use esp_idf::*; + implementation!(esp_idf); } else if #[cfg(any( target_os = "haiku", target_os = "redox", @@ -45,7 +80,7 @@ cfg_if! { target_os = "aix", ))] { mod use_file; - pub use use_file::*; + implementation!(use_file); } else if #[cfg(any( target_os = "macos", target_os = "openbsd", @@ -53,7 +88,7 @@ cfg_if! { target_os = "emscripten", ))] { mod getentropy; - pub use getentropy::*; + implementation!(getentropy); } else if #[cfg(any( // Rust supports Android API level 19 (KitKat) [0] and the next upgrade targets // level 21 (Lollipop) [1], while `getrandom(2)` was added only in @@ -101,7 +136,7 @@ cfg_if! { ))] { mod use_file; mod linux_android_with_fallback; - pub use linux_android_with_fallback::*; + implementation!(linux_android_with_fallback); } else if #[cfg(any( target_os = "android", target_os = "linux", @@ -115,16 +150,16 @@ cfg_if! { all(target_os = "horizon", target_arch = "arm"), ))] { mod getrandom; - pub use getrandom::*; + implementation!(getrandom); } else if #[cfg(target_os = "solaris")] { mod solaris; - pub use solaris::*; + implementation!(solaris); } else if #[cfg(target_os = "netbsd")] { mod netbsd; - pub use netbsd::*; + implementation!(netbsd); } else if #[cfg(target_os = "fuchsia")] { mod fuchsia; - pub use fuchsia::*; + implementation!(fuchsia); } else if #[cfg(any( target_os = "ios", target_os = "visionos", @@ -132,40 +167,42 @@ cfg_if! { target_os = "tvos", ))] { mod apple_other; - pub use apple_other::*; + implementation!(apple_other); } else if #[cfg(all(target_arch = "wasm32", target_os = "wasi"))] { cfg_if! { if #[cfg(target_env = "p1")] { mod wasi_p1; - pub use wasi_p1::*; + implementation!(wasi_p1); } else { mod wasi_p2_3; - pub use wasi_p2_3::*; + implementation!(wasi_p2_3); } } } else if #[cfg(target_os = "hermit")] { mod hermit; - pub use hermit::*; + implementation!(hermit); } else if #[cfg(target_os = "vxworks")] { mod vxworks; - pub use vxworks::*; + implementation!(vxworks); } else if #[cfg(target_os = "solid_asp3")] { mod solid; - pub use solid::*; + implementation!(solid); } else if #[cfg(all(windows, target_vendor = "win7"))] { mod windows_legacy; - pub use windows_legacy::*; + implementation!(windows_legacy); } else if #[cfg(windows)] { mod windows; - pub use windows::*; + implementation!(windows); } else if #[cfg(all(target_arch = "x86_64", target_env = "sgx"))] { mod rdrand; - pub use rdrand::*; + implementation!(rdrand); } else if #[cfg(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none")))] { cfg_if! { if #[cfg(feature = "wasm_js")] { mod wasm_js; - pub use wasm_js::*; + implementation!(wasm_js); + } else if #[cfg(getrandom_extern_item_impls)] { + implementation!(); } else { compile_error!(concat!( "The wasm32-unknown-unknown targets are not supported by default; \ @@ -175,6 +212,8 @@ cfg_if! { )); } } + } else if #[cfg(getrandom_extern_item_impls)] { + implementation!(); } else { compile_error!(concat!( "target is not supported. You may need to define a custom backend see: \ diff --git a/src/lib.rs b/src/lib.rs index ed69ad23..18ddf7f1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -26,6 +26,7 @@ clippy::unnecessary_cast, clippy::useless_conversion )] +#![cfg_attr(getrandom_extern_item_impls, feature(extern_item_impls))] #[macro_use] extern crate cfg_if; @@ -50,6 +51,32 @@ pub use sys_rng::SysRng; pub use crate::error::{Error, RawOsError}; +#[cfg(getrandom_extern_item_impls)] +pub mod implementation { + //! Provides Externally Implementable Iterfaces for 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 `getrandom_extern_item_impls` compiler flag, as this functionality + //! is currently limited to nightly. + //! + //! # Examples + //! + //! ```rust + //! # use core::mem::MaybeUninit; + //! #[getrandom::implementation::fill_uninit] + //! fn my_fill_uninit_implementation( + //! dest: &mut [MaybeUninit] + //! ) -> Result<(), getrandom::Error> { + //! // ... + //! # let _ = dest; + //! # Err(Error::UNSUPPORTED) + //! } + //! ``` + + pub use crate::backends::{fill_uninit, u32, u64}; +} + /// Fill `dest` with random bytes from the system's preferred random number source. /// /// This function returns an error on any failure, including partial reads. We From 3db99c1eb008256e0b62672e934b46068829ce67 Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Fri, 23 Jan 2026 15:16:31 +1100 Subject: [PATCH 02/12] Formatting and Typos --- README.md | 4 ++-- src/backends.rs | 4 ++-- src/lib.rs | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index cc076b4b..dedcbbff 100644 --- a/README.md +++ b/README.md @@ -208,7 +208,7 @@ it is possible to provide a custom backend for `getrandom`, even to override an existing first-party implementation. First, enable the `getrandom_extern_item_impls` Rust flag 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 `implemtation` module. +from the `implementation` module. ```rust use core::mem::MaybeUninit; @@ -222,7 +222,7 @@ fn my_fill_uninit_implementation( } ``` -If `getrandom` is able to provide a backend implemtation, it will be a weak +If `getrandom` is able to provide a backend implementation, it will be a weak symbol that can be overridden as above. If no implementation is available, a compilation error will be raised with instructions for how to provide an implementation. diff --git a/src/backends.rs b/src/backends.rs index bfd87622..35ef5aba 100644 --- a/src/backends.rs +++ b/src/backends.rs @@ -22,7 +22,7 @@ pub(crate) fn inner_u64() -> Result { macro_rules! implementation { () => { use $crate::util::{inner_u32 as default_u32, inner_u64 as default_u64}; - + /// Declares this function as an external implementation of [`fill_uninit`](crate::fill_uninit). #[cfg_attr(getrandom_extern_item_impls, eii(fill_uninit))] pub(crate) fn fill_inner( @@ -31,7 +31,7 @@ macro_rules! implementation { }; ($backend:ident) => { use $backend::{inner_u32 as default_u32, inner_u64 as default_u64}; - + /// Declares this function as an external implementation of [`fill_uninit`](crate::fill_uninit). #[cfg_attr(getrandom_extern_item_impls, eii(fill_uninit))] pub(crate) fn fill_inner( diff --git a/src/lib.rs b/src/lib.rs index 18ddf7f1..dac5e373 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -53,7 +53,7 @@ pub use crate::error::{Error, RawOsError}; #[cfg(getrandom_extern_item_impls)] pub mod implementation { - //! Provides Externally Implementable Iterfaces for the core functionality of this crate. + //! Provides Externally Implementable Interfaces for 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. //! From 8b445e8d3d51331cb4cf606128cd3ec26aa99dc0 Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Fri, 23 Jan 2026 15:20:41 +1100 Subject: [PATCH 03/12] Prevent feature-gate breaking rustdoc --- src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib.rs b/src/lib.rs index dac5e373..1754910c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -64,6 +64,7 @@ pub mod implementation { //! //! ```rust //! # use core::mem::MaybeUninit; + //! # #[cfg(getrandom_extern_item_impls)] //! #[getrandom::implementation::fill_uninit] //! fn my_fill_uninit_implementation( //! dest: &mut [MaybeUninit] From 5688a01bc6f9e713e5a75391c7a5f5ff496cbfc3 Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Fri, 23 Jan 2026 15:20:41 +1100 Subject: [PATCH 04/12] Prevent feature-gate breaking rustdoc --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index dedcbbff..ed77fe94 100644 --- a/README.md +++ b/README.md @@ -213,6 +213,7 @@ from the `implementation` module. ```rust use core::mem::MaybeUninit; +#[cfg(getrandom_extern_item_impls)] #[getrandom::implementation::fill_uninit] fn my_fill_uninit_implementation( dest: &mut [MaybeUninit] From c0cd3730fadafa590e895fab079ce33798383f7f Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Tue, 27 Jan 2026 08:01:38 +1100 Subject: [PATCH 05/12] Refactor EII as a self-contained backend --- Cargo.toml | 3 +- README.md | 13 +++-- src/backends.rs | 96 ++++++++++--------------------- src/backends/extern_item_impls.rs | 19 ++++++ src/lib.rs | 10 ++-- 5 files changed, 62 insertions(+), 79 deletions(-) create mode 100644 src/backends/extern_item_impls.rs diff --git a/Cargo.toml b/Cargo.toml index 1ba23da8..3dfec6ec 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -94,8 +94,7 @@ wasm-bindgen-test = "0.3" [lints.rust.unexpected_cfgs] level = "warn" check-cfg = [ - 'cfg(getrandom_extern_item_impls)', - 'cfg(getrandom_backend, values("custom", "efi_rng", "rdrand", "rndr", "linux_getrandom", "linux_raw", "windows_legacy", "unsupported"))', + 'cfg(getrandom_backend, values("custom", "efi_rng", "rdrand", "rndr", "linux_getrandom", "linux_raw", "windows_legacy", "unsupported", "extern_item_impls"))', 'cfg(getrandom_msan)', 'cfg(getrandom_test_linux_fallback)', 'cfg(getrandom_test_linux_without_fallback)', diff --git a/README.md b/README.md index ed77fe94..d40041d5 100644 --- a/README.md +++ b/README.md @@ -205,15 +205,15 @@ unsafe extern "Rust" fn __getrandom_v03_custom( 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 `getrandom_extern_item_impls` -Rust flag to allow usage of this nightly feature. Then, you may provide -implementations for `fill_uninit`, `u32`, and/or u64` with an attribute macro +an existing first-party implementation. First, enable the `gextern_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. ```rust use core::mem::MaybeUninit; -#[cfg(getrandom_extern_item_impls)] +#[cfg(getrandom_backend = "extern_item_impls")] #[getrandom::implementation::fill_uninit] fn my_fill_uninit_implementation( dest: &mut [MaybeUninit] @@ -223,8 +223,9 @@ fn my_fill_uninit_implementation( } ``` -If `getrandom` is able to provide a backend implementation, it will be a weak -symbol that can be overridden as above. If no implementation is available, +`getrandom` will provide a default implementation for `u32` and `u64`, but does +not currently provide a default for `fill_uninit`, even if one is normally +available for the current target. If no implementation is available, a compilation error will be raised with instructions for how to provide an implementation. diff --git a/src/backends.rs b/src/backends.rs index 35ef5aba..db06e033 100644 --- a/src/backends.rs +++ b/src/backends.rs @@ -7,72 +7,40 @@ //! The function MUST NOT ever write uninitialized bytes into `dest`, //! regardless of what value it returns. -/// Declares this function as an external implementation of [`u32`](crate::u32). -#[cfg_attr(getrandom_extern_item_impls, eii(u32))] -pub(crate) fn inner_u32() -> Result { - default_u32() -} - -/// Declares this function as an external implementation of [`u64`](crate::u64). -#[cfg_attr(getrandom_extern_item_impls, eii(u64))] -pub(crate) fn inner_u64() -> Result { - default_u64() -} - -macro_rules! implementation { - () => { - use $crate::util::{inner_u32 as default_u32, inner_u64 as default_u64}; - - /// Declares this function as an external implementation of [`fill_uninit`](crate::fill_uninit). - #[cfg_attr(getrandom_extern_item_impls, eii(fill_uninit))] - pub(crate) fn fill_inner( - dest: &mut [::core::mem::MaybeUninit], - ) -> Result<(), $crate::Error>; - }; - ($backend:ident) => { - use $backend::{inner_u32 as default_u32, inner_u64 as default_u64}; - - /// Declares this function as an external implementation of [`fill_uninit`](crate::fill_uninit). - #[cfg_attr(getrandom_extern_item_impls, eii(fill_uninit))] - pub(crate) fn fill_inner( - dest: &mut [::core::mem::MaybeUninit], - ) -> Result<(), $crate::Error> { - $backend::fill_inner(dest) - } - }; -} - cfg_if! { if #[cfg(getrandom_backend = "custom")] { mod custom; - implementation!(custom); + pub use custom::*; } else if #[cfg(getrandom_backend = "linux_getrandom")] { mod getrandom; - implementation!(getrandom); + pub use getrandom::*; } else if #[cfg(getrandom_backend = "linux_raw")] { mod linux_raw; - implementation!(linux_raw); + pub use linux_raw::*; } else if #[cfg(getrandom_backend = "rdrand")] { mod rdrand; - implementation!(rdrand); + pub use rdrand::*; } else if #[cfg(getrandom_backend = "rndr")] { mod rndr; - implementation!(rndr); + pub use rndr::*; } else if #[cfg(getrandom_backend = "efi_rng")] { mod efi_rng; - implementation!(efi_rng); + pub use efi_rng::*; } else if #[cfg(getrandom_backend = "windows_legacy")] { mod windows_legacy; - implementation!(windows_legacy); + pub use windows_legacy::*; } else if #[cfg(getrandom_backend = "unsupported")] { mod unsupported; - implementation!(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(all(target_os = "linux", target_env = ""))] { mod linux_raw; - implementation!(linux_raw); + pub use linux_raw::*; } else if #[cfg(target_os = "espidf")] { mod esp_idf; - implementation!(esp_idf); + pub use esp_idf::*; } else if #[cfg(any( target_os = "haiku", target_os = "redox", @@ -80,7 +48,7 @@ cfg_if! { target_os = "aix", ))] { mod use_file; - implementation!(use_file); + pub use use_file::*; } else if #[cfg(any( target_os = "macos", target_os = "openbsd", @@ -88,7 +56,7 @@ cfg_if! { target_os = "emscripten", ))] { mod getentropy; - implementation!(getentropy); + pub use getentropy::*; } else if #[cfg(any( // Rust supports Android API level 19 (KitKat) [0] and the next upgrade targets // level 21 (Lollipop) [1], while `getrandom(2)` was added only in @@ -136,7 +104,7 @@ cfg_if! { ))] { mod use_file; mod linux_android_with_fallback; - implementation!(linux_android_with_fallback); + pub use linux_android_with_fallback::*; } else if #[cfg(any( target_os = "android", target_os = "linux", @@ -150,16 +118,16 @@ cfg_if! { all(target_os = "horizon", target_arch = "arm"), ))] { mod getrandom; - implementation!(getrandom); + pub use getrandom::*; } else if #[cfg(target_os = "solaris")] { mod solaris; - implementation!(solaris); + pub use solaris::*; } else if #[cfg(target_os = "netbsd")] { mod netbsd; - implementation!(netbsd); + pub use netbsd::*; } else if #[cfg(target_os = "fuchsia")] { mod fuchsia; - implementation!(fuchsia); + pub use fuchsia::*; } else if #[cfg(any( target_os = "ios", target_os = "visionos", @@ -167,42 +135,40 @@ cfg_if! { target_os = "tvos", ))] { mod apple_other; - implementation!(apple_other); + pub use apple_other::*; } else if #[cfg(all(target_arch = "wasm32", target_os = "wasi"))] { cfg_if! { if #[cfg(target_env = "p1")] { mod wasi_p1; - implementation!(wasi_p1); + pub use wasi_p1::*; } else { mod wasi_p2_3; - implementation!(wasi_p2_3); + pub use wasi_p2_3::*; } } } else if #[cfg(target_os = "hermit")] { mod hermit; - implementation!(hermit); + pub use hermit::*; } else if #[cfg(target_os = "vxworks")] { mod vxworks; - implementation!(vxworks); + pub use vxworks::*; } else if #[cfg(target_os = "solid_asp3")] { mod solid; - implementation!(solid); + pub use solid::*; } else if #[cfg(all(windows, target_vendor = "win7"))] { mod windows_legacy; - implementation!(windows_legacy); + pub use windows_legacy::*; } else if #[cfg(windows)] { mod windows; - implementation!(windows); + pub use windows::*; } else if #[cfg(all(target_arch = "x86_64", target_env = "sgx"))] { mod rdrand; - implementation!(rdrand); + pub use rdrand::*; } else if #[cfg(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none")))] { cfg_if! { if #[cfg(feature = "wasm_js")] { mod wasm_js; - implementation!(wasm_js); - } else if #[cfg(getrandom_extern_item_impls)] { - implementation!(); + pub use wasm_js::*; } else { compile_error!(concat!( "The wasm32-unknown-unknown targets are not supported by default; \ @@ -212,8 +178,6 @@ cfg_if! { )); } } - } else if #[cfg(getrandom_extern_item_impls)] { - implementation!(); } else { compile_error!(concat!( "target is not supported. You may need to define a custom backend see: \ diff --git a/src/backends/extern_item_impls.rs b/src/backends/extern_item_impls.rs new file mode 100644 index 00000000..cf894d81 --- /dev/null +++ b/src/backends/extern_item_impls.rs @@ -0,0 +1,19 @@ +//! An implementation which calls out to an externally defined function. +use crate::Error; +use core::mem::MaybeUninit; + +/// Declares this function as an external implementation of [`fill_uninit`](crate::fill_uninit). +#[cfg_attr(getrandom_backend = "extern_item_impls", eii(fill_uninit))] +pub(crate) fn fill_inner(dest: &mut [MaybeUninit]) -> Result<(), Error>; + +/// Declares this function as an external implementation of [`u32`](crate::u32). +#[cfg_attr(getrandom_backend = "extern_item_impls", eii(u32))] +pub(crate) fn inner_u32() -> Result { + crate::util::inner_u32() +} + +/// Declares this function as an external implementation of [`u64`](crate::u64). +#[cfg_attr(getrandom_backend = "extern_item_impls", eii(u64))] +pub(crate) fn inner_u64() -> Result { + crate::util::inner_u64() +} diff --git a/src/lib.rs b/src/lib.rs index 1754910c..5d8edc9f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -26,7 +26,7 @@ clippy::unnecessary_cast, clippy::useless_conversion )] -#![cfg_attr(getrandom_extern_item_impls, feature(extern_item_impls))] +#![cfg_attr(getrandom_backend = "extern_item_impls", feature(extern_item_impls))] #[macro_use] extern crate cfg_if; @@ -51,20 +51,20 @@ pub use sys_rng::SysRng; pub use crate::error::{Error, RawOsError}; -#[cfg(getrandom_extern_item_impls)] +#[cfg(getrandom_backend = "extern_item_impls")] pub mod implementation { //! Provides Externally Implementable Interfaces for 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 `getrandom_extern_item_impls` compiler flag, as this functionality + //! Must be enabled via the `extern_item_impls` opt-in backend, as this functionality //! is currently limited to nightly. //! //! # Examples //! //! ```rust //! # use core::mem::MaybeUninit; - //! # #[cfg(getrandom_extern_item_impls)] + //! # #[cfg(getrandom_backend = "extern_item_impls")] //! #[getrandom::implementation::fill_uninit] //! fn my_fill_uninit_implementation( //! dest: &mut [MaybeUninit] @@ -75,7 +75,7 @@ pub mod implementation { //! } //! ``` - pub use crate::backends::{fill_uninit, u32, u64}; + pub use crate::backends::extern_item_impls::{fill_uninit, u32, u64}; } /// Fill `dest` with random bytes from the system's preferred random number source. From d09a65215d0aac8992fbeb6ec61a8173a1b03fc1 Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Tue, 27 Jan 2026 08:25:57 +1100 Subject: [PATCH 06/12] Update README.md --- README.md | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index bc62faa9..cfa1a4bc 100644 --- a/README.md +++ b/README.md @@ -99,16 +99,17 @@ or `js-sys` on "unknown" WASM targets then it's acceptable to enable this featur `getrandom` also provides optional (opt-in) backends, which allow users to customize the source 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_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 -| `efi_rng` | UEFI | `*-unknown‑uefi` | [`EFI_RNG_PROTOCOL`] with `EFI_RNG_ALGORITHM_RAW` (requires `std` and Nightly compiler) -| `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]) +| 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_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 +| `efi_rng` | UEFI | `*-unknown‑uefi` | [`EFI_RNG_PROTOCOL`] with `EFI_RNG_ALGORITHM_RAW` (requires `std` and Nightly compiler) +| `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]) 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`]: @@ -201,11 +202,11 @@ unsafe extern "Rust" fn __getrandom_v03_custom( } ``` -### Externally Implemented Interface (Nightly) +### 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 `gextern_item_impls` +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. @@ -223,6 +224,9 @@ fn my_fill_uninit_implementation( } ``` +For further details on what a suitable implementation for `fill_uninit` may look +like, see [custom backend]. + `getrandom` will provide a default implementation for `u32` and `u64`, but does not currently provide a default for `fill_uninit`, even if one is normally available for the current target. If no implementation is available, @@ -401,6 +405,7 @@ dual licensed as above, without any additional terms or conditions. [WASI]: https://github.com/WebAssembly/WASI [Emscripten]: https://emscripten.org [opt-in]: #opt-in-backends +[externally implemented interface]: #externally-implemented-interface [//]: # (licenses) From d6f5ea7cd22dcaebfbf48d94daece045af84f86c Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Tue, 27 Jan 2026 09:57:32 +1100 Subject: [PATCH 07/12] Remove redundant cfg_attr --- src/backends/extern_item_impls.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/backends/extern_item_impls.rs b/src/backends/extern_item_impls.rs index cf894d81..f08d4fd0 100644 --- a/src/backends/extern_item_impls.rs +++ b/src/backends/extern_item_impls.rs @@ -3,17 +3,17 @@ use crate::Error; use core::mem::MaybeUninit; /// Declares this function as an external implementation of [`fill_uninit`](crate::fill_uninit). -#[cfg_attr(getrandom_backend = "extern_item_impls", eii(fill_uninit))] +#[eii(fill_uninit)] pub(crate) fn fill_inner(dest: &mut [MaybeUninit]) -> Result<(), Error>; /// Declares this function as an external implementation of [`u32`](crate::u32). -#[cfg_attr(getrandom_backend = "extern_item_impls", eii(u32))] +#[eii(u32)] pub(crate) fn inner_u32() -> Result { crate::util::inner_u32() } /// Declares this function as an external implementation of [`u64`](crate::u64). -#[cfg_attr(getrandom_backend = "extern_item_impls", eii(u64))] +#[eii(u64)] pub(crate) fn inner_u64() -> Result { crate::util::inner_u64() } From 37a1ab8284a395e8844080ab59b20c6a2c9ace52 Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Tue, 27 Jan 2026 09:57:42 +1100 Subject: [PATCH 08/12] Move implementation docs --- src/lib.rs | 43 +++++++++++++++++++++---------------------- 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 11b21d59..f679f603 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -35,30 +35,29 @@ pub use sys_rng::SysRng; pub use crate::error::{Error, RawOsError}; +/// Provides Externally Implementable Interfaces for 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 +/// is currently limited to nightly. +/// +/// # Examples +/// +/// ```rust +/// # use core::mem::MaybeUninit; +/// # #[cfg(getrandom_backend = "extern_item_impls")] +/// #[getrandom::implementation::fill_uninit] +/// fn my_fill_uninit_implementation( +/// dest: &mut [MaybeUninit] +/// ) -> Result<(), getrandom::Error> { +/// // ... +/// # let _ = dest; +/// # Err(Error::UNSUPPORTED) +/// } +/// ``` #[cfg(getrandom_backend = "extern_item_impls")] pub mod implementation { - //! Provides Externally Implementable Interfaces for 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 - //! is currently limited to nightly. - //! - //! # Examples - //! - //! ```rust - //! # use core::mem::MaybeUninit; - //! # #[cfg(getrandom_backend = "extern_item_impls")] - //! #[getrandom::implementation::fill_uninit] - //! fn my_fill_uninit_implementation( - //! dest: &mut [MaybeUninit] - //! ) -> Result<(), getrandom::Error> { - //! // ... - //! # let _ = dest; - //! # Err(Error::UNSUPPORTED) - //! } - //! ``` - pub use crate::backends::extern_item_impls::{fill_uninit, u32, u64}; } From d20f31b9525d7f0cf40b4f2c6865627f3480c473 Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Tue, 27 Jan 2026 09:58:02 +1100 Subject: [PATCH 09/12] Add CI test for extern_item_impls --- .github/workflows/tests.yml | 4 ++++ tests/mod.rs | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 24465a1a..5edb14a1 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -77,6 +77,10 @@ jobs: RUSTFLAGS: -Dwarnings --cfg getrandom_backend="rdrand" RUSTDOCFLAGS: -Dwarnings --cfg getrandom_backend="rdrand" run: cargo test --features=std,sys_rng + - env: + RUSTFLAGS: -Dwarnings --cfg getrandom_backend="extern_item_impls" + RUSTDOCFLAGS: -Dwarnings --cfg getrandom_backend="extern_item_impls" + run: cargo test --target=${{ matrix.target }} --features=std --test mod extern_item_impls ios: name: iOS Simulator diff --git a/tests/mod.rs b/tests/mod.rs index 6033774c..29b25333 100644 --- a/tests/mod.rs +++ b/tests/mod.rs @@ -291,3 +291,36 @@ mod custom { assert!(res.is_err()); } } + +#[cfg(getrandom_backend = "extern_item_impls")] +mod extern_item_impls { + use core::mem::MaybeUninit; + use getrandom::Error; + + // This implementation for fill_uninit will always fail. + // + // WARNING: this custom implementation is for testing purposes ONLY! + + #[getrandom::implementation::fill_uninit] + fn my_fill_uninit_implementation(dest: &mut [MaybeUninit]) -> Result<(), Error> { + Err(Error::new_custom(4)) + } + + // This implementation returns a fixed value to demonstrate overriding defaults. + // + // WARNING: this custom implementation is for testing purposes ONLY! + + #[getrandom::implementation::u32] + fn my_u32_implementation() -> Result { + // Chosen by fair dice roll + Ok(4) + } + + // Test that enabling the custom feature indeed uses the custom implementation + #[test] + fn test_extern_item_impls() { + let mut buf = [0u8; 123]; + assert_eq!(getrandom::fill(&mut buf), Err(Error::new_custom(4))); + assert_eq!(getrandom::u32(), Ok(4)); + } +} From bf2e6914fa0dec733562c9cbcd2f47fd6bfb94e7 Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Tue, 27 Jan 2026 11:13:43 +1100 Subject: [PATCH 10/12] Ensure EII test runs on Nightly --- .github/workflows/tests.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 5edb14a1..51fc48f8 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -82,6 +82,20 @@ jobs: RUSTDOCFLAGS: -Dwarnings --cfg getrandom_backend="extern_item_impls" run: cargo test --target=${{ matrix.target }} --features=std --test mod extern_item_impls + extern_item_impls: + name: Extern Item Implementations + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v6 + - uses: dtolnay/rust-toolchain@master + with: + 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 + ios: name: iOS Simulator runs-on: macos-14 From 4d5f818cc9a341b6424a3053e325f60f43ad3a9a Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Tue, 27 Jan 2026 11:15:32 +1100 Subject: [PATCH 11/12] Clippy --- tests/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/mod.rs b/tests/mod.rs index 29b25333..3c6bf990 100644 --- a/tests/mod.rs +++ b/tests/mod.rs @@ -302,7 +302,7 @@ mod extern_item_impls { // WARNING: this custom implementation is for testing purposes ONLY! #[getrandom::implementation::fill_uninit] - fn my_fill_uninit_implementation(dest: &mut [MaybeUninit]) -> Result<(), Error> { + fn my_fill_uninit_implementation(_dest: &mut [MaybeUninit]) -> Result<(), Error> { Err(Error::new_custom(4)) } From ac60510dccef92b478ecad5bea3ebb5b10cbffc6 Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Tue, 27 Jan 2026 11:17:12 +1100 Subject: [PATCH 12/12] Remove redundant CI test --- .github/workflows/tests.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 51fc48f8..9c114d7b 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -77,10 +77,6 @@ jobs: RUSTFLAGS: -Dwarnings --cfg getrandom_backend="rdrand" RUSTDOCFLAGS: -Dwarnings --cfg getrandom_backend="rdrand" run: cargo test --features=std,sys_rng - - env: - RUSTFLAGS: -Dwarnings --cfg getrandom_backend="extern_item_impls" - RUSTDOCFLAGS: -Dwarnings --cfg getrandom_backend="extern_item_impls" - run: cargo test --target=${{ matrix.target }} --features=std --test mod extern_item_impls extern_item_impls: name: Extern Item Implementations