Snap for 10447354 from 370648b9291c82bbfb0b3efed0b00fe8bae89257 to mainline-wifi-release

Change-Id: Id029c113e2c9c7d40d337f8a7ac6e2ab9295cf9c
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json
index 279a9af..fc6c877 100644
--- a/.cargo_vcs_info.json
+++ b/.cargo_vcs_info.json
@@ -1,6 +1,6 @@
 {
   "git": {
-    "sha1": "ffd22552daa7d21b77ec20c1623bb4789b02122a"
+    "sha1": "5c1bb00b74a2c72ba182171b93d1c4b9d30c10c4"
   },
   "path_in_vcs": ""
 }
\ No newline at end of file
diff --git a/Android.bp b/Android.bp
index 27336fc..a23a93d 100644
--- a/Android.bp
+++ b/Android.bp
@@ -42,7 +42,7 @@
     host_supported: true,
     crate_name: "getrandom",
     cargo_env_compat: true,
-    cargo_pkg_version: "0.2.5",
+    cargo_pkg_version: "0.2.8",
     srcs: ["src/lib.rs"],
     test_suites: ["general-tests"],
     auto_gen_config: true,
@@ -61,7 +61,7 @@
     name: "getrandom_test_defaults",
     crate_name: "getrandom",
     cargo_env_compat: true,
-    cargo_pkg_version: "0.2.5",
+    cargo_pkg_version: "0.2.8",
     test_suites: ["general-tests"],
     auto_gen_config: true,
     edition: "2018",
@@ -98,7 +98,7 @@
     host_supported: true,
     crate_name: "getrandom",
     cargo_env_compat: true,
-    cargo_pkg_version: "0.2.5",
+    cargo_pkg_version: "0.2.8",
     srcs: ["src/lib.rs"],
     edition: "2018",
     features: ["std"],
@@ -108,7 +108,8 @@
     ],
     apex_available: [
         "//apex_available:platform",
-        "com.android.virt",
+        "//apex_available:anyapex",
     ],
+    product_available: true,
     vendor_available: true,
 }
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 99b442b..8cf9a58 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,56 @@
 The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
 and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
+## [0.2.8] - 2022-10-20
+### Changed
+- The [Web Cryptography API] will now be preferred on `wasm32-unknown-unknown`
+  when using the `"js"` feature, even on Node.js [#284] [#295]
+
+### Added
+- Added benchmarks to track buffer initialization cost [#272]
+
+### Fixed
+- Use `$crate` in `register_custom_getrandom!` [#270]
+
+### Documentation
+- Add information about enabling `"js"` feature [#280]
+- Fix link to `wasm-bindgen` [#278]
+- Document the varied implementations for underlying randomness sources [#276]
+
+[Web Cryptography API]: https://developer.mozilla.org/en-US/docs/Web/API/Web_Crypto_API
+[#284]: https://github.com/rust-random/getrandom/pull/284
+[#295]: https://github.com/rust-random/getrandom/pull/295
+[#272]: https://github.com/rust-random/getrandom/pull/272
+[#270]: https://github.com/rust-random/getrandom/pull/270
+[#280]: https://github.com/rust-random/getrandom/pull/280
+[#278]: https://github.com/rust-random/getrandom/pull/278
+[#276]: https://github.com/rust-random/getrandom/pull/276
+
+## [0.2.7] - 2022-06-14
+### Changed
+- Update `wasi` dependency to `0.11` [#253]
+
+### Fixed
+- Use `AtomicPtr` instead of `AtomicUsize` for Strict Provenance compatibility. [#263]
+
+### Documentation
+- Add comments explaining use of fallback mechanisms [#257] [#260]
+
+[#263]: https://github.com/rust-random/getrandom/pull/263
+[#260]: https://github.com/rust-random/getrandom/pull/260
+[#253]: https://github.com/rust-random/getrandom/pull/253
+[#257]: https://github.com/rust-random/getrandom/pull/257
+
+## [0.2.6] - 2022-03-28
+### Added
+- Nintendo 3DS (`armv6k-nintendo-3ds`) support [#248]
+
+### Changed
+- Retry `open` when interrupted [#252]
+
+[#248]: https://github.com/rust-random/getrandom/pull/248
+[#252]: https://github.com/rust-random/getrandom/pull/252
+
 ## [0.2.5] - 2022-02-22
 ### Added
 - ESP-IDF targets (`*‑espidf`) support [#245]
@@ -281,6 +331,9 @@
 ## [0.0.0] - 2019-01-19
 Publish an empty template library.
 
+[0.2.8]: https://github.com/rust-random/getrandom/compare/v0.2.7...v0.2.8
+[0.2.7]: https://github.com/rust-random/getrandom/compare/v0.2.6...v0.2.7
+[0.2.6]: https://github.com/rust-random/getrandom/compare/v0.2.5...v0.2.6
 [0.2.5]: https://github.com/rust-random/getrandom/compare/v0.2.4...v0.2.5
 [0.2.4]: https://github.com/rust-random/getrandom/compare/v0.2.3...v0.2.4
 [0.2.3]: https://github.com/rust-random/getrandom/compare/v0.2.2...v0.2.3
diff --git a/Cargo.toml b/Cargo.toml
index b3d191b..f0252c9 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -12,11 +12,12 @@
 [package]
 edition = "2018"
 name = "getrandom"
-version = "0.2.5"
+version = "0.2.8"
 authors = ["The Rand Project Developers"]
 exclude = [".*"]
 description = "A small cross-platform library for retrieving random data from system source"
 documentation = "https://docs.rs/getrandom"
+readme = "README.md"
 categories = [
     "os",
     "no-std",
@@ -75,8 +76,8 @@
 version = "0.3.18"
 
 [target."cfg(target_os = \"wasi\")".dependencies.wasi]
-version = "0.10"
+version = "0.11"
 
 [target."cfg(unix)".dependencies.libc]
-version = "0.2.64"
+version = "0.2.120"
 default-features = false
diff --git a/Cargo.toml.orig b/Cargo.toml.orig
index eb51149..2024c8f 100644
--- a/Cargo.toml.orig
+++ b/Cargo.toml.orig
@@ -1,6 +1,6 @@
 [package]
 name = "getrandom"
-version = "0.2.5" # Also update html_root_url in lib.rs when bumping this
+version = "0.2.8" # Also update html_root_url in lib.rs when bumping this
 edition = "2018"
 authors = ["The Rand Project Developers"]
 license = "MIT OR Apache-2.0"
@@ -18,10 +18,10 @@
 core = { version = "1.0", optional = true, package = "rustc-std-workspace-core" }
 
 [target.'cfg(unix)'.dependencies]
-libc = { version = "0.2.64", default-features = false }
+libc = { version = "0.2.120", default-features = false }
 
 [target.'cfg(target_os = "wasi")'.dependencies]
-wasi = "0.10"
+wasi = "0.11"
 
 [target.'cfg(all(target_arch = "wasm32", target_os = "unknown"))'.dependencies]
 wasm-bindgen = { version = "0.2.62", default-features = false, optional = true }
diff --git a/METADATA b/METADATA
index f9af3d4..8c76529 100644
--- a/METADATA
+++ b/METADATA
@@ -1,3 +1,7 @@
+# This project was upgraded with external_updater.
+# Usage: tools/external_updater/updater.sh update rust/crates/getrandom
+# For more info, check https://cs.android.com/android/platform/superproject/+/master:tools/external_updater/README.md
+
 name: "getrandom"
 description: "A small cross-platform library for retrieving random data from system source"
 third_party {
@@ -7,13 +11,13 @@
   }
   url {
     type: ARCHIVE
-    value: "https://static.crates.io/crates/getrandom/getrandom-0.2.5.crate"
+    value: "https://static.crates.io/crates/getrandom/getrandom-0.2.8.crate"
   }
-  version: "0.2.5"
+  version: "0.2.8"
   license_type: NOTICE
   last_upgrade_date {
     year: 2022
-    month: 3
-    day: 1
+    month: 12
+    day: 12
   }
 }
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 42e04b4..68e252c 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -23,6 +23,15 @@
       "path": "external/rust/crates/crossbeam-utils"
     },
     {
+      "path": "external/rust/crates/flate2"
+    },
+    {
+      "path": "external/rust/crates/hashbrown"
+    },
+    {
+      "path": "external/rust/crates/hashlink"
+    },
+    {
       "path": "external/rust/crates/mio"
     },
     {
@@ -45,19 +54,49 @@
     },
     {
       "path": "external/rust/crates/tokio"
+    },
+    {
+      "path": "external/rust/crates/zerocopy"
+    },
+    {
+      "path": "external/uwb/src"
+    },
+    {
+      "path": "packages/modules/Virtualization/apkdmverity"
+    },
+    {
+      "path": "packages/modules/Virtualization/authfs"
+    },
+    {
+      "path": "packages/modules/Virtualization/avmd"
+    },
+    {
+      "path": "packages/modules/Virtualization/encryptedstore"
+    },
+    {
+      "path": "packages/modules/Virtualization/libs/devicemapper"
+    },
+    {
+      "path": "packages/modules/Virtualization/microdroid_manager"
+    },
+    {
+      "path": "packages/modules/Virtualization/virtualizationmanager"
+    },
+    {
+      "path": "packages/modules/Virtualization/vm"
+    },
+    {
+      "path": "packages/modules/Virtualization/zipfuse"
+    },
+    {
+      "path": "system/security/keystore2"
+    },
+    {
+      "path": "system/security/keystore2/legacykeystore"
     }
   ],
   "presubmit": [
     {
-      "name": "ZipFuseTest"
-    },
-    {
-      "name": "apkdmverity.test"
-    },
-    {
-      "name": "authfs_device_test_src_lib"
-    },
-    {
       "name": "getrandom_test_src_lib"
     },
     {
@@ -65,34 +104,10 @@
     },
     {
       "name": "getrandom_test_tests_rdrand"
-    },
-    {
-      "name": "keystore2_test"
-    },
-    {
-      "name": "keystore2_test_utils_test"
-    },
-    {
-      "name": "legacykeystore_test"
-    },
-    {
-      "name": "microdroid_manager_test"
-    },
-    {
-      "name": "virtualizationservice_device_test"
     }
   ],
   "presubmit-rust": [
     {
-      "name": "ZipFuseTest"
-    },
-    {
-      "name": "apkdmverity.test"
-    },
-    {
-      "name": "authfs_device_test_src_lib"
-    },
-    {
       "name": "getrandom_test_src_lib"
     },
     {
@@ -100,21 +115,6 @@
     },
     {
       "name": "getrandom_test_tests_rdrand"
-    },
-    {
-      "name": "keystore2_test"
-    },
-    {
-      "name": "keystore2_test_utils_test"
-    },
-    {
-      "name": "legacykeystore_test"
-    },
-    {
-      "name": "microdroid_manager_test"
-    },
-    {
-      "name": "virtualizationservice_device_test"
     }
   ]
 }
diff --git a/benches/mod.rs b/benches/mod.rs
index a93e720..11be47e 100644
--- a/benches/mod.rs
+++ b/benches/mod.rs
@@ -1,22 +1,94 @@
 #![feature(test)]
 extern crate test;
 
-#[bench]
-fn bench_64(b: &mut test::Bencher) {
-    let mut buf = [0u8; 64];
+use std::{
+    alloc::{alloc_zeroed, dealloc, Layout},
+    ptr::NonNull,
+};
+
+// AlignedBuffer is like a Box<[u8; N]> except that it is always N-byte aligned
+struct AlignedBuffer<const N: usize>(NonNull<[u8; N]>);
+
+impl<const N: usize> AlignedBuffer<N> {
+    fn layout() -> Layout {
+        Layout::from_size_align(N, N).unwrap()
+    }
+
+    fn new() -> Self {
+        let p = unsafe { alloc_zeroed(Self::layout()) } as *mut [u8; N];
+        Self(NonNull::new(p).unwrap())
+    }
+
+    fn buf(&mut self) -> &mut [u8; N] {
+        unsafe { self.0.as_mut() }
+    }
+}
+
+impl<const N: usize> Drop for AlignedBuffer<N> {
+    fn drop(&mut self) {
+        unsafe { dealloc(self.0.as_ptr() as *mut u8, Self::layout()) }
+    }
+}
+
+// Used to benchmark the throughput of getrandom in an optimal scenario.
+// The buffer is hot, and does not require initialization.
+#[inline(always)]
+fn bench<const N: usize>(b: &mut test::Bencher) {
+    let mut ab = AlignedBuffer::<N>::new();
+    let buf = ab.buf();
     b.iter(|| {
         getrandom::getrandom(&mut buf[..]).unwrap();
         test::black_box(&buf);
     });
-    b.bytes = buf.len() as u64;
+    b.bytes = N as u64;
+}
+
+// Used to benchmark the throughput of getrandom is a slightly less optimal
+// scenario. The buffer is still hot, but requires initialization.
+#[inline(always)]
+fn bench_with_init<const N: usize>(b: &mut test::Bencher) {
+    let mut ab = AlignedBuffer::<N>::new();
+    let buf = ab.buf();
+    b.iter(|| {
+        for byte in buf.iter_mut() {
+            *byte = 0;
+        }
+        getrandom::getrandom(&mut buf[..]).unwrap();
+        test::black_box(&buf);
+    });
+    b.bytes = N as u64;
+}
+
+// 32 bytes (256-bit) is the seed sized used for rand::thread_rng
+const SEED: usize = 32;
+// Common size of a page, 4 KiB
+const PAGE: usize = 4096;
+// Large buffer to get asymptotic performance, 2 MiB
+const LARGE: usize = 1 << 21;
+
+#[bench]
+fn bench_seed(b: &mut test::Bencher) {
+    bench::<SEED>(b);
+}
+#[bench]
+fn bench_seed_init(b: &mut test::Bencher) {
+    bench_with_init::<SEED>(b);
 }
 
 #[bench]
-fn bench_65536(b: &mut test::Bencher) {
-    let mut buf = [0u8; 65536];
-    b.iter(|| {
-        getrandom::getrandom(&mut buf[..]).unwrap();
-        test::black_box(&buf);
-    });
-    b.bytes = buf.len() as u64;
+fn bench_page(b: &mut test::Bencher) {
+    bench::<PAGE>(b);
+}
+#[bench]
+fn bench_page_init(b: &mut test::Bencher) {
+    bench_with_init::<PAGE>(b);
+}
+
+#[bench]
+fn bench_large(b: &mut test::Bencher) {
+    bench::<LARGE>(b);
+}
+#[bench]
+fn bench_large_init(b: &mut test::Bencher) {
+    bench_with_init::<LARGE>(b);
 }
diff --git a/cargo2android.json b/cargo2android.json
index 8945bb0..305a6eb 100644
--- a/cargo2android.json
+++ b/cargo2android.json
@@ -1,8 +1,4 @@
 {
-  "apex-available": [
-    "//apex_available:platform",
-    "com.android.virt"
-  ],
   "dependencies": true,
   "device": true,
   "features": "std",
diff --git a/src/3ds.rs b/src/3ds.rs
new file mode 100644
index 0000000..6030512
--- /dev/null
+++ b/src/3ds.rs
@@ -0,0 +1,17 @@
+// 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;
+
+pub fn getrandom_inner(dest: &mut [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/bsd_arandom.rs b/src/bsd_arandom.rs
index f26f260..d441212 100644
--- a/src/bsd_arandom.rs
+++ b/src/bsd_arandom.rs
@@ -31,6 +31,7 @@
 }
 
 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;
diff --git a/src/custom.rs b/src/custom.rs
index 6110b05..8432dfd 100644
--- a/src/custom.rs
+++ b/src/custom.rs
@@ -79,7 +79,7 @@
         // 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<(), ::getrandom::Error> = $path;
+            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,
diff --git a/src/dragonfly.rs b/src/dragonfly.rs
index f27e906..8daaa40 100644
--- a/src/dragonfly.rs
+++ b/src/dragonfly.rs
@@ -17,6 +17,7 @@
     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) });
diff --git a/src/error.rs b/src/error.rs
index 6615753..ab39a3c 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -43,16 +43,19 @@
     pub const FAILED_RDRAND: Error = internal_error(5);
     /// RDRAND instruction unsupported on this target.
     pub const NO_RDRAND: Error = internal_error(6);
-    /// The browser does not have support for `self.crypto`.
+    /// The environment does not support the Web Crypto API.
     pub const WEB_CRYPTO: Error = internal_error(7);
-    /// The browser does not have support for `crypto.getRandomValues`.
+    /// Calling Web Crypto API `crypto.getRandomValues` failed.
     pub const WEB_GET_RANDOM_VALUES: Error = internal_error(8);
     /// On VxWorks, call to `randSecure` failed (random number generator is not yet initialized).
     pub const VXWORKS_RAND_SECURE: Error = internal_error(11);
-    /// NodeJS does not have support for the `crypto` module.
+    /// Node.js does not have the `crypto` CommonJS module.
     pub const NODE_CRYPTO: Error = internal_error(12);
-    /// NodeJS does not have support for `crypto.randomFillSync`.
+    /// Calling Node.js function `crypto.randomFillSync` failed.
     pub const NODE_RANDOM_FILL_SYNC: Error = internal_error(13);
+    /// Called from an ES module on Node.js. This is unsupported, see:
+    /// <https://docs.rs/getrandom#nodejs-es-module-support>.
+    pub const NODE_ES_MODULE: Error = internal_error(14);
 
     /// Codes below this point represent OS Errors (i.e. positive i32 values).
     /// Codes at or above this point, but below [`Error::CUSTOM_START`] are
@@ -109,10 +112,6 @@
             let idx = buf.iter().position(|&b| b == 0).unwrap_or(n);
             core::str::from_utf8(&buf[..idx]).ok()
         }
-    } else if #[cfg(target_os = "wasi")] {
-        fn os_err(errno: i32, _buf: &mut [u8]) -> Option<wasi::Error> {
-            wasi::Error::from_raw_error(errno as _)
-        }
     } else {
         fn os_err(_errno: i32, _buf: &mut [u8]) -> Option<&str> {
             None
@@ -170,10 +169,11 @@
         Error::FAILED_RDRAND => Some("RDRAND: failed multiple times: CPU issue likely"),
         Error::NO_RDRAND => Some("RDRAND: instruction not supported"),
         Error::WEB_CRYPTO => Some("Web Crypto API is unavailable"),
-        Error::WEB_GET_RANDOM_VALUES => Some("Web API crypto.getRandomValues is unavailable"),
+        Error::WEB_GET_RANDOM_VALUES => Some("Calling Web API crypto.getRandomValues failed"),
         Error::VXWORKS_RAND_SECURE => Some("randSecure: VxWorks RNG module is not initialized"),
-        Error::NODE_CRYPTO => Some("Node.js crypto module is unavailable"),
-        Error::NODE_RANDOM_FILL_SYNC => Some("Node.js API crypto.randomFillSync is unavailable"),
+        Error::NODE_CRYPTO => Some("Node.js crypto CommonJS module is unavailable"),
+        Error::NODE_RANDOM_FILL_SYNC => Some("Calling Node.js API crypto.randomFillSync failed"),
+        Error::NODE_ES_MODULE => Some("Node.js ES modules are not directly supported, see https://docs.rs/getrandom#nodejs-es-module-support"),
         _ => None,
     }
 }
diff --git a/src/js.rs b/src/js.rs
index e910f2b..574c4dc 100644
--- a/src/js.rs
+++ b/src/js.rs
@@ -10,15 +10,16 @@
 extern crate std;
 use std::thread_local;
 
-use js_sys::{global, Uint8Array};
+use js_sys::{global, Function, Uint8Array};
 use wasm_bindgen::{prelude::wasm_bindgen, JsCast, JsValue};
 
+// 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 BROWSER_CRYPTO_BUFFER_SIZE: usize = 256;
+const WEB_CRYPTO_BUFFER_SIZE: usize = 256;
 
 enum RngSource {
     Node(NodeCrypto),
-    Browser(BrowserCrypto, Uint8Array),
+    Web(WebCrypto, Uint8Array),
 }
 
 // JsValues are always per-thread, so we initialize RngSource for each thread.
@@ -37,10 +38,10 @@
                     return Err(Error::NODE_RANDOM_FILL_SYNC);
                 }
             }
-            RngSource::Browser(crypto, buf) => {
+            RngSource::Web(crypto, buf) => {
                 // getRandomValues does not work with all types of WASM memory,
                 // so we initially write to browser memory to avoid exceptions.
-                for chunk in dest.chunks_mut(BROWSER_CRYPTO_BUFFER_SIZE) {
+                for chunk in dest.chunks_mut(WEB_CRYPTO_BUFFER_SIZE) {
                     // The chunk can be smaller than buf's length, so we call to
                     // JS to create a smaller view of buf without allocation.
                     let sub_buf = buf.subarray(0, chunk.len() as u32);
@@ -58,25 +59,33 @@
 
 fn getrandom_init() -> Result<RngSource, Error> {
     let global: Global = global().unchecked_into();
-    if is_node(&global) {
-        let crypto = NODE_MODULE
-            .require("crypto")
-            .map_err(|_| Error::NODE_CRYPTO)?;
-        return Ok(RngSource::Node(crypto));
-    }
 
-    // Assume we are in some Web environment (browser or web worker). We get
-    // `self.crypto` (called `msCrypto` on IE), so we can call
-    // `crypto.getRandomValues`. If `crypto` isn't defined, we assume that
-    // we are in an older web browser and the OS RNG isn't available.
-    let crypto = match (global.crypto(), global.ms_crypto()) {
-        (c, _) if c.is_object() => c,
-        (_, c) if c.is_object() => c,
-        _ => return Err(Error::WEB_CRYPTO),
+    // Get the Web Crypto interface if we are in a browser, Web Worker, Deno,
+    // or another environment that supports the Web Cryptography API. This
+    // also allows for user-provided polyfills in unsupported environments.
+    let crypto = match global.crypto() {
+        // Standard Web Crypto interface
+        c if c.is_object() => c,
+        // Node.js CommonJS Crypto module
+        _ if is_node(&global) => {
+            // If module.require isn't a valid function, we are in an ES module.
+            match Module::require_fn().and_then(JsCast::dyn_into::<Function>) {
+                Ok(require_fn) => match require_fn.call1(&global, &JsValue::from_str("crypto")) {
+                    Ok(n) => return Ok(RngSource::Node(n.unchecked_into())),
+                    Err(_) => return Err(Error::NODE_CRYPTO),
+                },
+                Err(_) => return Err(Error::NODE_ES_MODULE),
+            }
+        }
+        // IE 11 Workaround
+        _ => match global.ms_crypto() {
+            c if c.is_object() => c,
+            _ => return Err(Error::WEB_CRYPTO),
+        },
     };
 
-    let buf = Uint8Array::new_with_length(BROWSER_CRYPTO_BUFFER_SIZE as u32);
-    Ok(RngSource::Browser(crypto, buf))
+    let buf = Uint8Array::new_with_length(WEB_CRYPTO_BUFFER_SIZE as u32);
+    Ok(RngSource::Web(crypto, buf))
 }
 
 // Taken from https://www.npmjs.com/package/browser-or-node
@@ -93,30 +102,36 @@
 
 #[wasm_bindgen]
 extern "C" {
-    type Global; // Return type of js_sys::global()
+    // Return type of js_sys::global()
+    type Global;
 
-    // Web Crypto API (https://www.w3.org/TR/WebCryptoAPI/)
-    #[wasm_bindgen(method, getter, js_name = "msCrypto")]
-    fn ms_crypto(this: &Global) -> BrowserCrypto;
+    // Web Crypto API: Crypto interface (https://www.w3.org/TR/WebCryptoAPI/)
+    type WebCrypto;
+    // Getters for the WebCrypto API
     #[wasm_bindgen(method, getter)]
-    fn crypto(this: &Global) -> BrowserCrypto;
-    type BrowserCrypto;
+    fn crypto(this: &Global) -> WebCrypto;
+    #[wasm_bindgen(method, getter, js_name = msCrypto)]
+    fn ms_crypto(this: &Global) -> WebCrypto;
+    // Crypto.getRandomValues()
     #[wasm_bindgen(method, js_name = getRandomValues, catch)]
-    fn get_random_values(this: &BrowserCrypto, buf: &Uint8Array) -> Result<(), JsValue>;
+    fn get_random_values(this: &WebCrypto, buf: &Uint8Array) -> Result<(), JsValue>;
 
-    // We use a "module" object here instead of just annotating require() with
-    // js_name = "module.require", so that Webpack doesn't give a warning. See:
-    //   https://github.com/rust-random/getrandom/issues/224
-    type NodeModule;
-    #[wasm_bindgen(js_name = module)]
-    static NODE_MODULE: NodeModule;
     // Node JS crypto module (https://nodejs.org/api/crypto.html)
-    #[wasm_bindgen(method, catch)]
-    fn require(this: &NodeModule, s: &str) -> Result<NodeCrypto, JsValue>;
     type NodeCrypto;
+    // crypto.randomFillSync()
     #[wasm_bindgen(method, js_name = randomFillSync, catch)]
     fn random_fill_sync(this: &NodeCrypto, buf: &mut [u8]) -> 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
+    // and manually invoke it using call1. This also lets us to check that the
+    // function actually exists, allowing for better error messages. See:
+    //   https://github.com/rust-random/getrandom/issues/224
+    //   https://github.com/rust-random/getrandom/issues/256
+    type Module;
+    #[wasm_bindgen(getter, static_method_of = Module, js_class = module, js_name = require, catch)]
+    fn require_fn() -> Result<JsValue, JsValue>;
+
     // Node JS process Object (https://nodejs.org/api/process.html)
     #[wasm_bindgen(method, getter)]
     fn process(this: &Global) -> Process;
diff --git a/src/lib.rs b/src/lib.rs
index 175da47..67325a3 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -30,9 +30,9 @@
 //! | ESP-IDF           | `*‑espidf`         | [`esp_fill_random`]
 //! | Emscripten        | `*‑emscripten`     | `/dev/random` (identical to `/dev/urandom`)
 //! | WASI              | `wasm32‑wasi`      | [`random_get`]
-//! | Web Browser       | `wasm32‑*‑unknown` | [`Crypto.getRandomValues`], see [WebAssembly support]
-//! | Node.js           | `wasm32‑*‑unknown` | [`crypto.randomBytes`], see [WebAssembly support]
+//! | Web Browser and Node.js | `wasm32‑*‑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]
 //!
 //! There is no blanket implementation on `unix` targets that reads from
 //! `/dev/urandom`. This ensures all supported targets are using the recommended
@@ -71,11 +71,37 @@
 //! that you are building for an environment containing JavaScript, and will
 //! call the appropriate methods. Both web browser (main window and Web Workers)
 //! and Node.js environments are supported, invoking the methods
-//! [described above](#supported-targets) using the
-//! [wasm-bindgen](https://github.com/rust-lang/rust-bindgen) toolchain.
+//! [described above](#supported-targets) using the [`wasm-bindgen`] toolchain.
+//!
+//! To enable the `js` Cargo feature, add the following to the `dependencies`
+//! section in your `Cargo.toml` file:
+//! ```toml
+//! [dependencies]
+//! getrandom = { version = "0.2", features = ["js"] }
+//! ```
+//!
+//! This can be done even if `getrandom` is not a direct dependency. Cargo
+//! allows crates to enable features for indirect dependencies.
+//!
+//! This feature should only be enabled for binary, test, or benchmark crates.
+//! Library crates should generally not enable this feature, leaving such a
+//! decision to *users* of their library. Also, libraries should not introduce
+//! their own `js` features *just* to enable `getrandom`'s `js` feature.
 //!
 //! This feature has no effect on targets other than `wasm32-unknown-unknown`.
 //!
+//! #### Node.js ES module support
+//!
+//! Node.js supports both [CommonJS modules] and [ES modules]. Due to
+//! limitations in wasm-bindgen's [`module`] support, we cannot directly
+//! support ES Modules running on Node.js. However, on Node v15 and later, the
+//! module author can add a simple shim to support the Web Cryptography API:
+//! ```js
+//! import { webcrypto } from 'node:crypto'
+//! globalThis.crypto = webcrypto
+//! ```
+//! This crate will then use the provided `webcrypto` implementation.
+//!
 //! ### Custom implementations
 //!
 //! The [`register_custom_getrandom!`] macro allows a user to mark their own
@@ -88,16 +114,6 @@
 //! using `rdrand` and `js` Cargo features) continue using their normal
 //! implementations even if a function is registered.
 //!
-//! ### Indirect Dependencies
-//!
-//! If `getrandom` is not a direct dependency of your crate, you can still
-//! enable any of the above fallback behaviors by enabling the relevant
-//! feature in your root crate's `Cargo.toml`:
-//! ```toml
-//! [dependencies]
-//! getrandom = { version = "0.2", features = ["js"] }
-//! ```
-//!
 //! ## Early boot
 //!
 //! Sometimes, early in the boot process, the OS has not collected enough
@@ -114,13 +130,22 @@
 //! entropy yet. To avoid returning low-entropy bytes, we first poll
 //! `/dev/random` and only switch to `/dev/urandom` once this has succeeded.
 //!
+//! On OpenBSD, this kind of entropy accounting isn't available, and on
+//! NetBSD, blocking on it is discouraged. On these platforms, nonblocking
+//! interfaces are used, even when reliable entropy may not be available.
+//! On the platforms where it is used, the reliability of entropy accounting
+//! itself isn't free from controversy. This library provides randomness
+//! sourced according to the platform's best practices, but each platform has
+//! its own limits on the grade of randomness it can promise in environments
+//! with few sources of entropy.
+//!
 //! ## Error handling
 //!
-//! We always choose failure over returning insecure "random" bytes. In general,
-//! on supported platforms, failure is highly unlikely, though not impossible.
-//! If an error does occur, then it is likely that it will occur on every call to
-//! `getrandom`, hence after the first successful call one can be reasonably
-//! confident that no errors will occur.
+//! We always choose failure over returning known insecure "random" bytes. In
+//! general, on supported platforms, failure is highly unlikely, though not
+//! impossible. If an error does occur, then it is likely that it will occur
+//! on every call to `getrandom`, hence after the first successful call one
+//! can be reasonably confident that no errors will occur.
 //!
 //! [1]: http://man7.org/linux/man-pages/man2/getrandom.2.html
 //! [2]: http://man7.org/linux/man-pages/man4/urandom.4.html
@@ -140,15 +165,19 @@
 //! [`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
 //! [`cprng_draw`]: https://fuchsia.dev/fuchsia-src/zircon/syscalls/cprng_draw
-//! [`crypto.randomBytes`]: https://nodejs.org/api/crypto.html#crypto_crypto_randombytes_size_callback
+//! [`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
 //! [`random_get`]: https://github.com/WebAssembly/WASI/blob/main/phases/snapshot/docs.md#-random_getbuf-pointeru8-buf_len-size---errno
 //! [WebAssembly support]: #webassembly-support
+//! [`wasm-bindgen`]: https://github.com/rustwasm/wasm-bindgen
+//! [`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
 
 #![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.5"
+    html_root_url = "https://docs.rs/getrandom/0.2.8"
 )]
 #![no_std]
 #![warn(rust_2018_idioms, unused_lifetimes, missing_docs)]
@@ -223,6 +252,11 @@
     } 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(feature = "custom")] {
         use custom as imp;
     } else if #[cfg(all(target_arch = "wasm32", target_os = "unknown"))] {
diff --git a/src/linux_android.rs b/src/linux_android.rs
index 5508fdd..4270b67 100644
--- a/src/linux_android.rs
+++ b/src/linux_android.rs
@@ -14,6 +14,7 @@
 };
 
 pub fn getrandom_inner(dest: &mut [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) {
         sys_fill_exact(dest, |buf| unsafe {
diff --git a/src/macos.rs b/src/macos.rs
index 585a35a..671a053 100644
--- a/src/macos.rs
+++ b/src/macos.rs
@@ -17,6 +17,7 @@
 type GetEntropyFn = unsafe extern "C" fn(*mut u8, 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) };
diff --git a/src/openbsd.rs b/src/openbsd.rs
index c8d28b3..4137173 100644
--- a/src/openbsd.rs
+++ b/src/openbsd.rs
@@ -10,6 +10,7 @@
 use crate::{util_libc::last_os_error, Error};
 
 pub fn getrandom_inner(dest: &mut [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()) };
         if ret == -1 {
diff --git a/src/solaris_illumos.rs b/src/solaris_illumos.rs
index 2d1b767..cf3067d 100644
--- a/src/solaris_illumos.rs
+++ b/src/solaris_illumos.rs
@@ -30,6 +30,7 @@
 type GetRandomFn = unsafe extern "C" fn(*mut u8, libc::size_t, libc::c_uint) -> libc::c_int;
 
 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") };
     if let Some(fptr) = GETRANDOM.ptr() {
         let func: GetRandomFn = unsafe { mem::transmute(fptr) };
diff --git a/src/util_libc.rs b/src/util_libc.rs
index 6823609..d057071 100644
--- a/src/util_libc.rs
+++ b/src/util_libc.rs
@@ -6,8 +6,13 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 #![allow(dead_code)]
-use crate::{util::LazyUsize, Error};
-use core::{num::NonZeroU32, ptr::NonNull};
+use crate::Error;
+use core::{
+    num::NonZeroU32,
+    ptr::NonNull,
+    sync::atomic::{fence, AtomicPtr, Ordering},
+};
+use libc::c_void;
 
 cfg_if! {
     if #[cfg(any(target_os = "netbsd", target_os = "openbsd", target_os = "android"))] {
@@ -20,6 +25,12 @@
         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"))] {
+        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;
     }
 }
 
@@ -70,29 +81,57 @@
 
 // A "weak" binding to a C function that may or may not be present at runtime.
 // Used for supporting newer OS features while still building on older systems.
-// F must be a function pointer of type `unsafe extern "C" fn`. Based off of the
-// weak! macro in libstd.
+// Based off of the DlsymWeak struct in libstd:
+// https://github.com/rust-lang/rust/blob/1.61.0/library/std/src/sys/unix/weak.rs#L84
+// except that the caller must manually cast self.ptr() to a function pointer.
 pub struct Weak {
     name: &'static str,
-    addr: LazyUsize,
+    addr: AtomicPtr<c_void>,
 }
 
 impl Weak {
+    // A non-null pointer value which indicates we are uninitialized. This
+    // constant should ideally not be a valid address of a function pointer.
+    // However, if by chance libc::dlsym does return UNINIT, there will not
+    // be undefined behavior. libc::dlsym will just be called each time ptr()
+    // is called. This would be inefficient, but correct.
+    // TODO: Replace with core::ptr::invalid_mut(1) when that is stable.
+    const UNINIT: *mut c_void = 1 as *mut c_void;
+
     // Construct a binding to a C function with a given name. This function is
     // unsafe because `name` _must_ be null terminated.
     pub const unsafe fn new(name: &'static str) -> Self {
         Self {
             name,
-            addr: LazyUsize::new(),
+            addr: AtomicPtr::new(Self::UNINIT),
         }
     }
 
-    // Return a function pointer if present at runtime. Otherwise, return null.
-    pub fn ptr(&self) -> Option<NonNull<libc::c_void>> {
-        let addr = self.addr.unsync_init(|| unsafe {
-            libc::dlsym(libc::RTLD_DEFAULT, self.name.as_ptr() as *const _) as usize
-        });
-        NonNull::new(addr as *mut _)
+    // Return the address of a function if present at runtime. Otherwise,
+    // return None. Multiple callers can call ptr() concurrently. It will
+    // always return _some_ value returned by libc::dlsym. However, the
+    // dlsym function may be called multiple times.
+    pub fn ptr(&self) -> Option<NonNull<c_void>> {
+        // Despite having only a single atomic variable (self.addr), we still
+        // cannot always use Ordering::Relaxed, as we need to make sure a
+        // successful call to dlsym() is "ordered before" any data read through
+        // the returned pointer (which occurs when the function is called).
+        // Our implementation mirrors that of the one in libstd, meaning that
+        // the use of non-Relaxed operations is probably unnecessary.
+        match self.addr.load(Ordering::Relaxed) {
+            Self::UNINIT => {
+                let symbol = self.name.as_ptr() as *const _;
+                let addr = unsafe { libc::dlsym(libc::RTLD_DEFAULT, symbol) };
+                // Synchronizes with the Acquire fence below
+                self.addr.store(addr, Ordering::Release);
+                NonNull::new(addr)
+            }
+            addr => {
+                let func = NonNull::new(addr)?;
+                fence(Ordering::Acquire);
+                Some(func)
+            }
+        }
     }
 }
 
@@ -107,9 +146,15 @@
 // 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));
-    let fd = open(path.as_ptr() as *const _, libc::O_RDONLY | libc::O_CLOEXEC);
-    if fd < 0 {
-        return Err(last_os_error());
+    loop {
+        let fd = open(path.as_ptr() as *const _, libc::O_RDONLY | libc::O_CLOEXEC);
+        if fd >= 0 {
+            return Ok(fd);
+        }
+        let err = last_os_error();
+        // We should try again if open() was interrupted.
+        if err.raw_os_error() != Some(libc::EINTR) {
+            return Err(err);
+        }
     }
-    Ok(fd)
 }
diff --git a/src/wasi.rs b/src/wasi.rs
index 2d413e0..c512182 100644
--- a/src/wasi.rs
+++ b/src/wasi.rs
@@ -9,15 +9,11 @@
 //! Implementation for WASI
 use crate::Error;
 use core::num::NonZeroU32;
-use wasi::random_get;
+use wasi::wasi_snapshot_preview1::random_get;
 
 pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
-    unsafe {
-        random_get(dest.as_mut_ptr(), dest.len()).map_err(|e: wasi::Error| {
-            // convert wasi's Error into getrandom's NonZeroU32 error
-            // SAFETY: `wasi::Error` is `NonZeroU16` internally, so `e.raw_error()`
-            // will never return 0
-            NonZeroU32::new_unchecked(e.raw_error() as u32).into()
-        })
+    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()),
     }
 }
diff --git a/src/windows.rs b/src/windows.rs
index 643badd..41dc37a 100644
--- a/src/windows.rs
+++ b/src/windows.rs
@@ -24,6 +24,7 @@
 pub fn getrandom_inner(dest: &mut [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(),