Skip to content

Commit c6277e1

Browse files
committed
Switch to serializing/deserializing on the Javascript side rather than the Rust side
Should avoid potential provenance issues, see rust-lang/rust#95583
1 parent 93f1abc commit c6277e1

File tree

3 files changed

+130
-67
lines changed

3 files changed

+130
-67
lines changed

arwa/src/html/custom_element.rs

+95-64
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
use std::any::{Any, TypeId};
2+
use std::mem::MaybeUninit;
23
use std::ops::Deref;
3-
use std::{marker, mem};
4+
use std::ptr::DynMetadata;
5+
use std::{marker, mem, ptr};
46

57
use js_sys::{Array, Function, Reflect, Uint8Array};
68
use wasm_bindgen::closure::Closure;
@@ -11,7 +13,6 @@ use web_sys::HtmlElement;
1113
use crate::dom::{impl_shadow_host_for_element, DynamicElement, Name, ParentNode};
1214
use crate::finalization_registry::FinalizationRegistry;
1315
use crate::html::{impl_html_element_traits, CustomElementName};
14-
use crate::util::type_id_to_u64;
1516
use crate::InvalidCast;
1617
use crate::{dom_exception_wrapper, impl_common_wrapper_traits};
1718

@@ -20,24 +21,19 @@ thread_local! {
2021
let callback = |held_value: JsValue| {
2122
// Reconstruct the Box<dyn Any> that holds the data, then drop it.
2223

23-
let pointer_data: Uint8Array = held_value.unchecked_into();
24+
let serialized_data: Uint8Array = held_value.unchecked_into();
2425

25-
// Copy pointer data to WASM linear memory that we can operate on.
26-
let mut scratch = [0u8; 24];
27-
let size_of_usize = mem::size_of::<usize>();
26+
let mut uninit_custom_element_data = MaybeUninit::<CustomElementData>::uninit();
27+
let data_ptr = uninit_custom_element_data.as_mut_ptr();
2828

29-
pointer_data.copy_to(&mut scratch[..size_of_usize * 2 + 8]);
29+
deserialize_custom_element_data(&wasm_bindgen::memory(), data_ptr, &serialized_data);
3030

31-
let (address_bytes, rest) = scratch.split_at(size_of_usize);
32-
let (vtable_bytes, _) = rest.split_at(size_of_usize);
33-
34-
let address_usize = usize::from_ne_bytes(address_bytes.try_into().unwrap_throw());
35-
let vtable_usize = usize::from_ne_bytes(vtable_bytes.try_into().unwrap_throw());
36-
37-
let ptr: *mut dyn Any = unsafe { mem::transmute((address_usize, vtable_usize)) };
31+
let custom_element_data = unsafe {
32+
uninit_custom_element_data.assume_init()
33+
};
3834

3935
unsafe {
40-
mem::drop(Box::from_raw(ptr));
36+
mem::drop(Box::from_raw(custom_element_data.to_dyn_any_ptr()));
4137
}
4238
};
4339

@@ -51,6 +47,18 @@ thread_local! {
5147
};
5248
}
5349

50+
struct CustomElementData {
51+
address: *mut (),
52+
metadata: DynMetadata<dyn Any>,
53+
type_id: TypeId,
54+
}
55+
56+
impl CustomElementData {
57+
fn to_dyn_any_ptr(&self) -> *mut dyn Any {
58+
ptr::from_raw_parts_mut(self.address, self.metadata)
59+
}
60+
}
61+
5462
pub(crate) mod extendable_element_seal {
5563
pub trait Seal {
5664
const EXTENDED_NAME: Option<&'static str>;
@@ -95,14 +103,14 @@ where
95103
E: ExtendableElement,
96104
{
97105
fn from_raw_unchecked(raw: RawCustomElement) -> Self {
98-
let mut scratch = [0u8; 24];
99-
let size_of_usize = mem::size_of::<usize>();
106+
let mut uninit_custom_element_data = MaybeUninit::<CustomElementData>::uninit();
107+
let data_ptr = uninit_custom_element_data.as_mut_ptr();
108+
109+
raw.deserialize_custom_element_data(&wasm_bindgen::memory(), data_ptr);
100110

101-
raw.data().copy_to(&mut scratch[0..size_of_usize * 2 + 8]);
111+
let custom_element_data = unsafe { uninit_custom_element_data.assume_init() };
102112

103-
let data_ptr_bits =
104-
usize::from_ne_bytes(scratch[..size_of_usize].try_into().unwrap_throw());
105-
let data = <*const T>::from_bits(data_ptr_bits);
113+
let data = custom_element_data.address as *const T;
106114
let extended = E::from_web_sys_html_element_unchecked(raw.into());
107115

108116
CustomElement { data, extended }
@@ -139,36 +147,36 @@ where
139147
fn try_from(element: DynamicElement) -> Result<Self, Self::Error> {
140148
let element: web_sys::Element = element.into();
141149

142-
if let Ok(value) = Reflect::get(element.as_ref(), &"__arwa_custom_element_data".into()) {
143-
if !value.is_undefined() {
144-
let data: Uint8Array = value.unchecked_into();
145-
let target_type_num = type_id_to_u64(TypeId::of::<CustomElement<T, E>>());
150+
let is_custom_element = Reflect::has(
151+
element.as_ref(),
152+
&"__deserialize_custom_element_data".into(),
153+
)
154+
.unwrap_or(false);
146155

147-
let mut scratch = [0u8; 24];
148-
let size_of_usize = mem::size_of::<usize>();
149-
let type_num_start = size_of_usize * 2;
150-
let type_num_end = size_of_usize * 2 + 8;
156+
if is_custom_element {
157+
let raw = element.unchecked_into::<RawCustomElement>();
151158

152-
data.copy_to(&mut scratch[0..size_of_usize * 2 + 8]);
159+
let mut uninit_custom_element_data = MaybeUninit::<CustomElementData>::uninit();
160+
let data_ptr = uninit_custom_element_data.as_mut_ptr();
153161

154-
let type_num = u64::from_ne_bytes(
155-
scratch[type_num_start..type_num_end]
156-
.try_into()
157-
.unwrap_throw(),
158-
);
162+
raw.deserialize_custom_element_data(&wasm_bindgen::memory(), data_ptr);
159163

160-
if type_num == target_type_num {
161-
let data_ptr_bits =
162-
usize::from_ne_bytes(scratch[..size_of_usize].try_into().unwrap_throw());
163-
let data = <*const T>::from_bits(data_ptr_bits);
164-
let extended = E::from_web_sys_html_element_unchecked(element.unchecked_into());
164+
let custom_element_data = unsafe { uninit_custom_element_data.assume_init() };
165+
let target_type_id = TypeId::of::<CustomElement<T, E>>();
165166

166-
return Ok(CustomElement { data, extended });
167-
}
167+
if custom_element_data.type_id == target_type_id {
168+
let data = custom_element_data.address as *const T;
169+
let extended = E::from_web_sys_html_element_unchecked(raw.unchecked_into());
170+
171+
Ok(CustomElement { data, extended })
172+
} else {
173+
Err(InvalidCast::new(DynamicElement::from(
174+
raw.unchecked_into::<web_sys::Element>(),
175+
)))
168176
}
177+
} else {
178+
Err(InvalidCast::new(DynamicElement::from(element)))
169179
}
170-
171-
Err(InvalidCast::new(DynamicElement::from(element)))
172180
}
173181
}
174182

@@ -256,7 +264,6 @@ impl CustomElementRegistry {
256264
} = descriptor;
257265

258266
let type_id = TypeId::of::<CustomElement<T, E>>();
259-
let type_num = type_id_to_u64(type_id);
260267

261268
let constructor = move |extended: web_sys::HtmlElement| {
262269
let extended = E::from_web_sys_html_element_unchecked(extended);
@@ -265,25 +272,31 @@ impl CustomElementRegistry {
265272

266273
let data = Box::new(data) as Box<dyn Any>;
267274
let data_ptr = Box::into_raw(data);
268-
let (address_ptr, vtable_ptr): (usize, usize) = unsafe { mem::transmute(data_ptr) };
269-
270-
let mut scratch = [0u8; 24];
271-
let size_of_usize = mem::size_of::<usize>();
272-
let type_num_start = size_of_usize * 2;
273-
let type_num_end = size_of_usize * 2 + 8;
274-
275-
scratch[0..size_of_usize].copy_from_slice(&address_ptr.to_ne_bytes());
276-
scratch[size_of_usize..type_num_start].copy_from_slice(&vtable_ptr.to_ne_bytes());
277-
scratch[type_num_start..type_num_end].copy_from_slice(&type_num.to_ne_bytes());
275+
let (address, metadata) = data_ptr.to_raw_parts();
276+
let mut custom_element_data = CustomElementData {
277+
address,
278+
metadata,
279+
type_id,
280+
};
281+
let ptr = &mut custom_element_data as *mut CustomElementData;
278282

279-
let data = Uint8Array::new_with_length(type_num_end as u32);
283+
let serialized = serialize_custom_element_data(
284+
&wasm_bindgen::memory(),
285+
ptr,
286+
mem::size_of::<CustomElementData>() as u32,
287+
);
280288

281-
data.copy_from(&scratch[..type_num_end]);
289+
// Make sure it doesn't drop early
290+
mem::drop(custom_element_data);
282291

283-
CUSTOM_ELEMENT_FINALIZATION_REGISTRY
284-
.with(|r| r.register(extended.as_web_sys_html_element().as_ref(), data.as_ref()));
292+
CUSTOM_ELEMENT_FINALIZATION_REGISTRY.with(|r| {
293+
r.register(
294+
extended.as_web_sys_html_element().as_ref(),
295+
serialized.as_ref(),
296+
)
297+
});
285298

286-
data
299+
serialized
287300
};
288301

289302
let constructor_boxed =
@@ -389,10 +402,14 @@ dom_exception_wrapper!(RegisterCustomElementError);
389402
#[wasm_bindgen]
390403
extern "C" {
391404
#[wasm_bindgen(extends = HtmlElement)]
392-
pub type RawCustomElement;
393-
394-
#[wasm_bindgen(method, getter, js_name = "__arwa_custom_element_data")]
395-
pub fn data(this: &RawCustomElement) -> Uint8Array;
405+
type RawCustomElement;
406+
407+
#[wasm_bindgen(method, js_name = "__deserialize_custom_element_data")]
408+
fn deserialize_custom_element_data(
409+
this: &RawCustomElement,
410+
wasm_memory: &JsValue,
411+
ptr: *mut CustomElementData,
412+
);
396413
}
397414

398415
#[wasm_bindgen(module = "/src/html/define_custom_element.js")]
@@ -408,6 +425,20 @@ extern "C" {
408425
attribute_changed_callback: &Function,
409426
observed_attributes: &Array,
410427
) -> Result<JsValue, JsValue>;
428+
429+
#[wasm_bindgen]
430+
fn serialize_custom_element_data(
431+
wasm_memory: &JsValue,
432+
pointer: *mut CustomElementData,
433+
size: u32,
434+
) -> Uint8Array;
435+
436+
#[wasm_bindgen]
437+
fn deserialize_custom_element_data(
438+
wasm_memory: &JsValue,
439+
pointer: *mut CustomElementData,
440+
serialized_data: &Uint8Array,
441+
);
411442
}
412443

413444
macro_rules! impl_extendable_element {

arwa/src/html/define_custom_element.js

+28-2
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,12 @@ export function define_custom_element(
2525
this.#arwa_custom_element_data = constructor(this);
2626
}
2727

28-
get __arwa_custom_element_data() {
29-
return this.#arwa_custom_element_data;
28+
__deserialize_custom_element_data(wasm_linear_memory_buffer, pointer) {
29+
return deserialize_custom_element_data(
30+
wasm_linear_memory_buffer,
31+
pointer,
32+
this.#arwa_custom_element_data
33+
);
3034
}
3135

3236
connectedCallback() {
@@ -48,3 +52,25 @@ export function define_custom_element(
4852
extendedName ? { extends: extendedName } : undefined
4953
);
5054
}
55+
56+
export function serialize_custom_element_data(
57+
wasm_memory,
58+
pointer,
59+
size
60+
) {
61+
// Create a view the relevant region of the WASM linear memory buffer.
62+
let view = new Uint8Array(wasm_memory.buffer, pointer, size);
63+
64+
// Copy it to a new non-view Uint8Array and return
65+
return new Uint8Array(view);
66+
}
67+
68+
export function deserialize_custom_element_data(
69+
wasm_memory,
70+
pointer,
71+
custom_element_data
72+
) {
73+
let buffer_view = new Uint8Array(wasm_memory.buffer);
74+
75+
buffer_view.set(custom_element_data, pointer);
76+
}

arwa/src/lib.rs

+7-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
1-
#![feature(const_type_id, get_mut_unchecked, iter_intersperse, ptr_to_from_bits)]
1+
#![feature(
2+
const_type_id,
3+
get_mut_unchecked,
4+
iter_intersperse,
5+
ptr_metadata,
6+
ptr_to_from_bits
7+
)]
28

39
pub mod collection;
410
pub mod connection;

0 commit comments

Comments
 (0)