Skip to content

Commit 82d0751

Browse files
committed
Account for SharedArrayBuffer not being available
1 parent d0b6d66 commit 82d0751

File tree

1 file changed

+64
-31
lines changed

1 file changed

+64
-31
lines changed

src/backends/wasm_js.rs

+64-31
Original file line numberDiff line numberDiff line change
@@ -5,53 +5,26 @@
55
extern crate std;
66

77
use crate::Error;
8-
use core::{
9-
mem::MaybeUninit,
10-
sync::atomic::{AtomicU8, Ordering},
11-
};
8+
use core::mem::MaybeUninit;
129
use std::thread_local;
1310

1411
pub use crate::util::{inner_u32, inner_u64};
1512

1613
#[cfg(not(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none"))))]
1714
compile_error!("`wasm_js` backend can be enabled only for OS-less WASM targets!");
1815

19-
use js_sys::{SharedArrayBuffer, Uint8Array, WebAssembly::Memory};
20-
use wasm_bindgen::{prelude::wasm_bindgen, JsCast, JsValue};
16+
use js_sys::{JsString, Uint8Array};
17+
use wasm_bindgen::{prelude::wasm_bindgen, JsValue};
2118

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

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

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

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

0 commit comments

Comments
 (0)