From 1e669bef2a35e8b20ae627c9e3512aeebe5a5ca1 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Wed, 14 Apr 2021 07:51:04 -0700 Subject: [PATCH 1/2] Fix UB in setting memory in emscripten EmEnv --- lib/emscripten/src/env/unix/mod.rs | 33 ++++++++---------- lib/emscripten/src/env/windows/mod.rs | 20 +++++++---- lib/emscripten/src/lib.rs | 16 ++++----- lib/emscripten/src/syscalls/mod.rs | 6 ++-- lib/emscripten/src/syscalls/unix.rs | 48 ++++++++++++-------------- lib/emscripten/src/syscalls/windows.rs | 4 ++- 6 files changed, 64 insertions(+), 63 deletions(-) diff --git a/lib/emscripten/src/env/unix/mod.rs b/lib/emscripten/src/env/unix/mod.rs index 16746e5e464..6355fb3ec0d 100644 --- a/lib/emscripten/src/env/unix/mod.rs +++ b/lib/emscripten/src/env/unix/mod.rs @@ -150,10 +150,11 @@ pub fn _gai_strerror(ctx: &EmEnv, ecode: i32) -> i32 { let cstr = unsafe { std::ffi::CStr::from_ptr(libc::gai_strerror(ecode)) }; let bytes = cstr.to_bytes_with_nul(); let string_on_guest: WasmPtr = call_malloc_with_cast(ctx, bytes.len() as _); + let memory = ctx.memory(0); let writer = unsafe { string_on_guest - .deref_mut(ctx.memory(0), 0, bytes.len() as _) + .deref_mut(&memory, 0, bytes.len() as _) .unwrap() }; for (i, byte) in bytes.iter().enumerate() { @@ -175,7 +176,7 @@ pub fn _getaddrinfo( let memory = ctx.memory(0); debug!(" => node = {}", unsafe { node_ptr - .deref(memory) + .deref(&memory) .map(|np| { std::ffi::CStr::from_ptr(np as *const Cell as *const c_char) .to_string_lossy() @@ -184,7 +185,7 @@ pub fn _getaddrinfo( }); debug!(" => server_str = {}", unsafe { service_str_ptr - .deref(memory) + .deref(&memory) .map(|np| { std::ffi::CStr::from_ptr(np as *const Cell as *const c_char) .to_string_lossy() @@ -192,7 +193,7 @@ pub fn _getaddrinfo( .unwrap_or(std::borrow::Cow::Borrowed("null")) }); - let hints = hints_ptr.deref(memory).map(|hints_memory| { + let hints = hints_ptr.deref(&memory).map(|hints_memory| { let hints_guest = hints_memory.get(); addrinfo { ai_flags: hints_guest.ai_flags, @@ -212,11 +213,11 @@ pub fn _getaddrinfo( let result = unsafe { libc::getaddrinfo( (node_ptr - .deref(memory) + .deref(&memory) .map(|m| m as *const Cell as *const c_char)) .unwrap_or(std::ptr::null()), service_str_ptr - .deref(memory) + .deref(&memory) .map(|m| m as *const Cell as *const c_char) .unwrap_or(std::ptr::null()), hints @@ -245,7 +246,7 @@ pub fn _getaddrinfo( // connect list if let Some(prev_guest) = previous_guest_node { - let mut pg = prev_guest.deref_mut(ctx.memory(0)).unwrap().get_mut(); + let mut pg = prev_guest.deref_mut(&memory).unwrap().get_mut(); pg.ai_next = current_guest_node_ptr; } @@ -257,10 +258,7 @@ pub fn _getaddrinfo( let host_sockaddr_ptr = (*current_host_node).ai_addr; let guest_sockaddr_ptr: WasmPtr = call_malloc_with_cast(ctx, host_addrlen as _); - let guest_sockaddr = guest_sockaddr_ptr - .deref_mut(ctx.memory(0)) - .unwrap() - .get_mut(); + let guest_sockaddr = guest_sockaddr_ptr.deref_mut(&memory).unwrap().get_mut(); guest_sockaddr.sa_family = (*host_sockaddr_ptr).sa_family as i16; guest_sockaddr.sa_data = (*host_sockaddr_ptr).sa_data; @@ -277,9 +275,8 @@ pub fn _getaddrinfo( let guest_canonname: WasmPtr = call_malloc_with_cast(ctx, str_size as _); - let guest_canonname_writer = guest_canonname - .deref(ctx.memory(0), 0, str_size as _) - .unwrap(); + let guest_canonname_writer = + guest_canonname.deref(&memory, 0, str_size as _).unwrap(); for (i, b) in canonname_bytes.into_iter().enumerate() { guest_canonname_writer[i].set(*b as _) } @@ -290,10 +287,8 @@ pub fn _getaddrinfo( } }; - let mut current_guest_node = current_guest_node_ptr - .deref_mut(ctx.memory(0)) - .unwrap() - .get_mut(); + let mut current_guest_node = + current_guest_node_ptr.deref_mut(&memory).unwrap().get_mut(); current_guest_node.ai_flags = (*current_host_node).ai_flags; current_guest_node.ai_family = (*current_host_node).ai_family; current_guest_node.ai_socktype = (*current_host_node).ai_socktype; @@ -311,7 +306,7 @@ pub fn _getaddrinfo( head_of_list.unwrap_or_else(|| WasmPtr::new(0)) }; - res_val_ptr.deref(ctx.memory(0)).unwrap().set(head_of_list); + res_val_ptr.deref(&memory).unwrap().set(head_of_list); 0 } diff --git a/lib/emscripten/src/env/windows/mod.rs b/lib/emscripten/src/env/windows/mod.rs index 874d5e5e683..9a2d00f7cfe 100644 --- a/lib/emscripten/src/env/windows/mod.rs +++ b/lib/emscripten/src/env/windows/mod.rs @@ -19,7 +19,8 @@ extern "C" { /// emscripten: _getenv // (name: *const char) -> *const c_char; pub fn _getenv(ctx: &EmEnv, name: u32) -> u32 { debug!("emscripten::_getenv"); - let name_string = read_string_from_wasm(ctx.memory(0), name); + let memory = ctx.memory(0); + let name_string = read_string_from_wasm(&memory, name); debug!("=> name({:?})", name_string); let c_str = unsafe { getenv(name_string.as_ptr() as *const libc::c_char) }; if c_str.is_null() { @@ -31,9 +32,10 @@ pub fn _getenv(ctx: &EmEnv, name: u32) -> u32 { /// emscripten: _setenv // (name: *const char, name: *const value, overwrite: int); pub fn _setenv(ctx: &EmEnv, name: u32, value: u32, _overwrite: u32) -> c_int { debug!("emscripten::_setenv"); + let memory = ctx.memory(0); // setenv does not exist on windows, so we hack it with _putenv - let name = read_string_from_wasm(ctx.memory(0), name); - let value = read_string_from_wasm(ctx.memory(0), value); + let name = read_string_from_wasm(&memory, name); + let value = read_string_from_wasm(&memory, value); let putenv_string = format!("{}={}", name, value); let putenv_cstring = CString::new(putenv_string).unwrap(); let putenv_raw_ptr = putenv_cstring.as_ptr(); @@ -45,7 +47,8 @@ pub fn _setenv(ctx: &EmEnv, name: u32, value: u32, _overwrite: u32) -> c_int { /// emscripten: _putenv // (name: *const char); pub fn _putenv(ctx: &EmEnv, name: c_int) -> c_int { debug!("emscripten::_putenv"); - let name_addr = emscripten_memory_pointer!(ctx.memory(0), name) as *const c_char; + let memory = ctx.memory(0); + let name_addr = emscripten_memory_pointer!(&memory, name) as *const c_char; debug!("=> name({:?})", unsafe { std::ffi::CStr::from_ptr(name_addr) }); @@ -55,7 +58,8 @@ pub fn _putenv(ctx: &EmEnv, name: c_int) -> c_int { /// emscripten: _unsetenv // (name: *const char); pub fn _unsetenv(ctx: &EmEnv, name: u32) -> c_int { debug!("emscripten::_unsetenv"); - let name = read_string_from_wasm(ctx.memory(0), name); + let memory = ctx.memory(0); + let name = read_string_from_wasm(&memory, name); // no unsetenv on windows, so use putenv with an empty value let unsetenv_string = format!("{}=", name); let unsetenv_cstring = CString::new(unsetenv_string).unwrap(); @@ -69,6 +73,7 @@ pub fn _getpwnam(ctx: &EmEnv, name_ptr: c_int) -> c_int { debug!("emscripten::_getpwnam {}", name_ptr); #[cfg(not(feature = "debug"))] let _ = name_ptr; + let memory = ctx.memory(0); #[repr(C)] struct GuestPasswd { @@ -85,7 +90,7 @@ pub fn _getpwnam(ctx: &EmEnv, name_ptr: c_int) -> c_int { unsafe { let passwd_struct_offset = call_malloc(ctx, mem::size_of::() as _); let passwd_struct_ptr = - emscripten_memory_pointer!(ctx.memory(0), passwd_struct_offset) as *mut GuestPasswd; + emscripten_memory_pointer!(&memory, passwd_struct_offset) as *mut GuestPasswd; (*passwd_struct_ptr).pw_name = 0; (*passwd_struct_ptr).pw_passwd = 0; (*passwd_struct_ptr).pw_gecos = 0; @@ -103,6 +108,7 @@ pub fn _getgrnam(ctx: &EmEnv, name_ptr: c_int) -> c_int { debug!("emscripten::_getgrnam {}", name_ptr); #[cfg(not(feature = "debug"))] let _ = name_ptr; + let memory = ctx.memory(0); #[repr(C)] struct GuestGroup { @@ -116,7 +122,7 @@ pub fn _getgrnam(ctx: &EmEnv, name_ptr: c_int) -> c_int { unsafe { let group_struct_offset = call_malloc(ctx, mem::size_of::() as _); let group_struct_ptr = - emscripten_memory_pointer!(ctx.memory(0), group_struct_offset) as *mut GuestGroup; + emscripten_memory_pointer!(&memory, group_struct_offset) as *mut GuestGroup; (*group_struct_ptr).gr_name = 0; (*group_struct_ptr).gr_passwd = 0; (*group_struct_ptr).gr_gid = 0; diff --git a/lib/emscripten/src/lib.rs b/lib/emscripten/src/lib.rs index f733aecd758..6a4251fbf88 100644 --- a/lib/emscripten/src/lib.rs +++ b/lib/emscripten/src/lib.rs @@ -17,7 +17,7 @@ use lazy_static::lazy_static; use std::collections::HashMap; use std::f64; use std::path::PathBuf; -use std::sync::{Arc, Mutex}; +use std::sync::{Arc, Mutex, RwLock}; use wasmer::{ imports, namespace, Exports, Function, FunctionType, Global, ImportObject, Instance, LazyInit, Memory, MemoryType, Module, NativeFunc, Pages, RuntimeError, Store, Table, TableType, Val, @@ -71,7 +71,7 @@ pub use self::utils::{ #[derive(Clone)] /// The environment provided to the Emscripten imports. pub struct EmEnv { - memory: Arc>, + memory: Arc>>, data: Arc>, } @@ -86,21 +86,19 @@ impl WasmerEnv for EmEnv { impl EmEnv { pub fn new(data: &EmscriptenGlobalsData, mapped_dirs: HashMap) -> Self { Self { - memory: Arc::new(None), + memory: Arc::new(RwLock::new(None)), data: Arc::new(Mutex::new(EmscriptenData::new(data.clone(), mapped_dirs))), } } pub fn set_memory(&mut self, memory: Memory) { - let ptr = Arc::as_ptr(&self.memory) as *mut _; - unsafe { - *ptr = Some(memory); - } + let mut w = self.memory.write().unwrap(); + *w = Some(memory); } /// Get a reference to the memory - pub fn memory(&self, _mem_idx: u32) -> &Memory { - (*self.memory).as_ref().unwrap() + pub fn memory(&self, _mem_idx: u32) -> Memory { + (&*self.memory.read().unwrap()).as_ref().cloned().unwrap() } } diff --git a/lib/emscripten/src/syscalls/mod.rs b/lib/emscripten/src/syscalls/mod.rs index 1922e249643..5210da76468 100644 --- a/lib/emscripten/src/syscalls/mod.rs +++ b/lib/emscripten/src/syscalls/mod.rs @@ -356,8 +356,9 @@ pub fn ___syscall183(ctx: &EmEnv, _which: c_int, mut varargs: VarArgs) -> i32 { let path = get_current_directory(ctx); let path_string = path.unwrap().display().to_string(); let len = path_string.len(); + let memory = ctx.memory(0); - let buf_writer = buf_offset.deref(ctx.memory(0), 0, len as u32 + 1).unwrap(); + let buf_writer = buf_offset.deref(&memory, 0, len as u32 + 1).unwrap(); for (i, byte) in path_string.bytes().enumerate() { buf_writer[i].set(byte as _); } @@ -411,8 +412,9 @@ pub fn ___syscall140(ctx: &EmEnv, _which: i32, mut varargs: VarArgs) -> i32 { let whence: i32 = varargs.get(ctx); let offset = offset_low; let ret = unsafe { lseek(fd, offset as _, whence) as i64 }; + let memory = ctx.memory(0); - let result_ptr = result_ptr_value.deref(ctx.memory(0)).unwrap(); + let result_ptr = result_ptr_value.deref(&memory).unwrap(); result_ptr.set(ret); debug!( diff --git a/lib/emscripten/src/syscalls/unix.rs b/lib/emscripten/src/syscalls/unix.rs index cf39590edb0..69ac5a2f4eb 100644 --- a/lib/emscripten/src/syscalls/unix.rs +++ b/lib/emscripten/src/syscalls/unix.rs @@ -515,6 +515,7 @@ pub fn ___syscall102(ctx: &EmEnv, _which: c_int, mut varargs: VarArgs) -> c_int debug!("emscripten::___syscall102 (socketcall) {}", _which); let call: u32 = varargs.get(ctx); let mut socket_varargs: VarArgs = varargs.get(ctx); + let memory = ctx.memory(0); // migrating to EmSockAddr, port being separate here is nice, should update that too #[repr(C)] @@ -579,7 +580,7 @@ pub fn ___syscall102(ctx: &EmEnv, _which: c_int, mut varargs: VarArgs) -> c_int let socket = socket_varargs.get(ctx); let address: u32 = socket_varargs.get(ctx); let address_len = socket_varargs.get(ctx); - let address = emscripten_memory_pointer!(ctx.memory(0), address) as *mut sockaddr; + let address = emscripten_memory_pointer!(&memory, address) as *mut sockaddr; // Debug received address let _proper_address = address as *const GuestSockaddrIn; @@ -604,7 +605,7 @@ pub fn ___syscall102(ctx: &EmEnv, _which: c_int, mut varargs: VarArgs) -> c_int let socket = socket_varargs.get(ctx); let address: u32 = socket_varargs.get(ctx); let address_len = socket_varargs.get(ctx); - let address = emscripten_memory_pointer!(ctx.memory(0), address) as *mut sockaddr; + let address = emscripten_memory_pointer!(&memory, address) as *mut sockaddr; unsafe { connect(socket, address, address_len) } } 4 => { @@ -629,11 +630,10 @@ pub fn ___syscall102(ctx: &EmEnv, _which: c_int, mut varargs: VarArgs) -> c_int debug!( "=> socket: {}, address: {:?}, address_len: {}", socket, - address.deref(ctx.memory(0)).unwrap().get(), - address_len.deref(ctx.memory(0)).unwrap().get() + address.deref(&memory).unwrap().get(), + address_len.deref(&memory).unwrap().get() ); - let address_len_addr = - unsafe { address_len.deref_mut(ctx.memory(0)).unwrap().get_mut() }; + let address_len_addr = unsafe { address_len.deref_mut(&memory).unwrap().get_mut() }; // let mut address_len_addr: socklen_t = 0; let mut host_address: sockaddr = sockaddr { @@ -643,7 +643,7 @@ pub fn ___syscall102(ctx: &EmEnv, _which: c_int, mut varargs: VarArgs) -> c_int sa_len: Default::default(), }; let fd = unsafe { accept(socket, &mut host_address, address_len_addr) }; - let address_addr = unsafe { address.deref_mut(ctx.memory(0)).unwrap().get_mut() }; + let address_addr = unsafe { address.deref_mut(&memory).unwrap().get_mut() }; address_addr.sa_family = host_address.sa_family as _; address_addr.sa_data = host_address.sa_data; @@ -667,8 +667,7 @@ pub fn ___syscall102(ctx: &EmEnv, _which: c_int, mut varargs: VarArgs) -> c_int let socket: i32 = socket_varargs.get(ctx); let address: WasmPtr = socket_varargs.get(ctx); let address_len: WasmPtr = socket_varargs.get(ctx); - let address_len_addr = - unsafe { address_len.deref_mut(ctx.memory(0)).unwrap().get_mut() }; + let address_len_addr = unsafe { address_len.deref_mut(&memory).unwrap().get_mut() }; let mut sock_addr_host: sockaddr = sockaddr { sa_family: Default::default(), @@ -684,7 +683,7 @@ pub fn ___syscall102(ctx: &EmEnv, _which: c_int, mut varargs: VarArgs) -> c_int ) }; // translate from host data into emscripten data - let mut address_mut = unsafe { address.deref_mut(ctx.memory(0)).unwrap().get_mut() }; + let mut address_mut = unsafe { address.deref_mut(&memory).unwrap().get_mut() }; address_mut.sa_family = sock_addr_host.sa_family as _; address_mut.sa_data = sock_addr_host.sa_data; @@ -701,9 +700,9 @@ pub fn ___syscall102(ctx: &EmEnv, _which: c_int, mut varargs: VarArgs) -> c_int let socket = socket_varargs.get(ctx); let address: u32 = socket_varargs.get(ctx); let address_len: u32 = socket_varargs.get(ctx); - let address = emscripten_memory_pointer!(ctx.memory(0), address) as *mut sockaddr; + let address = emscripten_memory_pointer!(memory, address) as *mut sockaddr; let address_len_addr = - emscripten_memory_pointer!(ctx.memory(0), address_len) as *mut socklen_t; + emscripten_memory_pointer!(memory, address_len) as *mut socklen_t; unsafe { getpeername(socket, address, address_len_addr) } } 11 => { @@ -715,8 +714,8 @@ pub fn ___syscall102(ctx: &EmEnv, _which: c_int, mut varargs: VarArgs) -> c_int let len: i32 = socket_varargs.get(ctx); let address: u32 = socket_varargs.get(ctx); let address_len = socket_varargs.get(ctx); - let buf_addr = emscripten_memory_pointer!(ctx.memory(0), buf) as _; - let address = emscripten_memory_pointer!(ctx.memory(0), address) as *mut sockaddr; + let buf_addr = emscripten_memory_pointer!(memory, buf) as _; + let address = emscripten_memory_pointer!(memory, address) as *mut sockaddr; unsafe { sendto(socket, buf_addr, flags, len, address, address_len) as i32 } } 12 => { @@ -728,10 +727,10 @@ pub fn ___syscall102(ctx: &EmEnv, _which: c_int, mut varargs: VarArgs) -> c_int let flags: i32 = socket_varargs.get(ctx); let address: u32 = socket_varargs.get(ctx); let address_len: u32 = socket_varargs.get(ctx); - let buf_addr = emscripten_memory_pointer!(ctx.memory(0), buf) as _; - let address = emscripten_memory_pointer!(ctx.memory(0), address) as *mut sockaddr; + let buf_addr = emscripten_memory_pointer!(memory, buf) as _; + let address = emscripten_memory_pointer!(memory, address) as *mut sockaddr; let address_len_addr = - emscripten_memory_pointer!(ctx.memory(0), address_len) as *mut socklen_t; + emscripten_memory_pointer!(memory, address_len) as *mut socklen_t; unsafe { recvfrom( socket, @@ -755,8 +754,7 @@ pub fn ___syscall102(ctx: &EmEnv, _which: c_int, mut varargs: VarArgs) -> c_int let untranslated_name: i32 = socket_varargs.get(ctx); let value: u32 = socket_varargs.get(ctx); let option_len: u32 = socket_varargs.get(ctx); - let value_addr = - emscripten_memory_pointer!(ctx.memory(0), value) as *const libc::c_void; + let value_addr = emscripten_memory_pointer!(memory, value) as *const libc::c_void; let name: i32 = translate_socket_name_flag(untranslated_name); let ret = unsafe { setsockopt(socket, level, name, value_addr, option_len) }; @@ -774,9 +772,8 @@ pub fn ___syscall102(ctx: &EmEnv, _which: c_int, mut varargs: VarArgs) -> c_int let name: i32 = translate_socket_name_flag(untranslated_name); let value: u32 = socket_varargs.get(ctx); let option_len: u32 = socket_varargs.get(ctx); - let value_addr = emscripten_memory_pointer!(ctx.memory(0), value) as _; - let option_len_addr = - emscripten_memory_pointer!(ctx.memory(0), option_len) as *mut socklen_t; + let value_addr = emscripten_memory_pointer!(memory, value) as _; + let option_len_addr = emscripten_memory_pointer!(memory, option_len) as *mut socklen_t; unsafe { getsockopt(socket, level, name, value_addr, option_len_addr) } } 16 => { @@ -785,7 +782,7 @@ pub fn ___syscall102(ctx: &EmEnv, _which: c_int, mut varargs: VarArgs) -> c_int let socket: i32 = socket_varargs.get(ctx); let msg: u32 = socket_varargs.get(ctx); let flags: i32 = socket_varargs.get(ctx); - let msg_addr = emscripten_memory_pointer!(ctx.memory(0), msg) as *const msghdr; + let msg_addr = emscripten_memory_pointer!(memory, msg) as *const msghdr; unsafe { sendmsg(socket, msg_addr, flags) as i32 } } 17 => { @@ -794,7 +791,7 @@ pub fn ___syscall102(ctx: &EmEnv, _which: c_int, mut varargs: VarArgs) -> c_int let socket: i32 = socket_varargs.get(ctx); let msg: u32 = socket_varargs.get(ctx); let flags: i32 = socket_varargs.get(ctx); - let msg_addr = emscripten_memory_pointer!(ctx.memory(0), msg) as *mut msghdr; + let msg_addr = emscripten_memory_pointer!(memory, msg) as *mut msghdr; unsafe { recvmsg(socket, msg_addr, flags) as i32 } } _ => { @@ -858,8 +855,9 @@ pub fn ___syscall168(ctx: &EmEnv, _which: i32, mut varargs: VarArgs) -> i32 { let fds: WasmPtr = varargs.get(ctx); let nfds: u32 = varargs.get(ctx); let timeout: i32 = varargs.get(ctx); + let memory = ctx.memory(0); - let fds_mut = unsafe { fds.deref_mut(ctx.memory(0)).unwrap().get_mut() }; + let fds_mut = unsafe { fds.deref_mut(&memory).unwrap().get_mut() }; let ret = unsafe { libc::poll( diff --git a/lib/emscripten/src/syscalls/windows.rs b/lib/emscripten/src/syscalls/windows.rs index abd2ad94aad..169b5f836f6 100644 --- a/lib/emscripten/src/syscalls/windows.rs +++ b/lib/emscripten/src/syscalls/windows.rs @@ -27,6 +27,8 @@ pub fn ___syscall5(ctx: &EmEnv, which: c_int, mut varargs: VarArgs) -> c_int { let flags: i32 = varargs.get(ctx); let mode: u32 = varargs.get(ctx); let path_str = unsafe { std::ffi::CStr::from_ptr(real_path).to_str().unwrap() }; + let memory = ctx.memory(0); + match path_str { "/dev/urandom" => { // create a fake urandom file for windows, super hacky @@ -44,7 +46,7 @@ pub fn ___syscall5(ctx: &EmEnv, which: c_int, mut varargs: VarArgs) -> c_int { // put the file path string into wasm memory let urandom_file_offset = unsafe { copy_cstr_into_wasm(ctx, ptr) }; let raw_pointer_to_urandom_file = - emscripten_memory_pointer!(ctx.memory(0), urandom_file_offset) as *const i8; + emscripten_memory_pointer!(&memory, urandom_file_offset) as *const i8; let fd = unsafe { open(raw_pointer_to_urandom_file, flags, mode) }; debug!( "=> pathname: {}, flags: {}, mode: {} = fd: {}", From 065bfb35bc16e825baf574d05a9650e2b2134f51 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Wed, 14 Apr 2021 07:56:12 -0700 Subject: [PATCH 2/2] Fix emscripten table being init'd with externref::null --- lib/emscripten/src/lib.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/emscripten/src/lib.rs b/lib/emscripten/src/lib.rs index 6a4251fbf88..0785627d8a1 100644 --- a/lib/emscripten/src/lib.rs +++ b/lib/emscripten/src/lib.rs @@ -475,8 +475,7 @@ impl EmscriptenGlobals { minimum: table_min, maximum: table_max, }; - // TODO: review init value - let table = Table::new(store, table_type, Val::null()).unwrap(); + let table = Table::new(store, table_type, Val::FuncRef(None)).unwrap(); let data = { let static_bump = STATIC_BUMP;