Skip to content

Commit 59c32bf

Browse files
committed
Account for SharedArrayBuffer not being available
1 parent 81e43f7 commit 59c32bf

File tree

1 file changed

+64
-31
lines changed

1 file changed

+64
-31
lines changed

src/backends/wasm_js.rs

Lines changed: 64 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,7 @@
66
extern crate std;
77

88
use crate::Error;
9-
use core::{
10-
mem::MaybeUninit,
11-
sync::atomic::{AtomicU8, Ordering},
12-
};
9+
use core::mem::MaybeUninit;
1310
#[cfg(feature = "std")]
1411
use std::thread_local;
1512

@@ -18,42 +15,18 @@ pub use crate::util::{inner_u32, inner_u64};
1815
#[cfg(not(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none"))))]
1916
compile_error!("`wasm_js` backend can be enabled only for OS-less WASM targets!");
2017

21-
use js_sys::{SharedArrayBuffer, Uint8Array, WebAssembly::Memory};
22-
use wasm_bindgen::{prelude::wasm_bindgen, JsCast, JsValue};
18+
use js_sys::{JsString, Uint8Array};
19+
use wasm_bindgen::{prelude::wasm_bindgen, JsValue};
2320

2421
// Size of our temporary Uint8Array buffer used with WebCrypto methods
2522
// Maximum is 65536 bytes see https://developer.mozilla.org/en-US/docs/Web/API/Crypto/getRandomValues
2623
const CRYPTO_BUFFER_SIZE: u16 = 256;
2724

28-
const MEMORY_KIND_UNINIT: u8 = 0;
29-
const MEMORY_KIND_NOT_SHARED: u8 = 1;
30-
const MEMORY_KIND_SHARED: u8 = 2;
31-
32-
static MEMORY_KIND: AtomicU8 = AtomicU8::new(0);
33-
3425
pub fn fill_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
3526
CRYPTO.with(|crypto| {
3627
let crypto = crypto.as_ref().ok_or(Error::WEB_CRYPTO)?;
3728

38-
let shared = loop {
39-
break match MEMORY_KIND.load(Ordering::Relaxed) {
40-
MEMORY_KIND_NOT_SHARED => false,
41-
MEMORY_KIND_SHARED => true,
42-
MEMORY_KIND_UNINIT => {
43-
let memory: Memory = wasm_bindgen::memory().unchecked_into();
44-
let val = if memory.buffer().is_instance_of::<SharedArrayBuffer>() {
45-
MEMORY_KIND_SHARED
46-
} else {
47-
MEMORY_KIND_NOT_SHARED
48-
};
49-
MEMORY_KIND.store(val, Ordering::Relaxed);
50-
continue;
51-
}
52-
_ => unreachable!(),
53-
};
54-
};
55-
56-
if shared {
29+
if is_sab() {
5730
// getRandomValues does not work with all types of WASM memory,
5831
// so we initially write to browser memory to avoid exceptions.
5932
let buf = Uint8Array::new_with_length(CRYPTO_BUFFER_SIZE.into());
@@ -88,7 +61,62 @@ pub fn fill_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
8861
})
8962
}
9063

64+
#[cfg(not(target_feature = "atomics"))]
65+
fn is_sab() -> bool {
66+
use core::sync::atomic::{AtomicU8, Ordering};
67+
68+
use js_sys::WebAssembly::Memory;
69+
use js_sys::{Object, SharedArrayBuffer};
70+
use wasm_bindgen::JsCast;
71+
72+
const MEMORY_KIND_UNINIT: u8 = 0;
73+
const MEMORY_KIND_NOT_SHARED: u8 = 1;
74+
const MEMORY_KIND_SHARED: u8 = 2;
75+
76+
static MEMORY_KIND: AtomicU8 = AtomicU8::new(0);
77+
78+
loop {
79+
break match MEMORY_KIND.load(Ordering::Relaxed) {
80+
MEMORY_KIND_NOT_SHARED => false,
81+
MEMORY_KIND_SHARED => true,
82+
MEMORY_KIND_UNINIT => {
83+
let buffer = wasm_bindgen::memory().unchecked_into::<Memory>().buffer();
84+
85+
// `SharedArrayBuffer` is only available with COOP & COEP. But even without its
86+
// possible to create a shared `WebAssembly.Memory`, so we check for that via
87+
// the constructor name.
88+
//
89+
// Keep in mind that `crossOriginIsolated` is not available on Node.js, in
90+
// which case we can still use `instanceof` because `SharedArrayBuffer` is
91+
// always available.
92+
let shared = match CROSS_ORIGIN_ISOLATED.with(Option::clone) {
93+
Some(true) | None => buffer.is_instance_of::<SharedArrayBuffer>(),
94+
Some(false) => {
95+
let constructor_name = Object::from(buffer).constructor().name();
96+
SHARED_ARRAY_BUFFER_NAME.with(|name| &constructor_name == name)
97+
}
98+
};
99+
100+
let val = if shared {
101+
MEMORY_KIND_SHARED
102+
} else {
103+
MEMORY_KIND_NOT_SHARED
104+
};
105+
MEMORY_KIND.store(val, Ordering::Relaxed);
106+
continue;
107+
}
108+
_ => unreachable!(),
109+
};
110+
}
111+
}
112+
113+
#[cfg(target_feature = "atomics")]
114+
fn is_sab() -> bool {
115+
true
116+
}
117+
91118
#[wasm_bindgen]
119+
#[rustfmt::skip]
92120
extern "C" {
93121
// Web Crypto API: Crypto interface (https://www.w3.org/TR/WebCryptoAPI/)
94122
type Crypto;
@@ -100,4 +128,9 @@ extern "C" {
100128
fn get_random_values(this: &Crypto, buf: &Uint8Array) -> Result<(), JsValue>;
101129
#[wasm_bindgen(method, js_name = getRandomValues, catch)]
102130
fn get_random_values_ref(this: &Crypto, buf: &mut [u8]) -> Result<(), JsValue>;
131+
// Returns the [`crossOriginIsolated`](https://developer.mozilla.org/en-US/docs/Web/API/crossOriginIsolated) global property.
132+
#[wasm_bindgen(thread_local_v2, js_namespace = globalThis, js_name = crossOriginIsolated)]
133+
static CROSS_ORIGIN_ISOLATED: Option<bool>;
134+
#[wasm_bindgen(thread_local_v2, static_string)]
135+
static SHARED_ARRAY_BUFFER_NAME: JsString = "SharedArrayBuffer";
103136
}

0 commit comments

Comments
 (0)