Upgrade getrandom to 0.2.12

This project was upgraded with external_updater.
Usage: tools/external_updater/updater.sh update external/rust/crates/getrandom
For more info, check https://cs.android.com/android/platform/superproject/+/main:tools/external_updater/README.md

Test: TreeHugger
Change-Id: I43a887168c674135090cee3b29d16158c2e03cf7
diff --git a/src/3ds.rs b/src/3ds.rs
index 6030512..a5aae77 100644
--- a/src/3ds.rs
+++ b/src/3ds.rs
@@ -1,16 +1,9 @@
-// Copyright 2021 Developers of the Rand project.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
 //! Implementation for Nintendo 3DS
 use crate::util_libc::sys_fill_exact;
 use crate::Error;
+use core::mem::MaybeUninit;
 
-pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
+pub fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
     sys_fill_exact(dest, |buf| unsafe {
         libc::getrandom(buf.as_mut_ptr() as *mut libc::c_void, buf.len(), 0)
     })
diff --git a/src/apple-other.rs b/src/apple-other.rs
new file mode 100644
index 0000000..167d8cf
--- /dev/null
+++ b/src/apple-other.rs
@@ -0,0 +1,24 @@
+//! Implementation for iOS, tvOS, and watchOS where `getentropy` is unavailable.
+use crate::Error;
+use core::{ffi::c_void, mem::MaybeUninit};
+
+// libsystem contains the libc of Darwin, and every binary ends up linked against it either way. This
+// makes it a more lightweight choice compared to `Security.framework`.
+extern "C" {
+    // This RNG uses a thread-local CSPRNG to provide data, which is seeded by the operating system's root CSPRNG.
+    // Its the best option after `getentropy` on modern Darwin-based platforms that also avoids the
+    // high startup costs and linking of Security.framework.
+    //
+    // While its just an implementation detail, `Security.framework` just calls into this anyway.
+    fn CCRandomGenerateBytes(bytes: *mut c_void, size: usize) -> i32;
+}
+
+pub fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
+    let ret = unsafe { CCRandomGenerateBytes(dest.as_mut_ptr() as *mut c_void, dest.len()) };
+    // kCCSuccess (from CommonCryptoError.h) is always zero.
+    if ret != 0 {
+        Err(Error::IOS_SEC_RANDOM)
+    } else {
+        Ok(())
+    }
+}
diff --git a/src/bsd_arandom.rs b/src/bsd_arandom.rs
index d441212..6e133d8 100644
--- a/src/bsd_arandom.rs
+++ b/src/bsd_arandom.rs
@@ -1,16 +1,11 @@
-// Copyright 2018 Developers of the Rand project.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
 //! Implementation for FreeBSD and NetBSD
-use crate::{util_libc::sys_fill_exact, Error};
-use core::ptr;
+use crate::{
+    util_libc::{sys_fill_exact, Weak},
+    Error,
+};
+use core::{mem::MaybeUninit, ptr};
 
-fn kern_arnd(buf: &mut [u8]) -> libc::ssize_t {
+fn kern_arnd(buf: &mut [MaybeUninit<u8>]) -> libc::ssize_t {
     static MIB: [libc::c_int; 2] = [libc::CTL_KERN, libc::KERN_ARND];
     let mut len = buf.len();
     let ret = unsafe {
@@ -30,20 +25,18 @@
     }
 }
 
-pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
-    // getrandom(2) was introduced in FreeBSD 12.0 and NetBSD 10.0
-    #[cfg(target_os = "freebsd")]
-    {
-        use crate::util_libc::Weak;
-        static GETRANDOM: Weak = unsafe { Weak::new("getrandom\0") };
-        type GetRandomFn =
-            unsafe extern "C" fn(*mut u8, libc::size_t, libc::c_uint) -> libc::ssize_t;
+type GetRandomFn = unsafe extern "C" fn(*mut u8, libc::size_t, libc::c_uint) -> libc::ssize_t;
 
-        if let Some(fptr) = GETRANDOM.ptr() {
-            let func: GetRandomFn = unsafe { core::mem::transmute(fptr) };
-            return sys_fill_exact(dest, |buf| unsafe { func(buf.as_mut_ptr(), buf.len(), 0) });
-        }
+pub fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
+    // getrandom(2) was introduced in FreeBSD 12.0 and NetBSD 10.0
+    static GETRANDOM: Weak = unsafe { Weak::new("getrandom\0") };
+    if let Some(fptr) = GETRANDOM.ptr() {
+        let func: GetRandomFn = unsafe { core::mem::transmute(fptr) };
+        return sys_fill_exact(dest, |buf| unsafe {
+            func(buf.as_mut_ptr() as *mut u8, buf.len(), 0)
+        });
     }
+
     // Both FreeBSD and NetBSD will only return up to 256 bytes at a time, and
     // older NetBSD kernels will fail on longer buffers.
     for chunk in dest.chunks_mut(256) {
diff --git a/src/custom.rs b/src/custom.rs
index 8432dfd..8dc9cb7 100644
--- a/src/custom.rs
+++ b/src/custom.rs
@@ -1,14 +1,6 @@
-// Copyright 2018 Developers of the Rand project.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
 //! An implementation which calls out to an externally defined function.
-use crate::Error;
-use core::num::NonZeroU32;
+use crate::{util::uninit_slice_fill_zero, Error};
+use core::{mem::MaybeUninit, num::NonZeroU32};
 
 /// Register a function to be invoked by `getrandom` on unsupported targets.
 ///
@@ -76,24 +68,36 @@
 #[cfg_attr(docsrs, doc(cfg(feature = "custom")))]
 macro_rules! register_custom_getrandom {
     ($path:path) => {
-        // We use an extern "C" function to get the guarantees of a stable ABI.
-        #[no_mangle]
-        extern "C" fn __getrandom_custom(dest: *mut u8, len: usize) -> u32 {
-            let f: fn(&mut [u8]) -> Result<(), $crate::Error> = $path;
-            let slice = unsafe { ::core::slice::from_raw_parts_mut(dest, len) };
-            match f(slice) {
-                Ok(()) => 0,
-                Err(e) => e.code().get(),
+        // TODO(MSRV 1.37): change to unnamed block
+        const __GETRANDOM_INTERNAL: () = {
+            // We use Rust ABI to be safe against potential panics in the passed function.
+            #[no_mangle]
+            unsafe fn __getrandom_custom(dest: *mut u8, len: usize) -> u32 {
+                // Make sure the passed function has the type of getrandom::getrandom
+                type F = fn(&mut [u8]) -> ::core::result::Result<(), $crate::Error>;
+                let _: F = $crate::getrandom;
+                let f: F = $path;
+                let slice = ::core::slice::from_raw_parts_mut(dest, len);
+                match f(slice) {
+                    Ok(()) => 0,
+                    Err(e) => e.code().get(),
+                }
             }
-        }
+        };
     };
 }
 
 #[allow(dead_code)]
-pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
-    extern "C" {
+pub fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
+    extern "Rust" {
         fn __getrandom_custom(dest: *mut u8, len: usize) -> u32;
     }
+    // Previously we always passed a valid, initialized slice to
+    // `__getrandom_custom`. Ensure `dest` has been initialized for backward
+    // compatibility with implementations that rely on that (e.g. Rust
+    // implementations that construct a `&mut [u8]` slice from `dest` and
+    // `len`).
+    let dest = uninit_slice_fill_zero(dest);
     let ret = unsafe { __getrandom_custom(dest.as_mut_ptr(), dest.len()) };
     match NonZeroU32::new(ret) {
         None => Ok(()),
diff --git a/src/dragonfly.rs b/src/dragonfly.rs
index 8daaa40..ac4794c 100644
--- a/src/dragonfly.rs
+++ b/src/dragonfly.rs
@@ -1,26 +1,21 @@
-// Copyright 2021 Developers of the Rand project.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
 //! Implementation for DragonFly BSD
 use crate::{
     use_file,
     util_libc::{sys_fill_exact, Weak},
     Error,
 };
+use core::mem::MaybeUninit;
 
-pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
+pub fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
     static GETRANDOM: Weak = unsafe { Weak::new("getrandom\0") };
     type GetRandomFn = unsafe extern "C" fn(*mut u8, libc::size_t, libc::c_uint) -> libc::ssize_t;
 
     // getrandom(2) was introduced in DragonflyBSD 5.7
     if let Some(fptr) = GETRANDOM.ptr() {
         let func: GetRandomFn = unsafe { core::mem::transmute(fptr) };
-        return sys_fill_exact(dest, |buf| unsafe { func(buf.as_mut_ptr(), buf.len(), 0) });
+        return sys_fill_exact(dest, |buf| unsafe {
+            func(buf.as_mut_ptr() as *mut u8, buf.len(), 0)
+        });
     } else {
         use_file::getrandom_inner(dest)
     }
diff --git a/src/emscripten.rs b/src/emscripten.rs
new file mode 100644
index 0000000..30221c6
--- /dev/null
+++ b/src/emscripten.rs
@@ -0,0 +1,13 @@
+//! Implementation for Emscripten
+use crate::{util_libc::last_os_error, Error};
+use core::mem::MaybeUninit;
+
+pub fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
+    // Emscripten 2.0.5 added getentropy, so we can use it unconditionally.
+    // Unlike other getentropy implementations, there is no max buffer length.
+    let ret = unsafe { libc::getentropy(dest.as_mut_ptr() as *mut libc::c_void, dest.len()) };
+    if ret < 0 {
+        return Err(last_os_error());
+    }
+    Ok(())
+}
diff --git a/src/error.rs b/src/error.rs
index ab39a3c..13c81c7 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -1,10 +1,3 @@
-// Copyright 2018 Developers of the Rand project.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
 use core::{fmt, num::NonZeroU32};
 
 /// A small and `no_std` compatible error type
@@ -35,7 +28,11 @@
     pub const UNSUPPORTED: Error = internal_error(0);
     /// The platform-specific `errno` returned a non-positive value.
     pub const ERRNO_NOT_POSITIVE: Error = internal_error(1);
-    /// Call to iOS [`SecRandomCopyBytes`](https://developer.apple.com/documentation/security/1399291-secrandomcopybytes) failed.
+    /// Encountered an unexpected situation which should not happen in practice.
+    pub const UNEXPECTED: Error = internal_error(2);
+    /// Call to [`CCRandomGenerateBytes`](https://opensource.apple.com/source/CommonCrypto/CommonCrypto-60074/include/CommonRandom.h.auto.html) failed
+    /// on iOS, tvOS, or waatchOS.
+    // TODO: Update this constant name in the next breaking release.
     pub const IOS_SEC_RANDOM: Error = internal_error(3);
     /// Call to Windows [`RtlGenRandom`](https://docs.microsoft.com/en-us/windows/win32/api/ntsecapi/nf-ntsecapi-rtlgenrandom) failed.
     pub const WINDOWS_RTL_GEN_RANDOM: Error = internal_error(4);
@@ -164,6 +161,7 @@
     match error {
         Error::UNSUPPORTED => Some("getrandom: this target is not supported"),
         Error::ERRNO_NOT_POSITIVE => Some("errno: did not return a positive value"),
+        Error::UNEXPECTED => Some("unexpected situation"),
         Error::IOS_SEC_RANDOM => Some("SecRandomCopyBytes: iOS Security framework failure"),
         Error::WINDOWS_RTL_GEN_RANDOM => Some("RtlGenRandom: Windows system function failure"),
         Error::FAILED_RDRAND => Some("RDRAND: failed multiple times: CPU issue likely"),
diff --git a/src/error_impls.rs b/src/error_impls.rs
index 61f46d2..a7bffb8 100644
--- a/src/error_impls.rs
+++ b/src/error_impls.rs
@@ -1,10 +1,3 @@
-// Copyright 2018 Developers of the Rand project.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
 #![cfg_attr(docsrs, doc(cfg(feature = "std")))]
 extern crate std;
 
diff --git a/src/espidf.rs b/src/espidf.rs
index dce8a2a..7da5ca8 100644
--- a/src/espidf.rs
+++ b/src/espidf.rs
@@ -1,20 +1,12 @@
-// Copyright 2021 Developers of the Rand project.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
 //! Implementation for ESP-IDF
 use crate::Error;
-use core::ffi::c_void;
+use core::{ffi::c_void, mem::MaybeUninit};
 
 extern "C" {
     fn esp_fill_random(buf: *mut c_void, len: usize) -> u32;
 }
 
-pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
+pub fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
     // Not that NOT enabling WiFi, BT, or the voltage noise entropy source (via `bootloader_random_enable`)
     // will cause ESP-IDF to return pseudo-random numbers based on the voltage noise entropy, after the initial boot process:
     // https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/random.html
diff --git a/src/fuchsia.rs b/src/fuchsia.rs
index 572ff53..1197068 100644
--- a/src/fuchsia.rs
+++ b/src/fuchsia.rs
@@ -1,20 +1,13 @@
-// Copyright 2018 Developers of the Rand project.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
 //! Implementation for Fuchsia Zircon
 use crate::Error;
+use core::mem::MaybeUninit;
 
 #[link(name = "zircon")]
 extern "C" {
     fn zx_cprng_draw(buffer: *mut u8, length: usize);
 }
 
-pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
-    unsafe { zx_cprng_draw(dest.as_mut_ptr(), dest.len()) }
+pub fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
+    unsafe { zx_cprng_draw(dest.as_mut_ptr() as *mut u8, dest.len()) }
     Ok(())
 }
diff --git a/src/hermit.rs b/src/hermit.rs
new file mode 100644
index 0000000..c4f6194
--- /dev/null
+++ b/src/hermit.rs
@@ -0,0 +1,29 @@
+//! Implementation for Hermit
+use crate::Error;
+use core::{mem::MaybeUninit, num::NonZeroU32};
+
+/// Minimum return value which we should get from syscalls in practice,
+/// because Hermit uses positive `i32`s for error codes:
+/// https://github.com/hermitcore/libhermit-rs/blob/main/src/errno.rs
+const MIN_RET_CODE: isize = -(i32::MAX as isize);
+
+extern "C" {
+    fn sys_read_entropy(buffer: *mut u8, length: usize, flags: u32) -> isize;
+}
+
+pub fn getrandom_inner(mut dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
+    while !dest.is_empty() {
+        let res = unsafe { sys_read_entropy(dest.as_mut_ptr() as *mut u8, dest.len(), 0) };
+        // Positive `isize`s can be safely casted to `usize`
+        if res > 0 && (res as usize) <= dest.len() {
+            dest = &mut dest[res as usize..];
+        } else {
+            let err = match res {
+                MIN_RET_CODE..=-1 => NonZeroU32::new(-res as u32).unwrap().into(),
+                _ => Error::UNEXPECTED,
+            };
+            return Err(err);
+        }
+    }
+    Ok(())
+}
diff --git a/src/hurd.rs b/src/hurd.rs
new file mode 100644
index 0000000..472a7d8
--- /dev/null
+++ b/src/hurd.rs
@@ -0,0 +1,10 @@
+//! Implementation for GNU/Hurd
+use crate::util_libc::sys_fill_exact;
+use crate::Error;
+use core::mem::MaybeUninit;
+
+pub fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
+    sys_fill_exact(dest, |buf| unsafe {
+        libc::getrandom(buf.as_mut_ptr() as *mut libc::c_void, buf.len(), 0)
+    })
+}
diff --git a/src/ios.rs b/src/ios.rs
deleted file mode 100644
index 226de16..0000000
--- a/src/ios.rs
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright 2018 Developers of the Rand project.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-//! Implementation for iOS
-use crate::Error;
-use core::{ffi::c_void, ptr::null};
-
-#[link(name = "Security", kind = "framework")]
-extern "C" {
-    fn SecRandomCopyBytes(rnd: *const c_void, count: usize, bytes: *mut u8) -> i32;
-}
-
-pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
-    // Apple's documentation guarantees kSecRandomDefault is a synonym for NULL.
-    let ret = unsafe { SecRandomCopyBytes(null(), dest.len(), dest.as_mut_ptr()) };
-    // errSecSuccess (from SecBase.h) is always zero.
-    if ret != 0 {
-        Err(Error::IOS_SEC_RANDOM)
-    } else {
-        Ok(())
-    }
-}
diff --git a/src/js.rs b/src/js.rs
index 574c4dc..e5428f5 100644
--- a/src/js.rs
+++ b/src/js.rs
@@ -1,14 +1,8 @@
-// Copyright 2018 Developers of the Rand project.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
+//! Implementation for WASM based on Web and Node.js
 use crate::Error;
 
 extern crate std;
-use std::thread_local;
+use std::{mem::MaybeUninit, thread_local};
 
 use js_sys::{global, Function, Uint8Array};
 use wasm_bindgen::{prelude::wasm_bindgen, JsCast, JsValue};
@@ -16,6 +10,8 @@
 // Size of our temporary Uint8Array buffer used with WebCrypto methods
 // Maximum is 65536 bytes see https://developer.mozilla.org/en-US/docs/Web/API/Crypto/getRandomValues
 const WEB_CRYPTO_BUFFER_SIZE: usize = 256;
+// Node.js's crypto.randomFillSync requires the size to be less than 2**31.
+const NODE_MAX_BUFFER_SIZE: usize = (1 << 31) - 1;
 
 enum RngSource {
     Node(NodeCrypto),
@@ -28,14 +24,27 @@
     static RNG_SOURCE: Result<RngSource, Error> = getrandom_init();
 );
 
-pub(crate) fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
+pub(crate) fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
     RNG_SOURCE.with(|result| {
         let source = result.as_ref().map_err(|&e| e)?;
 
         match source {
             RngSource::Node(n) => {
-                if n.random_fill_sync(dest).is_err() {
-                    return Err(Error::NODE_RANDOM_FILL_SYNC);
+                for chunk in dest.chunks_mut(NODE_MAX_BUFFER_SIZE) {
+                    // SAFETY: chunk is never used directly, the memory is only
+                    // modified via the Uint8Array view, which is passed
+                    // directly to JavaScript. Also, crypto.randomFillSync does
+                    // not resize the buffer. We know the length is less than
+                    // u32::MAX because of the chunking above.
+                    // Note that this uses the fact that JavaScript doesn't
+                    // have a notion of "uninitialized memory", this is purely
+                    // a Rust/C/C++ concept.
+                    let res = n.random_fill_sync(unsafe {
+                        Uint8Array::view_mut_raw(chunk.as_mut_ptr() as *mut u8, chunk.len())
+                    });
+                    if res.is_err() {
+                        return Err(Error::NODE_RANDOM_FILL_SYNC);
+                    }
                 }
             }
             RngSource::Web(crypto, buf) => {
@@ -49,7 +58,9 @@
                     if crypto.get_random_values(&sub_buf).is_err() {
                         return Err(Error::WEB_GET_RANDOM_VALUES);
                     }
-                    sub_buf.copy_to(chunk);
+
+                    // SAFETY: `sub_buf`'s length is the same length as `chunk`
+                    unsafe { sub_buf.raw_copy_to_ptr(chunk.as_mut_ptr() as *mut u8) };
                 }
             }
         };
@@ -120,7 +131,7 @@
     type NodeCrypto;
     // crypto.randomFillSync()
     #[wasm_bindgen(method, js_name = randomFillSync, catch)]
-    fn random_fill_sync(this: &NodeCrypto, buf: &mut [u8]) -> Result<(), JsValue>;
+    fn random_fill_sync(this: &NodeCrypto, buf: Uint8Array) -> Result<(), JsValue>;
 
     // Ideally, we would just use `fn require(s: &str)` here. However, doing
     // this causes a Webpack warning. So we instead return the function itself
diff --git a/src/lazy.rs b/src/lazy.rs
new file mode 100644
index 0000000..b9c2f88
--- /dev/null
+++ b/src/lazy.rs
@@ -0,0 +1,56 @@
+use core::sync::atomic::{AtomicUsize, Ordering::Relaxed};
+
+// This structure represents a lazily initialized static usize value. Useful
+// when it is preferable to just rerun initialization instead of locking.
+// Both unsync_init and sync_init will invoke an init() function until it
+// succeeds, then return the cached value for future calls.
+//
+// Both methods support init() "failing". If the init() method returns UNINIT,
+// that value will be returned as normal, but will not be cached.
+//
+// Users should only depend on the _value_ returned by init() functions.
+// Specifically, for the following init() function:
+//      fn init() -> usize {
+//          a();
+//          let v = b();
+//          c();
+//          v
+//      }
+// the effects of c() or writes to shared memory will not necessarily be
+// observed and additional synchronization methods with be needed.
+pub(crate) struct LazyUsize(AtomicUsize);
+
+impl LazyUsize {
+    pub const fn new() -> Self {
+        Self(AtomicUsize::new(Self::UNINIT))
+    }
+
+    // The initialization is not completed.
+    pub const UNINIT: usize = usize::max_value();
+
+    // Runs the init() function at least once, returning the value of some run
+    // of init(). Multiple callers can run their init() functions in parallel.
+    // init() should always return the same value, if it succeeds.
+    pub fn unsync_init(&self, init: impl FnOnce() -> usize) -> usize {
+        // Relaxed ordering is fine, as we only have a single atomic variable.
+        let mut val = self.0.load(Relaxed);
+        if val == Self::UNINIT {
+            val = init();
+            self.0.store(val, Relaxed);
+        }
+        val
+    }
+}
+
+// Identical to LazyUsize except with bool instead of usize.
+pub(crate) struct LazyBool(LazyUsize);
+
+impl LazyBool {
+    pub const fn new() -> Self {
+        Self(LazyUsize::new())
+    }
+
+    pub fn unsync_init(&self, init: impl FnOnce() -> bool) -> bool {
+        self.0.unsync_init(|| init() as usize) != 0
+    }
+}
diff --git a/src/lib.rs b/src/lib.rs
index 67325a3..e80ec6f 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,11 +1,3 @@
-// Copyright 2019 Developers of the Rand project.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
 //! Interface to the operating system's random number generator.
 //!
 //! # Supported targets
@@ -14,25 +6,29 @@
 //! | ----------------- | ------------------ | --------------
 //! | Linux, Android    | `*‑linux‑*`        | [`getrandom`][1] system call if available, otherwise [`/dev/urandom`][2] after successfully polling `/dev/random`
 //! | Windows           | `*‑windows‑*`      | [`BCryptGenRandom`]
-//! | macOS             | `*‑apple‑darwin`   | [`getentropy`][3] if available, otherwise [`/dev/random`][4] (identical to `/dev/urandom`)
-//! | iOS               | `*‑apple‑ios`      | [`SecRandomCopyBytes`]
+//! | macOS             | `*‑apple‑darwin`   | [`getentropy`][3]
+//! | iOS, tvOS, watchOS | `*‑apple‑ios`, `*-apple-tvos`, `*-apple-watchos` | [`CCRandomGenerateBytes`]
 //! | FreeBSD           | `*‑freebsd`        | [`getrandom`][5] if available, otherwise [`kern.arandom`][6]
 //! | OpenBSD           | `*‑openbsd`        | [`getentropy`][7]
-//! | NetBSD            | `*‑netbsd`         | [`kern.arandom`][8]
-//! | Dragonfly BSD     | `*‑dragonfly`      | [`getrandom`][9] if available, otherwise [`/dev/random`][10]
+//! | NetBSD            | `*‑netbsd`         | [`getrandom`][16] if available, otherwise [`kern.arandom`][8]
+//! | Dragonfly BSD     | `*‑dragonfly`      | [`getrandom`][9] if available, otherwise [`/dev/urandom`][10] (identical to `/dev/random`)
 //! | Solaris, illumos  | `*‑solaris`, `*‑illumos` | [`getrandom`][11] if available, otherwise [`/dev/random`][12]
 //! | Fuchsia OS        | `*‑fuchsia`        | [`cprng_draw`]
 //! | Redox             | `*‑redox`          | `/dev/urandom`
-//! | Haiku             | `*‑haiku`          | `/dev/random` (identical to `/dev/urandom`)
-//! | Hermit            | `x86_64-*-hermit`  | [`RDRAND`]
+//! | Haiku             | `*‑haiku`          | `/dev/urandom` (identical to `/dev/random`)
+//! | Hermit            | `*-hermit`         | [`sys_read_entropy`]
+//! | Hurd              | `*-hurd-*`         | [`getrandom`][17]
 //! | SGX               | `x86_64‑*‑sgx`     | [`RDRAND`]
 //! | VxWorks           | `*‑wrs‑vxworks‑*`  | `randABytes` after checking entropy pool initialization with `randSecure`
 //! | ESP-IDF           | `*‑espidf`         | [`esp_fill_random`]
-//! | Emscripten        | `*‑emscripten`     | `/dev/random` (identical to `/dev/urandom`)
+//! | Emscripten        | `*‑emscripten`     | [`getentropy`][13]
 //! | WASI              | `wasm32‑wasi`      | [`random_get`]
-//! | Web Browser and Node.js | `wasm32‑*‑unknown` | [`Crypto.getRandomValues`] if available, then [`crypto.randomFillSync`] if on Node.js, see [WebAssembly support]
+//! | Web Browser and Node.js | `wasm*‑*‑unknown` | [`Crypto.getRandomValues`] if available, then [`crypto.randomFillSync`] if on Node.js, see [WebAssembly support]
 //! | SOLID             | `*-kmc-solid_*`    | `SOLID_RNG_SampleRandomBytes`
 //! | Nintendo 3DS      | `armv6k-nintendo-3ds` | [`getrandom`][1]
+//! | PS Vita           | `armv7-sony-vita-newlibeabihf` | [`getentropy`][13]
+//! | QNX Neutrino      | `*‑nto-qnx*`          | [`/dev/urandom`][14] (identical to `/dev/random`)
+//! | AIX               | `*-ibm-aix`        | [`/dev/urandom`][15]
 //!
 //! There is no blanket implementation on `unix` targets that reads from
 //! `/dev/urandom`. This ensures all supported targets are using the recommended
@@ -102,6 +98,13 @@
 //! ```
 //! This crate will then use the provided `webcrypto` implementation.
 //!
+//! ### Platform Support
+//! This crate generally supports the same operating system and platform versions that the Rust standard library does.
+//! Additional targets may be supported using pluggable custom implementations.
+//!
+//! This means that as Rust drops support for old versions of operating systems (such as old Linux kernel versions, Android API levels, etc)
+//! in stable releases, `getrandom` may create new patch releases (`0.N.x`) that remove support for outdated platform versions.
+//!
 //! ### Custom implementations
 //!
 //! The [`register_custom_getrandom!`] macro allows a user to mark their own
@@ -150,7 +153,7 @@
 //! [1]: http://man7.org/linux/man-pages/man2/getrandom.2.html
 //! [2]: http://man7.org/linux/man-pages/man4/urandom.4.html
 //! [3]: https://www.unix.com/man-page/mojave/2/getentropy/
-//! [4]: https://www.unix.com/man-page/mojave/4/random/
+//! [4]: https://www.unix.com/man-page/mojave/4/urandom/
 //! [5]: https://www.freebsd.org/cgi/man.cgi?query=getrandom&manpath=FreeBSD+12.0-stable
 //! [6]: https://www.freebsd.org/cgi/man.cgi?query=random&sektion=4
 //! [7]: https://man.openbsd.org/getentropy.2
@@ -159,11 +162,16 @@
 //! [10]: https://leaf.dragonflybsd.org/cgi/web-man?command=random&section=4
 //! [11]: https://docs.oracle.com/cd/E88353_01/html/E37841/getrandom-2.html
 //! [12]: https://docs.oracle.com/cd/E86824_01/html/E54777/random-7d.html
+//! [13]: https://github.com/emscripten-core/emscripten/pull/12240
+//! [14]: https://www.qnx.com/developers/docs/7.1/index.html#com.qnx.doc.neutrino.utilities/topic/r/random.html
+//! [15]: https://www.ibm.com/docs/en/aix/7.3?topic=files-random-urandom-devices
+//! [16]: https://man.netbsd.org/getrandom.2
+//! [17]: https://www.gnu.org/software/libc/manual/html_mono/libc.html#index-getrandom
 //!
 //! [`BCryptGenRandom`]: https://docs.microsoft.com/en-us/windows/win32/api/bcrypt/nf-bcrypt-bcryptgenrandom
 //! [`Crypto.getRandomValues`]: https://www.w3.org/TR/WebCryptoAPI/#Crypto-method-getRandomValues
 //! [`RDRAND`]: https://software.intel.com/en-us/articles/intel-digital-random-number-generator-drng-software-implementation-guide
-//! [`SecRandomCopyBytes`]: https://developer.apple.com/documentation/security/1399291-secrandomcopybytes?language=objc
+//! [`CCRandomGenerateBytes`]: https://opensource.apple.com/source/CommonCrypto/CommonCrypto-60074/include/CommonRandom.h.auto.html
 //! [`cprng_draw`]: https://fuchsia.dev/fuchsia-src/zircon/syscalls/cprng_draw
 //! [`crypto.randomFillSync`]: https://nodejs.org/api/crypto.html#cryptorandomfillsyncbuffer-offset-size
 //! [`esp_fill_random`]: https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/random.html#_CPPv415esp_fill_randomPv6size_t
@@ -173,11 +181,12 @@
 //! [`module`]: https://rustwasm.github.io/wasm-bindgen/reference/attributes/on-js-imports/module.html
 //! [CommonJS modules]: https://nodejs.org/api/modules.html
 //! [ES modules]: https://nodejs.org/api/esm.html
+//! [`sys_read_entropy`]: https://github.com/hermit-os/kernel/blob/315f58ff5efc81d9bf0618af85a59963ff55f8b1/src/syscalls/entropy.rs#L47-L55
 
 #![doc(
     html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk.png",
     html_favicon_url = "https://www.rust-lang.org/favicon.ico",
-    html_root_url = "https://docs.rs/getrandom/0.2.8"
+    html_root_url = "https://docs.rs/getrandom/0.2.12"
 )]
 #![no_std]
 #![warn(rust_2018_idioms, unused_lifetimes, missing_docs)]
@@ -186,6 +195,9 @@
 #[macro_use]
 extern crate cfg_if;
 
+use crate::util::{slice_as_uninit_mut, slice_assume_init_mut};
+use core::mem::MaybeUninit;
+
 mod error;
 mod util;
 // To prevent a breaking change when targets are added, we always export the
@@ -199,15 +211,19 @@
 
 // System-specific implementations.
 //
-// These should all provide getrandom_inner with the same signature as getrandom.
+// These should all provide getrandom_inner with the signature
+// `fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error>`.
+// The function MUST fully initialize `dest` when `Ok(())` is returned.
+// The function MUST NOT ever write uninitialized bytes into `dest`,
+// regardless of what value it returns.
 cfg_if! {
-    if #[cfg(any(target_os = "emscripten", target_os = "haiku",
-                 target_os = "redox"))] {
+    if #[cfg(any(target_os = "haiku", target_os = "redox", target_os = "nto", target_os = "aix"))] {
         mod util_libc;
         #[path = "use_file.rs"] mod imp;
     } else if #[cfg(any(target_os = "android", target_os = "linux"))] {
         mod util_libc;
         mod use_file;
+        mod lazy;
         #[path = "linux_android.rs"] mod imp;
     } else if #[cfg(any(target_os = "illumos", target_os = "solaris"))] {
         mod util_libc;
@@ -222,19 +238,18 @@
         #[path = "dragonfly.rs"] mod imp;
     } else if #[cfg(target_os = "fuchsia")] {
         #[path = "fuchsia.rs"] mod imp;
-    } else if #[cfg(target_os = "ios")] {
-        #[path = "ios.rs"] mod imp;
+    } else if #[cfg(any(target_os = "ios", target_os = "watchos", target_os = "tvos"))] {
+        #[path = "apple-other.rs"] mod imp;
     } else if #[cfg(target_os = "macos")] {
         mod util_libc;
-        mod use_file;
         #[path = "macos.rs"] mod imp;
     } else if #[cfg(target_os = "openbsd")] {
         mod util_libc;
         #[path = "openbsd.rs"] mod imp;
-    } else if #[cfg(target_os = "wasi")] {
+    } else if #[cfg(all(target_arch = "wasm32", target_os = "wasi"))] {
         #[path = "wasi.rs"] mod imp;
-    } else if #[cfg(all(target_arch = "x86_64", target_os = "hermit"))] {
-        #[path = "rdrand.rs"] mod imp;
+    } else if #[cfg(target_os = "hermit")] {
+        #[path = "hermit.rs"] mod imp;
     } else if #[cfg(target_os = "vxworks")] {
         mod util_libc;
         #[path = "vxworks.rs"] mod imp;
@@ -244,23 +259,36 @@
         #[path = "espidf.rs"] mod imp;
     } else if #[cfg(windows)] {
         #[path = "windows.rs"] mod imp;
-    } else if #[cfg(all(target_arch = "x86_64", target_env = "sgx"))] {
-        #[path = "rdrand.rs"] mod imp;
-    } else if #[cfg(all(feature = "rdrand",
-                        any(target_arch = "x86_64", target_arch = "x86")))] {
-        #[path = "rdrand.rs"] mod imp;
-    } else if #[cfg(all(feature = "js",
-                        target_arch = "wasm32", target_os = "unknown"))] {
-        #[path = "js.rs"] mod imp;
     } else if #[cfg(all(target_os = "horizon", target_arch = "arm"))] {
         // We check for target_arch = "arm" because the Nintendo Switch also
         // uses Horizon OS (it is aarch64).
         mod util_libc;
         #[path = "3ds.rs"] mod imp;
+    } else if #[cfg(target_os = "vita")] {
+        mod util_libc;
+        #[path = "vita.rs"] mod imp;
+    } else if #[cfg(target_os = "emscripten")] {
+        mod util_libc;
+        #[path = "emscripten.rs"] mod imp;
+    } else if #[cfg(all(target_arch = "x86_64", target_env = "sgx"))] {
+        mod lazy;
+        #[path = "rdrand.rs"] mod imp;
+    } else if #[cfg(all(feature = "rdrand",
+                        any(target_arch = "x86_64", target_arch = "x86")))] {
+        mod lazy;
+        #[path = "rdrand.rs"] mod imp;
+    } else if #[cfg(all(feature = "js",
+                        any(target_arch = "wasm32", target_arch = "wasm64"),
+                        target_os = "unknown"))] {
+        #[path = "js.rs"] mod imp;
+    } else if #[cfg(target_os = "hurd")] {
+        mod util_libc;
+        #[path = "hurd.rs"] mod imp;
     } else if #[cfg(feature = "custom")] {
         use custom as imp;
-    } else if #[cfg(all(target_arch = "wasm32", target_os = "unknown"))] {
-        compile_error!("the wasm32-unknown-unknown target is not supported by \
+    } else if #[cfg(all(any(target_arch = "wasm32", target_arch = "wasm64"),
+                        target_os = "unknown"))] {
+        compile_error!("the wasm*-unknown-unknown targets are not supported by \
                         default, you may need to enable the \"js\" feature. \
                         For more information see: \
                         https://docs.rs/getrandom/#webassembly-support");
@@ -283,9 +311,42 @@
 /// In general, `getrandom` will be fast enough for interactive usage, though
 /// significantly slower than a user-space CSPRNG; for the latter consider
 /// [`rand::thread_rng`](https://docs.rs/rand/*/rand/fn.thread_rng.html).
+#[inline]
 pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> {
-    if dest.is_empty() {
-        return Ok(());
+    // SAFETY: The `&mut MaybeUninit<_>` reference doesn't escape, and
+    // `getrandom_uninit` guarantees it will never de-initialize any part of
+    // `dest`.
+    getrandom_uninit(unsafe { slice_as_uninit_mut(dest) })?;
+    Ok(())
+}
+
+/// Version of the `getrandom` function which fills `dest` with random bytes
+/// returns a mutable reference to those bytes.
+///
+/// On successful completion this function is guaranteed to return a slice
+/// which points to the same memory as `dest` and has the same length.
+/// In other words, it's safe to assume that `dest` is initialized after
+/// this function has returned `Ok`.
+///
+/// No part of `dest` will ever be de-initialized at any point, regardless
+/// of what is returned.
+///
+/// # Examples
+///
+/// ```ignore
+/// # // We ignore this test since `uninit_array` is unstable.
+/// #![feature(maybe_uninit_uninit_array)]
+/// # fn main() -> Result<(), getrandom::Error> {
+/// let mut buf = core::mem::MaybeUninit::uninit_array::<1024>();
+/// let buf: &mut [u8] = getrandom::getrandom_uninit(&mut buf)?;
+/// # Ok(()) }
+/// ```
+#[inline]
+pub fn getrandom_uninit(dest: &mut [MaybeUninit<u8>]) -> Result<&mut [u8], Error> {
+    if !dest.is_empty() {
+        imp::getrandom_inner(dest)?;
     }
-    imp::getrandom_inner(dest)
+    // SAFETY: `dest` has been fully initialized by `imp::getrandom_inner`
+    // since it returned `Ok`.
+    Ok(unsafe { slice_assume_init_mut(dest) })
 }
diff --git a/src/linux_android.rs b/src/linux_android.rs
index 4270b67..2517159 100644
--- a/src/linux_android.rs
+++ b/src/linux_android.rs
@@ -1,19 +1,12 @@
-// Copyright 2018 Developers of the Rand project.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
 //! Implementation for Linux / Android
 use crate::{
-    util::LazyBool,
+    lazy::LazyBool,
     util_libc::{last_os_error, sys_fill_exact},
     {use_file, Error},
 };
+use core::mem::MaybeUninit;
 
-pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
+pub fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
     // getrandom(2) was introduced in Linux 3.17
     static HAS_GETRANDOM: LazyBool = LazyBool::new();
     if HAS_GETRANDOM.unsync_init(is_getrandom_available) {
diff --git a/src/macos.rs b/src/macos.rs
index 671a053..44af76b 100644
--- a/src/macos.rs
+++ b/src/macos.rs
@@ -1,36 +1,18 @@
-// Copyright 2019 Developers of the Rand project.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
 //! Implementation for macOS
-use crate::{
-    use_file,
-    util_libc::{last_os_error, Weak},
-    Error,
-};
-use core::mem;
+use crate::{util_libc::last_os_error, Error};
+use core::mem::MaybeUninit;
 
-type GetEntropyFn = unsafe extern "C" fn(*mut u8, libc::size_t) -> libc::c_int;
+extern "C" {
+    // Supported as of macOS 10.12+.
+    fn getentropy(buf: *mut u8, size: libc::size_t) -> libc::c_int;
+}
 
-pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
-    // getentropy(2) was added in 10.12, Rust supports 10.7+
-    static GETENTROPY: Weak = unsafe { Weak::new("getentropy\0") };
-    if let Some(fptr) = GETENTROPY.ptr() {
-        let func: GetEntropyFn = unsafe { mem::transmute(fptr) };
-        for chunk in dest.chunks_mut(256) {
-            let ret = unsafe { func(chunk.as_mut_ptr(), chunk.len()) };
-            if ret != 0 {
-                return Err(last_os_error());
-            }
+pub fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
+    for chunk in dest.chunks_mut(256) {
+        let ret = unsafe { getentropy(chunk.as_mut_ptr() as *mut u8, chunk.len()) };
+        if ret != 0 {
+            return Err(last_os_error());
         }
-        Ok(())
-    } else {
-        // We fallback to reading from /dev/random instead of SecRandomCopyBytes
-        // to avoid high startup costs and linking the Security framework.
-        use_file::getrandom_inner(dest)
     }
+    Ok(())
 }
diff --git a/src/openbsd.rs b/src/openbsd.rs
index 4137173..f4d64da 100644
--- a/src/openbsd.rs
+++ b/src/openbsd.rs
@@ -1,15 +1,8 @@
-// Copyright 2018 Developers of the Rand project.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
 //! Implementation for OpenBSD
 use crate::{util_libc::last_os_error, Error};
+use core::mem::MaybeUninit;
 
-pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
+pub fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
     // getentropy(2) was added in OpenBSD 5.6, so we can use it unconditionally.
     for chunk in dest.chunks_mut(256) {
         let ret = unsafe { libc::getentropy(chunk.as_mut_ptr() as *mut libc::c_void, chunk.len()) };
diff --git a/src/rdrand.rs b/src/rdrand.rs
index 1df21e5..f527c8c 100644
--- a/src/rdrand.rs
+++ b/src/rdrand.rs
@@ -1,14 +1,6 @@
-// Copyright 2018 Developers of the Rand project.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-//! Implementation for SGX using RDRAND instruction
-use crate::Error;
-use core::mem;
+//! RDRAND backend for x86(-64) targets
+use crate::{lazy::LazyBool, util::slice_as_uninit, Error};
+use core::mem::{size_of, MaybeUninit};
 
 cfg_if! {
     if #[cfg(target_arch = "x86_64")] {
@@ -24,74 +16,106 @@
 // Implementation Guide" - Section 5.2.1 and "Intel® 64 and IA-32 Architectures
 // Software Developer’s Manual" - Volume 1 - Section 7.3.17.1.
 const RETRY_LIMIT: usize = 10;
-const WORD_SIZE: usize = mem::size_of::<usize>();
 
 #[target_feature(enable = "rdrand")]
-unsafe fn rdrand() -> Result<[u8; WORD_SIZE], Error> {
+unsafe fn rdrand() -> Option<usize> {
     for _ in 0..RETRY_LIMIT {
-        let mut el = mem::zeroed();
-        if rdrand_step(&mut el) == 1 {
-            // AMD CPUs from families 14h to 16h (pre Ryzen) sometimes fail to
-            // set CF on bogus random data, so we check these values explicitly.
-            // See https://github.com/systemd/systemd/issues/11810#issuecomment-489727505
-            // We perform this check regardless of target to guard against
-            // any implementation that incorrectly fails to set CF.
-            if el != 0 && el != !0 {
-                return Ok(el.to_ne_bytes());
-            }
-            // Keep looping in case this was a false positive.
+        let mut val = 0;
+        if rdrand_step(&mut val) == 1 {
+            return Some(val as usize);
         }
     }
-    Err(Error::FAILED_RDRAND)
+    None
 }
 
-// "rdrand" target feature requires "+rdrnd" flag, see https://github.com/rust-lang/rust/issues/49653.
+// "rdrand" target feature requires "+rdrand" flag, see https://github.com/rust-lang/rust/issues/49653.
 #[cfg(all(target_env = "sgx", not(target_feature = "rdrand")))]
 compile_error!(
-    "SGX targets require 'rdrand' target feature. Enable by using -C target-feature=+rdrnd."
+    "SGX targets require 'rdrand' target feature. Enable by using -C target-feature=+rdrand."
 );
 
-#[cfg(target_feature = "rdrand")]
-fn is_rdrand_supported() -> bool {
-    true
+// Run a small self-test to make sure we aren't repeating values
+// Adapted from Linux's test in arch/x86/kernel/cpu/rdrand.c
+// Fails with probability < 2^(-90) on 32-bit systems
+#[target_feature(enable = "rdrand")]
+unsafe fn self_test() -> bool {
+    // On AMD, RDRAND returns 0xFF...FF on failure, count it as a collision.
+    let mut prev = !0; // TODO(MSRV 1.43): Move to usize::MAX
+    let mut fails = 0;
+    for _ in 0..8 {
+        match rdrand() {
+            Some(val) if val == prev => fails += 1,
+            Some(val) => prev = val,
+            None => return false,
+        };
+    }
+    fails <= 2
 }
 
-// TODO use is_x86_feature_detected!("rdrand") when that works in core. See:
-// https://github.com/rust-lang-nursery/stdsimd/issues/464
-#[cfg(not(target_feature = "rdrand"))]
-fn is_rdrand_supported() -> bool {
-    use crate::util::LazyBool;
+fn is_rdrand_good() -> bool {
+    #[cfg(not(target_feature = "rdrand"))]
+    {
+        // SAFETY: All Rust x86 targets are new enough to have CPUID, and we
+        // check that leaf 1 is supported before using it.
+        let cpuid0 = unsafe { arch::__cpuid(0) };
+        if cpuid0.eax < 1 {
+            return false;
+        }
+        let cpuid1 = unsafe { arch::__cpuid(1) };
 
-    // SAFETY: All Rust x86 targets are new enough to have CPUID, and if CPUID
-    // is supported, CPUID leaf 1 is always supported.
-    const FLAG: u32 = 1 << 30;
-    static HAS_RDRAND: LazyBool = LazyBool::new();
-    HAS_RDRAND.unsync_init(|| unsafe { (arch::__cpuid(1).ecx & FLAG) != 0 })
-}
+        let vendor_id = [
+            cpuid0.ebx.to_le_bytes(),
+            cpuid0.edx.to_le_bytes(),
+            cpuid0.ecx.to_le_bytes(),
+        ];
+        if vendor_id == [*b"Auth", *b"enti", *b"cAMD"] {
+            let mut family = (cpuid1.eax >> 8) & 0xF;
+            if family == 0xF {
+                family += (cpuid1.eax >> 20) & 0xFF;
+            }
+            // AMD CPUs families before 17h (Zen) sometimes fail to set CF when
+            // RDRAND fails after suspend. Don't use RDRAND on those families.
+            // See https://bugzilla.redhat.com/show_bug.cgi?id=1150286
+            if family < 0x17 {
+                return false;
+            }
+        }
 
-pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
-    if !is_rdrand_supported() {
-        return Err(Error::NO_RDRAND);
+        const RDRAND_FLAG: u32 = 1 << 30;
+        if cpuid1.ecx & RDRAND_FLAG == 0 {
+            return false;
+        }
     }
 
-    // SAFETY: After this point, rdrand is supported, so calling the rdrand
-    // functions is not undefined behavior.
-    unsafe { rdrand_exact(dest) }
+    // SAFETY: We have already checked that rdrand is available.
+    unsafe { self_test() }
 }
 
+pub fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
+    static RDRAND_GOOD: LazyBool = LazyBool::new();
+    if !RDRAND_GOOD.unsync_init(is_rdrand_good) {
+        return Err(Error::NO_RDRAND);
+    }
+    // SAFETY: After this point, we know rdrand is supported.
+    unsafe { rdrand_exact(dest) }.ok_or(Error::FAILED_RDRAND)
+}
+
+// TODO: make this function safe when we have feature(target_feature_11)
 #[target_feature(enable = "rdrand")]
-unsafe fn rdrand_exact(dest: &mut [u8]) -> Result<(), Error> {
+unsafe fn rdrand_exact(dest: &mut [MaybeUninit<u8>]) -> Option<()> {
     // We use chunks_exact_mut instead of chunks_mut as it allows almost all
     // calls to memcpy to be elided by the compiler.
-    let mut chunks = dest.chunks_exact_mut(WORD_SIZE);
+    let mut chunks = dest.chunks_exact_mut(size_of::<usize>());
     for chunk in chunks.by_ref() {
-        chunk.copy_from_slice(&rdrand()?);
+        let src = rdrand()?.to_ne_bytes();
+        chunk.copy_from_slice(slice_as_uninit(&src));
     }
 
     let tail = chunks.into_remainder();
     let n = tail.len();
     if n > 0 {
-        tail.copy_from_slice(&rdrand()?[..n]);
+        let src = rdrand()?.to_ne_bytes();
+        tail.copy_from_slice(slice_as_uninit(&src[..n]));
     }
-    Ok(())
+    Some(())
 }
diff --git a/src/solaris_illumos.rs b/src/solaris_illumos.rs
index cf3067d..fbc2394 100644
--- a/src/solaris_illumos.rs
+++ b/src/solaris_illumos.rs
@@ -1,19 +1,10 @@
-// Copyright 2018 Developers of the Rand project.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
 //! Implementation for the Solaris family
 //!
-//! Read from `/dev/random`, with chunks of limited size (256 bytes).
 //! `/dev/random` uses the Hash_DRBG with SHA512 algorithm from NIST SP 800-90A.
 //! `/dev/urandom` uses the FIPS 186-2 algorithm, which is considered less
-//! secure. We choose to read from `/dev/random`.
+//! secure. We choose to read from `/dev/random` (and use GRND_RANDOM).
 //!
-//! Since Solaris 11.3 and mid-2015 illumos, the `getrandom` syscall is available.
+//! Solaris 11.3 and late-2018 illumos added the getrandom(2) libc function.
 //! To make sure we can compile on both Solaris and its derivatives, as well as
 //! function, we check for the existence of getrandom(2) in libc by calling
 //! libc::dlsym.
@@ -22,23 +13,25 @@
     util_libc::{sys_fill_exact, Weak},
     Error,
 };
-use core::mem;
+use core::mem::{self, MaybeUninit};
 
-#[cfg(target_os = "illumos")]
-type GetRandomFn = unsafe extern "C" fn(*mut u8, libc::size_t, libc::c_uint) -> libc::ssize_t;
-#[cfg(target_os = "solaris")]
-type GetRandomFn = unsafe extern "C" fn(*mut u8, libc::size_t, libc::c_uint) -> libc::c_int;
+static GETRANDOM: Weak = unsafe { Weak::new("getrandom\0") };
+type GetRandomFn =
+    unsafe extern "C" fn(*mut libc::c_void, libc::size_t, libc::c_uint) -> libc::ssize_t;
 
-pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
-    // getrandom(2) was introduced in Solaris 11.3 for Illumos in 2015.
-    static GETRANDOM: Weak = unsafe { Weak::new("getrandom\0") };
+pub fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
     if let Some(fptr) = GETRANDOM.ptr() {
         let func: GetRandomFn = unsafe { mem::transmute(fptr) };
         // 256 bytes is the lowest common denominator across all the Solaris
         // derived platforms for atomically obtaining random data.
         for chunk in dest.chunks_mut(256) {
             sys_fill_exact(chunk, |buf| unsafe {
-                func(buf.as_mut_ptr(), buf.len(), 0) as libc::ssize_t
+                // A cast is needed for the flags as libc uses the wrong type.
+                func(
+                    buf.as_mut_ptr() as *mut libc::c_void,
+                    buf.len(),
+                    libc::GRND_RANDOM as libc::c_uint,
+                )
             })?
         }
         Ok(())
diff --git a/src/solid.rs b/src/solid.rs
index dc76aac..cae8caf 100644
--- a/src/solid.rs
+++ b/src/solid.rs
@@ -1,21 +1,13 @@
-// Copyright 2021 Developers of the Rand project.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
 //! Implementation for SOLID
 use crate::Error;
-use core::num::NonZeroU32;
+use core::{mem::MaybeUninit, num::NonZeroU32};
 
 extern "C" {
     pub fn SOLID_RNG_SampleRandomBytes(buffer: *mut u8, length: usize) -> i32;
 }
 
-pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
-    let ret = unsafe { SOLID_RNG_SampleRandomBytes(dest.as_mut_ptr(), dest.len()) };
+pub fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
+    let ret = unsafe { SOLID_RNG_SampleRandomBytes(dest.as_mut_ptr() as *mut u8, dest.len()) };
     if ret >= 0 {
         Ok(())
     } else {
diff --git a/src/use_file.rs b/src/use_file.rs
index 16c0216..333325b 100644
--- a/src/use_file.rs
+++ b/src/use_file.rs
@@ -1,57 +1,47 @@
-// Copyright 2018 Developers of the Rand project.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
 //! Implementations that just need to read from a file
 use crate::{
-    util::LazyUsize,
     util_libc::{open_readonly, sys_fill_exact},
     Error,
 };
 use core::{
     cell::UnsafeCell,
+    mem::MaybeUninit,
     sync::atomic::{AtomicUsize, Ordering::Relaxed},
 };
 
-#[cfg(any(
-    target_os = "dragonfly",
-    target_os = "emscripten",
-    target_os = "haiku",
-    target_os = "macos",
-    target_os = "solaris",
-    target_os = "illumos"
-))]
+// We prefer using /dev/urandom and only use /dev/random if the OS
+// documentation indicates that /dev/urandom is insecure.
+// On Solaris/Illumos, see src/solaris_illumos.rs
+// On Dragonfly, Haiku, and QNX Neutrino the devices are identical.
+#[cfg(any(target_os = "solaris", target_os = "illumos"))]
 const FILE_PATH: &str = "/dev/random\0";
-#[cfg(any(target_os = "android", target_os = "linux", target_os = "redox"))]
+#[cfg(any(
+    target_os = "aix",
+    target_os = "android",
+    target_os = "linux",
+    target_os = "redox",
+    target_os = "dragonfly",
+    target_os = "haiku",
+    target_os = "nto",
+))]
 const FILE_PATH: &str = "/dev/urandom\0";
+const FD_UNINIT: usize = usize::max_value();
 
-pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
+pub fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
     let fd = get_rng_fd()?;
-    let read = |buf: &mut [u8]| unsafe { libc::read(fd, buf.as_mut_ptr() as *mut _, buf.len()) };
-
-    if cfg!(target_os = "emscripten") {
-        // `Crypto.getRandomValues` documents `dest` should be at most 65536 bytes.
-        for chunk in dest.chunks_mut(65536) {
-            sys_fill_exact(chunk, read)?;
-        }
-    } else {
-        sys_fill_exact(dest, read)?;
-    }
-    Ok(())
+    sys_fill_exact(dest, |buf| unsafe {
+        libc::read(fd, buf.as_mut_ptr() as *mut libc::c_void, buf.len())
+    })
 }
 
 // Returns the file descriptor for the device file used to retrieve random
 // bytes. The file will be opened exactly once. All subsequent calls will
 // return the same file descriptor. This file descriptor is never closed.
 fn get_rng_fd() -> Result<libc::c_int, Error> {
-    static FD: AtomicUsize = AtomicUsize::new(LazyUsize::UNINIT);
+    static FD: AtomicUsize = AtomicUsize::new(FD_UNINIT);
     fn get_fd() -> Option<libc::c_int> {
         match FD.load(Relaxed) {
-            LazyUsize::UNINIT => None,
+            FD_UNINIT => None,
             val => Some(val as libc::c_int),
         }
     }
@@ -76,8 +66,8 @@
     wait_until_rng_ready()?;
 
     let fd = unsafe { open_readonly(FILE_PATH)? };
-    // The fd always fits in a usize without conflicting with UNINIT.
-    debug_assert!(fd >= 0 && (fd as usize) < LazyUsize::UNINIT);
+    // The fd always fits in a usize without conflicting with FD_UNINIT.
+    debug_assert!(fd >= 0 && (fd as usize) < FD_UNINIT);
     FD.store(fd as usize, Relaxed);
 
     Ok(fd)
diff --git a/src/util.rs b/src/util.rs
index 06e23c2..1c4e70b 100644
--- a/src/util.rs
+++ b/src/util.rs
@@ -1,64 +1,35 @@
-// Copyright 2019 Developers of the Rand project.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
 #![allow(dead_code)]
-use core::sync::atomic::{AtomicUsize, Ordering::Relaxed};
+use core::{mem::MaybeUninit, ptr};
 
-// This structure represents a lazily initialized static usize value. Useful
-// when it is preferable to just rerun initialization instead of locking.
-// Both unsync_init and sync_init will invoke an init() function until it
-// succeeds, then return the cached value for future calls.
-//
-// Both methods support init() "failing". If the init() method returns UNINIT,
-// that value will be returned as normal, but will not be cached.
-//
-// Users should only depend on the _value_ returned by init() functions.
-// Specifically, for the following init() function:
-//      fn init() -> usize {
-//          a();
-//          let v = b();
-//          c();
-//          v
-//      }
-// the effects of c() or writes to shared memory will not necessarily be
-// observed and additional synchronization methods with be needed.
-pub struct LazyUsize(AtomicUsize);
-
-impl LazyUsize {
-    pub const fn new() -> Self {
-        Self(AtomicUsize::new(Self::UNINIT))
-    }
-
-    // The initialization is not completed.
-    pub const UNINIT: usize = usize::max_value();
-
-    // Runs the init() function at least once, returning the value of some run
-    // of init(). Multiple callers can run their init() functions in parallel.
-    // init() should always return the same value, if it succeeds.
-    pub fn unsync_init(&self, init: impl FnOnce() -> usize) -> usize {
-        // Relaxed ordering is fine, as we only have a single atomic variable.
-        let mut val = self.0.load(Relaxed);
-        if val == Self::UNINIT {
-            val = init();
-            self.0.store(val, Relaxed);
-        }
-        val
-    }
+/// Polyfill for `maybe_uninit_slice` feature's
+/// `MaybeUninit::slice_assume_init_mut`. Every element of `slice` must have
+/// been initialized.
+#[inline(always)]
+pub unsafe fn slice_assume_init_mut<T>(slice: &mut [MaybeUninit<T>]) -> &mut [T] {
+    // SAFETY: `MaybeUninit<T>` is guaranteed to be layout-compatible with `T`.
+    &mut *(slice as *mut [MaybeUninit<T>] as *mut [T])
 }
 
-// Identical to LazyUsize except with bool instead of usize.
-pub struct LazyBool(LazyUsize);
+#[inline]
+pub fn uninit_slice_fill_zero(slice: &mut [MaybeUninit<u8>]) -> &mut [u8] {
+    unsafe { ptr::write_bytes(slice.as_mut_ptr(), 0, slice.len()) };
+    unsafe { slice_assume_init_mut(slice) }
+}
 
-impl LazyBool {
-    pub const fn new() -> Self {
-        Self(LazyUsize::new())
-    }
+#[inline(always)]
+pub fn slice_as_uninit<T>(slice: &[T]) -> &[MaybeUninit<T>] {
+    // SAFETY: `MaybeUninit<T>` is guaranteed to be layout-compatible with `T`.
+    // There is no risk of writing a `MaybeUninit<T>` into the result since
+    // the result isn't mutable.
+    unsafe { &*(slice as *const [T] as *const [MaybeUninit<T>]) }
+}
 
-    pub fn unsync_init(&self, init: impl FnOnce() -> bool) -> bool {
-        self.0.unsync_init(|| init() as usize) != 0
-    }
+/// View an mutable initialized array as potentially-uninitialized.
+///
+/// This is unsafe because it allows assigning uninitialized values into
+/// `slice`, which would be undefined behavior.
+#[inline(always)]
+pub unsafe fn slice_as_uninit_mut<T>(slice: &mut [T]) -> &mut [MaybeUninit<T>] {
+    // SAFETY: `MaybeUninit<T>` is guaranteed to be layout-compatible with `T`.
+    &mut *(slice as *mut [T] as *mut [MaybeUninit<T>])
 }
diff --git a/src/util_libc.rs b/src/util_libc.rs
index d057071..0b792c3 100644
--- a/src/util_libc.rs
+++ b/src/util_libc.rs
@@ -1,13 +1,7 @@
-// Copyright 2019 Developers of the Rand project.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
 #![allow(dead_code)]
 use crate::Error;
 use core::{
+    mem::MaybeUninit,
     num::NonZeroU32,
     ptr::NonNull,
     sync::atomic::{fence, AtomicPtr, Ordering},
@@ -17,7 +11,7 @@
 cfg_if! {
     if #[cfg(any(target_os = "netbsd", target_os = "openbsd", target_os = "android"))] {
         use libc::__errno as errno_location;
-    } else if #[cfg(any(target_os = "linux", target_os = "emscripten", target_os = "redox"))] {
+    } else if #[cfg(any(target_os = "linux", target_os = "emscripten", target_os = "hurd", target_os = "redox"))] {
         use libc::__errno_location as errno_location;
     } else if #[cfg(any(target_os = "solaris", target_os = "illumos"))] {
         use libc::___errno as errno_location;
@@ -25,12 +19,16 @@
         use libc::__error as errno_location;
     } else if #[cfg(target_os = "haiku")] {
         use libc::_errnop as errno_location;
-    } else if #[cfg(all(target_os = "horizon", target_arch = "arm"))] {
+    } else if #[cfg(target_os = "nto")] {
+        use libc::__get_errno_ptr as errno_location;
+    } else if #[cfg(any(all(target_os = "horizon", target_arch = "arm"), target_os = "vita"))] {
         extern "C" {
             // Not provided by libc: https://github.com/rust-lang/libc/issues/1995
             fn __errno() -> *mut libc::c_int;
         }
         use __errno as errno_location;
+    } else if #[cfg(target_os = "aix")] {
+        use libc::_Errno as errno_location;
     }
 }
 
@@ -59,21 +57,24 @@
 //   - should return -1 and set errno on failure
 //   - should return the number of bytes written on success
 pub fn sys_fill_exact(
-    mut buf: &mut [u8],
-    sys_fill: impl Fn(&mut [u8]) -> libc::ssize_t,
+    mut buf: &mut [MaybeUninit<u8>],
+    sys_fill: impl Fn(&mut [MaybeUninit<u8>]) -> libc::ssize_t,
 ) -> Result<(), Error> {
     while !buf.is_empty() {
         let res = sys_fill(buf);
-        if res < 0 {
-            let err = last_os_error();
-            // We should try again if the call was interrupted.
-            if err.raw_os_error() != Some(libc::EINTR) {
-                return Err(err);
+        match res {
+            res if res > 0 => buf = buf.get_mut(res as usize..).ok_or(Error::UNEXPECTED)?,
+            -1 => {
+                let err = last_os_error();
+                // We should try again if the call was interrupted.
+                if err.raw_os_error() != Some(libc::EINTR) {
+                    return Err(err);
+                }
             }
-        } else {
-            // We don't check for EOF (ret = 0) as the data we are reading
+            // Negative return codes not equal to -1 should be impossible.
+            // EOF (ret = 0) should be impossible, as the data we are reading
             // should be an infinite stream of random bytes.
-            buf = &mut buf[(res as usize)..];
+            _ => return Err(Error::UNEXPECTED),
         }
     }
     Ok(())
@@ -135,19 +136,11 @@
     }
 }
 
-cfg_if! {
-    if #[cfg(any(target_os = "linux", target_os = "emscripten"))] {
-        use libc::open64 as open;
-    } else {
-        use libc::open;
-    }
-}
-
 // SAFETY: path must be null terminated, FD must be manually closed.
 pub unsafe fn open_readonly(path: &str) -> Result<libc::c_int, Error> {
     debug_assert_eq!(path.as_bytes().last(), Some(&0));
     loop {
-        let fd = open(path.as_ptr() as *const _, libc::O_RDONLY | libc::O_CLOEXEC);
+        let fd = libc::open(path.as_ptr() as *const _, libc::O_RDONLY | libc::O_CLOEXEC);
         if fd >= 0 {
             return Ok(fd);
         }
diff --git a/src/vita.rs b/src/vita.rs
new file mode 100644
index 0000000..20a9878
--- /dev/null
+++ b/src/vita.rs
@@ -0,0 +1,13 @@
+//! Implementation for PS Vita
+use crate::{util_libc::last_os_error, Error};
+use core::mem::MaybeUninit;
+
+pub fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
+    for chunk in dest.chunks_mut(256) {
+        let ret = unsafe { libc::getentropy(chunk.as_mut_ptr() as *mut libc::c_void, chunk.len()) };
+        if ret == -1 {
+            return Err(last_os_error());
+        }
+    }
+    Ok(())
+}
diff --git a/src/vxworks.rs b/src/vxworks.rs
index 6cb5d52..7ca9d6b 100644
--- a/src/vxworks.rs
+++ b/src/vxworks.rs
@@ -1,16 +1,11 @@
-// Copyright 2018 Developers of the Rand project.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
 //! Implementation for VxWorks
 use crate::{util_libc::last_os_error, Error};
-use core::sync::atomic::{AtomicBool, Ordering::Relaxed};
+use core::{
+    mem::MaybeUninit,
+    sync::atomic::{AtomicBool, Ordering::Relaxed},
+};
 
-pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
+pub fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
     static RNG_INIT: AtomicBool = AtomicBool::new(false);
     while !RNG_INIT.load(Relaxed) {
         let ret = unsafe { libc::randSecure() };
@@ -25,7 +20,7 @@
 
     // Prevent overflow of i32
     for chunk in dest.chunks_mut(i32::max_value() as usize) {
-        let ret = unsafe { libc::randABytes(chunk.as_mut_ptr(), chunk.len() as i32) };
+        let ret = unsafe { libc::randABytes(chunk.as_mut_ptr() as *mut u8, chunk.len() as i32) };
         if ret != 0 {
             return Err(last_os_error());
         }
diff --git a/src/wasi.rs b/src/wasi.rs
index c512182..d6c8a91 100644
--- a/src/wasi.rs
+++ b/src/wasi.rs
@@ -1,19 +1,17 @@
-// Copyright 2018 Developers of the Rand project.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
 //! Implementation for WASI
 use crate::Error;
-use core::num::NonZeroU32;
-use wasi::wasi_snapshot_preview1::random_get;
+use core::{
+    mem::MaybeUninit,
+    num::{NonZeroU16, NonZeroU32},
+};
+use wasi::random_get;
 
-pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
-    match unsafe { random_get(dest.as_mut_ptr() as i32, dest.len() as i32) } {
-        0 => Ok(()),
-        err => Err(unsafe { NonZeroU32::new_unchecked(err as u32) }.into()),
-    }
+pub fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
+    unsafe { random_get(dest.as_mut_ptr() as *mut u8, dest.len()) }.map_err(|e| {
+        // The WASI errno will always be non-zero, but we check just in case.
+        match NonZeroU16::new(e.raw()) {
+            Some(r) => Error::from(NonZeroU32::from(r)),
+            None => Error::ERRNO_NOT_POSITIVE,
+        }
+    })
 }
diff --git a/src/windows.rs b/src/windows.rs
index 41dc37a..2d1c483 100644
--- a/src/windows.rs
+++ b/src/windows.rs
@@ -1,13 +1,6 @@
-// Copyright 2018 Developers of the Rand project.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
+//! Implementation for Windows
 use crate::Error;
-use core::{ffi::c_void, num::NonZeroU32, ptr};
+use core::{ffi::c_void, mem::MaybeUninit, num::NonZeroU32, ptr};
 
 const BCRYPT_USE_SYSTEM_PREFERRED_RNG: u32 = 0x00000002;
 
@@ -21,20 +14,37 @@
     ) -> u32;
 }
 
-pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
+// Forbidden when targetting UWP
+#[cfg(not(target_vendor = "uwp"))]
+#[link(name = "advapi32")]
+extern "system" {
+    #[link_name = "SystemFunction036"]
+    fn RtlGenRandom(RandomBuffer: *mut c_void, RandomBufferLength: u32) -> u8;
+}
+
+pub fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
     // Prevent overflow of u32
     for chunk in dest.chunks_mut(u32::max_value() as usize) {
         // BCryptGenRandom was introduced in Windows Vista
         let ret = unsafe {
             BCryptGenRandom(
                 ptr::null_mut(),
-                chunk.as_mut_ptr(),
+                chunk.as_mut_ptr() as *mut u8,
                 chunk.len() as u32,
                 BCRYPT_USE_SYSTEM_PREFERRED_RNG,
             )
         };
         // NTSTATUS codes use the two highest bits for severity status.
         if ret >> 30 == 0b11 {
+            // Failed. Try RtlGenRandom as a fallback.
+            #[cfg(not(target_vendor = "uwp"))]
+            {
+                let ret =
+                    unsafe { RtlGenRandom(chunk.as_mut_ptr() as *mut c_void, chunk.len() as u32) };
+                if ret != 0 {
+                    continue;
+                }
+            }
             // We zeroize the highest bit, so the error code will reside
             // inside the range designated for OS codes.
             let code = ret ^ (1 << 31);