Skip to content

Commit d0b6d66

Browse files
committed
Don't use Uint8Array when not necessary
1 parent 00530ff commit d0b6d66

File tree

2 files changed

+92
-34
lines changed

2 files changed

+92
-34
lines changed

.github/workflows/tests.yml

+31-14
Original file line numberDiff line numberDiff line change
@@ -229,11 +229,27 @@ jobs:
229229
# run: cargo test
230230

231231
web:
232-
name: Web
232+
name: Web ${{ matrix.rust.description }}
233233
runs-on: ubuntu-24.04
234+
strategy:
235+
fail-fast: false
236+
matrix:
237+
rust:
238+
- { version: stable, atomics: false }
239+
- {
240+
description: with Atomics,
241+
atomics: true,
242+
version: nightly,
243+
components: rust-src,
244+
flags: '-Ctarget-feature=+atomics,+bulk-memory',
245+
args: '-- -Zbuild-std=panic_abort,std',
246+
}
234247
steps:
235248
- uses: actions/checkout@v4
236-
- uses: dtolnay/rust-toolchain@stable
249+
- uses: dtolnay/rust-toolchain@master
250+
with:
251+
toolchain: ${{ matrix.rust.version }}
252+
components: ${{ matrix.rust.components }}
237253
- name: Install precompiled wasm-pack
238254
shell: bash
239255
run: |
@@ -243,35 +259,36 @@ jobs:
243259
wasm-pack --version
244260
- uses: Swatinem/rust-cache@v2
245261
- name: Test (Node)
262+
if: matrix.rust.atomics == false
246263
env:
247-
RUSTFLAGS: -Dwarnings --cfg getrandom_backend="wasm_js"
248-
run: wasm-pack test --node
264+
RUSTFLAGS: -Dwarnings --cfg getrandom_backend="wasm_js" ${{ matrix.rust.flags }}
265+
run: wasm-pack test --node ${{ matrix.rust.args }}
249266
- name: Test (Firefox)
250267
env:
251268
WASM_BINDGEN_USE_BROWSER: 1
252-
RUSTFLAGS: -Dwarnings --cfg getrandom_backend="wasm_js"
253-
run: wasm-pack test --headless --firefox
269+
RUSTFLAGS: -Dwarnings --cfg getrandom_backend="wasm_js" ${{ matrix.rust.flags }}
270+
run: wasm-pack test --headless --firefox ${{ matrix.rust.args }}
254271
- name: Test (Chrome)
255272
env:
256273
WASM_BINDGEN_USE_BROWSER: 1
257-
RUSTFLAGS: -Dwarnings --cfg getrandom_backend="wasm_js"
258-
run: wasm-pack test --headless --chrome
274+
RUSTFLAGS: -Dwarnings --cfg getrandom_backend="wasm_js" ${{ matrix.rust.flags }}
275+
run: wasm-pack test --headless --chrome ${{ matrix.rust.args }}
259276
- name: Test (dedicated worker)
260277
env:
261278
WASM_BINDGEN_USE_DEDICATED_WORKER: 1
262-
RUSTFLAGS: -Dwarnings --cfg getrandom_backend="wasm_js"
263-
run: wasm-pack test --headless --firefox
279+
RUSTFLAGS: -Dwarnings --cfg getrandom_backend="wasm_js" ${{ matrix.rust.flags }}
280+
run: wasm-pack test --headless --firefox ${{ matrix.rust.args }}
264281
- name: Test (shared worker)
265282
env:
266283
WASM_BINDGEN_USE_SHARED_WORKER: 1
267-
RUSTFLAGS: -Dwarnings --cfg getrandom_backend="wasm_js"
268-
run: wasm-pack test --headless --firefox
284+
RUSTFLAGS: -Dwarnings --cfg getrandom_backend="wasm_js" ${{ matrix.rust.flags }}
285+
run: wasm-pack test --headless --firefox ${{ matrix.rust.args }}
269286
- name: Test (service worker)
270287
env:
271288
WASM_BINDGEN_USE_SERVICE_WORKER: 1
272-
RUSTFLAGS: -Dwarnings --cfg getrandom_backend="wasm_js"
289+
RUSTFLAGS: -Dwarnings --cfg getrandom_backend="wasm_js" ${{ matrix.rust.flags }}
273290
# Firefox doesn't support module service workers and therefor can't import scripts
274-
run: wasm-pack test --headless --chrome
291+
run: wasm-pack test --headless --chrome ${{ matrix.rust.args }}
275292

276293
wasi:
277294
name: WASI

src/backends/wasm_js.rs

+61-20
Original file line numberDiff line numberDiff line change
@@ -5,43 +5,82 @@
55
extern crate std;
66

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

1114
pub use crate::util::{inner_u32, inner_u64};
1215

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

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

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

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+
2332
pub fn fill_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
2433
CRYPTO.with(|crypto| {
2534
let crypto = crypto.as_ref().ok_or(Error::WEB_CRYPTO)?;
2635

27-
// getRandomValues does not work with all types of WASM memory,
28-
// so we initially write to browser memory to avoid exceptions.
29-
let buf = Uint8Array::new_with_length(CRYPTO_BUFFER_SIZE.into());
30-
for chunk in dest.chunks_mut(CRYPTO_BUFFER_SIZE.into()) {
31-
let chunk_len: u32 = chunk
32-
.len()
33-
.try_into()
34-
.expect("chunk length is bounded by CRYPTO_BUFFER_SIZE");
35-
// The chunk can be smaller than buf's length, so we call to
36-
// JS to create a smaller view of buf without allocation.
37-
let sub_buf = buf.subarray(0, chunk_len);
38-
39-
if crypto.get_random_values(&sub_buf).is_err() {
40-
return Err(Error::WEB_GET_RANDOM_VALUES);
41-
}
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 {
55+
// getRandomValues does not work with all types of WASM memory,
56+
// so we initially write to browser memory to avoid exceptions.
57+
let buf = Uint8Array::new_with_length(CRYPTO_BUFFER_SIZE.into());
58+
for chunk in dest.chunks_mut(CRYPTO_BUFFER_SIZE.into()) {
59+
let chunk_len: u32 = chunk
60+
.len()
61+
.try_into()
62+
.expect("chunk length is bounded by CRYPTO_BUFFER_SIZE");
63+
// The chunk can be smaller than buf's length, so we call to
64+
// JS to create a smaller view of buf without allocation.
65+
let sub_buf = buf.subarray(0, chunk_len);
4266

43-
// SAFETY: `sub_buf`'s length is the same length as `chunk`
44-
unsafe { sub_buf.raw_copy_to_ptr(chunk.as_mut_ptr().cast::<u8>()) };
67+
if crypto.get_random_values(&sub_buf).is_err() {
68+
return Err(Error::WEB_GET_RANDOM_VALUES);
69+
}
70+
71+
// SAFETY: `sub_buf`'s length is the same length as `chunk`
72+
unsafe { sub_buf.raw_copy_to_ptr(chunk.as_mut_ptr().cast::<u8>()) };
73+
}
74+
} else {
75+
for chunk in dest.chunks_mut(CRYPTO_BUFFER_SIZE.into()) {
76+
// SAFETY: this is only safe because on Wasm the issues with unitialized data don't exist
77+
let buf = unsafe {
78+
core::slice::from_raw_parts_mut(chunk.as_mut_ptr().cast::<u8>(), chunk.len())
79+
};
80+
if crypto.get_random_values_ref(buf).is_err() {
81+
return Err(Error::WEB_GET_RANDOM_VALUES);
82+
}
83+
}
4584
}
4685
Ok(())
4786
})
@@ -57,4 +96,6 @@ extern "C" {
5796
// Crypto.getRandomValues()
5897
#[wasm_bindgen(method, js_name = getRandomValues, catch)]
5998
fn get_random_values(this: &Crypto, buf: &Uint8Array) -> Result<(), JsValue>;
99+
#[wasm_bindgen(method, js_name = getRandomValues, catch)]
100+
fn get_random_values_ref(this: &Crypto, buf: &mut [u8]) -> Result<(), JsValue>;
60101
}

0 commit comments

Comments
 (0)