From 07d8431f100e8df5f523adf542b47f8b91af3539 Mon Sep 17 00:00:00 2001 From: Aaron O'Mullan Date: Tue, 15 Mar 2022 22:50:17 +0100 Subject: [PATCH] fix(core): nuke Deno.core.ops pre-snapshot (#13970) To avoid OOB & other ExternalReference snapshot serialization issues Co-authored-by: Bert Belder --- core/bindings.rs | 152 ++++++++++++++++++++++------------------------- core/runtime.rs | 32 ++++++++-- 2 files changed, 99 insertions(+), 85 deletions(-) diff --git a/core/bindings.rs b/core/bindings.rs index 2ab55187d7..0a87c0ffa6 100644 --- a/core/bindings.rs +++ b/core/bindings.rs @@ -14,6 +14,7 @@ use crate::PromiseId; use crate::ZeroCopyBuf; use anyhow::Error; use log::debug; +use once_cell::sync::Lazy; use serde::Deserialize; use serde::Serialize; use serde_v8::to_v8; @@ -29,88 +30,77 @@ use v8::SharedArrayBuffer; use v8::ValueDeserializerHelper; use v8::ValueSerializerHelper; -pub fn external_references( - ops: &[OpPair], - op_state: Rc>, -) -> v8::ExternalReferences { - let mut refs = vec![ - v8::ExternalReference { - function: ref_op.map_fn_to(), - }, - v8::ExternalReference { - function: unref_op.map_fn_to(), - }, - v8::ExternalReference { - function: set_macrotask_callback.map_fn_to(), - }, - v8::ExternalReference { - function: set_nexttick_callback.map_fn_to(), - }, - v8::ExternalReference { - function: set_promise_reject_callback.map_fn_to(), - }, - v8::ExternalReference { - function: set_uncaught_exception_callback.map_fn_to(), - }, - v8::ExternalReference { - function: run_microtasks.map_fn_to(), - }, - v8::ExternalReference { - function: has_tick_scheduled.map_fn_to(), - }, - v8::ExternalReference { - function: set_has_tick_scheduled.map_fn_to(), - }, - v8::ExternalReference { - function: eval_context.map_fn_to(), - }, - v8::ExternalReference { - function: queue_microtask.map_fn_to(), - }, - v8::ExternalReference { - function: create_host_object.map_fn_to(), - }, - v8::ExternalReference { - function: encode.map_fn_to(), - }, - v8::ExternalReference { - function: decode.map_fn_to(), - }, - v8::ExternalReference { - function: serialize.map_fn_to(), - }, - v8::ExternalReference { - function: deserialize.map_fn_to(), - }, - v8::ExternalReference { - function: get_promise_details.map_fn_to(), - }, - v8::ExternalReference { - function: get_proxy_details.map_fn_to(), - }, - v8::ExternalReference { - function: is_proxy.map_fn_to(), - }, - v8::ExternalReference { - function: memory_usage.map_fn_to(), - }, - v8::ExternalReference { - function: call_console.map_fn_to(), - }, - v8::ExternalReference { - function: set_wasm_streaming_callback.map_fn_to(), - }, - ]; - let op_refs = ops - .iter() - .map(|(_, opref)| v8::ExternalReference { function: *opref }); - refs.extend(op_refs); - let raw_op_state = Rc::as_ptr(&op_state) as *mut c_void; - refs.push(v8::ExternalReference { - pointer: raw_op_state, +pub static EXTERNAL_REFERENCES: Lazy = + Lazy::new(|| { + v8::ExternalReferences::new(&[ + v8::ExternalReference { + function: ref_op.map_fn_to(), + }, + v8::ExternalReference { + function: unref_op.map_fn_to(), + }, + v8::ExternalReference { + function: set_macrotask_callback.map_fn_to(), + }, + v8::ExternalReference { + function: set_nexttick_callback.map_fn_to(), + }, + v8::ExternalReference { + function: set_promise_reject_callback.map_fn_to(), + }, + v8::ExternalReference { + function: set_uncaught_exception_callback.map_fn_to(), + }, + v8::ExternalReference { + function: run_microtasks.map_fn_to(), + }, + v8::ExternalReference { + function: has_tick_scheduled.map_fn_to(), + }, + v8::ExternalReference { + function: set_has_tick_scheduled.map_fn_to(), + }, + v8::ExternalReference { + function: eval_context.map_fn_to(), + }, + v8::ExternalReference { + function: queue_microtask.map_fn_to(), + }, + v8::ExternalReference { + function: create_host_object.map_fn_to(), + }, + v8::ExternalReference { + function: encode.map_fn_to(), + }, + v8::ExternalReference { + function: decode.map_fn_to(), + }, + v8::ExternalReference { + function: serialize.map_fn_to(), + }, + v8::ExternalReference { + function: deserialize.map_fn_to(), + }, + v8::ExternalReference { + function: get_promise_details.map_fn_to(), + }, + v8::ExternalReference { + function: get_proxy_details.map_fn_to(), + }, + v8::ExternalReference { + function: is_proxy.map_fn_to(), + }, + v8::ExternalReference { + function: memory_usage.map_fn_to(), + }, + v8::ExternalReference { + function: call_console.map_fn_to(), + }, + v8::ExternalReference { + function: set_wasm_streaming_callback.map_fn_to(), + }, + ]) }); - v8::ExternalReferences::new(&refs) -} pub fn script_origin<'a>( s: &mut v8::HandleScope<'a>, diff --git a/core/runtime.rs b/core/runtime.rs index 0190655268..c95ef6a20d 100644 --- a/core/runtime.rs +++ b/core/runtime.rs @@ -297,13 +297,12 @@ impl JsRuntime { let op_state = Rc::new(RefCell::new(op_state)); - let refs = bindings::external_references(&ops, op_state.clone()); - let refs: &'static v8::ExternalReferences = Box::leak(Box::new(refs)); let global_context; let (mut isolate, maybe_snapshot_creator) = if options.will_snapshot { // TODO(ry) Support loading snapshots before snapshotting. assert!(options.startup_snapshot.is_none()); - let mut creator = v8::SnapshotCreator::new(Some(refs)); + let mut creator = + v8::SnapshotCreator::new(Some(&bindings::EXTERNAL_REFERENCES)); let isolate = unsafe { creator.get_owned_isolate() }; let mut isolate = JsRuntime::setup_isolate(isolate); { @@ -319,7 +318,7 @@ impl JsRuntime { .create_params .take() .unwrap_or_else(v8::Isolate::create_params) - .external_references(&**refs); + .external_references(&**bindings::EXTERNAL_REFERENCES); let snapshot_loaded = if let Some(snapshot) = options.startup_snapshot { params = match snapshot { Snapshot::Static(data) => params.snapshot_blob(data), @@ -518,6 +517,18 @@ impl JsRuntime { v8::Global::new(scope, cb) } + fn grab_obj<'s>( + scope: &mut v8::HandleScope<'s>, + code: &str, + ) -> v8::Local<'s, v8::Object> { + let scope = &mut v8::EscapableHandleScope::new(scope); + let code = v8::String::new(scope, code).unwrap(); + let script = v8::Script::compile(scope, code, None).unwrap(); + let v8_value = script.run(scope).unwrap(); + let obj = v8::Local::::try_from(v8_value).unwrap(); + scope.escape(obj) + } + /// Grabs a reference to core.js' opresolve & syncOpsCache() fn init_cbs(&mut self) { let mut scope = self.handle_scope(); @@ -594,6 +605,19 @@ impl JsRuntime { /// be a different type if `RuntimeOptions::js_error_create_fn` has been set. pub fn snapshot(&mut self) -> v8::StartupData { assert!(self.snapshot_creator.is_some()); + + // Nuke Deno.core.ops.* to avoid ExternalReference snapshotting issues + // TODO(@AaronO): make ops stable across snapshots + { + let scope = &mut self.handle_scope(); + let obj = Self::grab_obj(scope, "Deno.core.ops"); + let names = obj.get_own_property_names(scope).unwrap(); + for i in 0..names.length() { + let key = names.get_index(scope, i).unwrap(); + obj.delete(scope, key); + } + } + let state = Self::state(self.v8_isolate()); // Note: create_blob() method must not be called from within a HandleScope.