script: make Error::to_jsval safe (#35411)

Signed-off-by: Stephen Muss <stephenmuss@gmail.com>
This commit is contained in:
Stephen Muss 2025-02-11 10:47:31 +11:00 committed by GitHub
parent 8486e585f5
commit cb588bab6a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 41 additions and 64 deletions

View file

@ -11,9 +11,7 @@ use backtrace::Backtrace;
use js::error::{throw_range_error, throw_type_error}; use js::error::{throw_range_error, throw_type_error};
#[cfg(feature = "js_backtrace")] #[cfg(feature = "js_backtrace")]
use js::jsapi::StackFormat as JSStackFormat; use js::jsapi::StackFormat as JSStackFormat;
use js::jsapi::{ use js::jsapi::{ExceptionStackBehavior, JS_ClearPendingException, JS_IsExceptionPending};
ExceptionStackBehavior, JSContext, JS_ClearPendingException, JS_IsExceptionPending,
};
use js::jsval::UndefinedValue; use js::jsval::UndefinedValue;
use js::rust::wrappers::{JS_ErrorFromException, JS_GetPendingException, JS_SetPendingException}; use js::rust::wrappers::{JS_ErrorFromException, JS_GetPendingException, JS_SetPendingException};
use js::rust::{HandleObject, HandleValue, MutableHandleValue}; use js::rust::{HandleObject, HandleValue, MutableHandleValue};
@ -341,19 +339,21 @@ pub(crate) fn throw_constructor_without_new(cx: SafeJSContext, name: &str) {
impl Error { impl Error {
/// Convert this error value to a JS value, consuming it in the process. /// Convert this error value to a JS value, consuming it in the process.
#[allow(clippy::wrong_self_convention)] #[allow(clippy::wrong_self_convention)]
pub(crate) unsafe fn to_jsval( pub(crate) fn to_jsval(
self, self,
cx: *mut JSContext, cx: SafeJSContext,
global: &GlobalScope, global: &GlobalScope,
rval: MutableHandleValue, rval: MutableHandleValue,
) { ) {
match self { match self {
Error::JSFailed => (), Error::JSFailed => (),
_ => assert!(!JS_IsExceptionPending(cx)), _ => unsafe { assert!(!JS_IsExceptionPending(*cx)) },
}
throw_dom_exception(cx, global, self);
unsafe {
assert!(JS_IsExceptionPending(*cx));
assert!(JS_GetPendingException(*cx, rval));
JS_ClearPendingException(*cx);
} }
throw_dom_exception(SafeJSContext::from_ptr(cx), global, self);
assert!(JS_IsExceptionPending(cx));
assert!(JS_GetPendingException(cx, rval));
JS_ClearPendingException(cx);
} }
} }

View file

@ -219,14 +219,11 @@ impl Promise {
self.reject(cx, v.handle()); self.reject(cx, v.handle());
} }
#[allow(unsafe_code)]
pub(crate) fn reject_error(&self, error: Error) { pub(crate) fn reject_error(&self, error: Error) {
let cx = GlobalScope::get_cx(); let cx = GlobalScope::get_cx();
let _ac = enter_realm(self); let _ac = enter_realm(self);
rooted!(in(*cx) let mut v = UndefinedValue()); rooted!(in(*cx) let mut v = UndefinedValue());
unsafe { error.to_jsval(cx, &self.global(), v.handle_mut());
error.to_jsval(*cx, &self.global(), v.handle_mut());
}
self.reject(cx, v.handle()); self.reject(cx, v.handle());
} }

View file

@ -417,11 +417,10 @@ impl ReadableStream {
/// <https://streams.spec.whatwg.org/#readable-stream-error> /// <https://streams.spec.whatwg.org/#readable-stream-error>
/// Note: in other use cases this call happens via the controller. /// Note: in other use cases this call happens via the controller.
#[allow(unsafe_code)]
pub(crate) fn error_native(&self, error: Error) { pub(crate) fn error_native(&self, error: Error) {
let cx = GlobalScope::get_cx(); let cx = GlobalScope::get_cx();
rooted!(in(*cx) let mut error_val = UndefinedValue()); rooted!(in(*cx) let mut error_val = UndefinedValue());
unsafe { error.to_jsval(*cx, &self.global(), error_val.handle_mut()) }; error.to_jsval(cx, &self.global(), error_val.handle_mut());
self.error(error_val.handle()); self.error(error_val.handle());
} }

View file

@ -153,20 +153,17 @@ impl ReadableStreamBYOBReader {
} }
/// <https://streams.spec.whatwg.org/#abstract-opdef-readablestreambyobreaderrelease> /// <https://streams.spec.whatwg.org/#abstract-opdef-readablestreambyobreaderrelease>
#[allow(unsafe_code)]
pub(crate) fn release(&self) -> Fallible<()> { pub(crate) fn release(&self) -> Fallible<()> {
// Perform ! ReadableStreamReaderGenericRelease(reader). // Perform ! ReadableStreamReaderGenericRelease(reader).
self.generic_release()?; self.generic_release()?;
// Let e be a new TypeError exception. // Let e be a new TypeError exception.
let cx = GlobalScope::get_cx(); let cx = GlobalScope::get_cx();
rooted!(in(*cx) let mut error = UndefinedValue()); rooted!(in(*cx) let mut error = UndefinedValue());
unsafe {
Error::Type("Reader is released".to_owned()).to_jsval( Error::Type("Reader is released".to_owned()).to_jsval(
*cx, cx,
&self.global(), &self.global(),
error.handle_mut(), error.handle_mut(),
) );
};
// Perform ! ReadableStreamBYOBReaderErrorReadIntoRequests(reader, e). // Perform ! ReadableStreamBYOBReaderErrorReadIntoRequests(reader, e).
self.error_read_into_requests(error.handle()); self.error_read_into_requests(error.handle());

View file

@ -450,7 +450,6 @@ impl ReadableStreamDefaultController {
} }
/// <https://streams.spec.whatwg.org/#readable-stream-default-controller-call-pull-if-needed> /// <https://streams.spec.whatwg.org/#readable-stream-default-controller-call-pull-if-needed>
#[allow(unsafe_code)]
fn call_pull_if_needed(&self, can_gc: CanGc) { fn call_pull_if_needed(&self, can_gc: CanGc) {
if !self.should_call_pull() { if !self.should_call_pull() {
return; return;
@ -500,11 +499,9 @@ impl ReadableStreamDefaultController {
let cx = GlobalScope::get_cx(); let cx = GlobalScope::get_cx();
rooted!(in(*cx) let mut rval = UndefinedValue()); rooted!(in(*cx) let mut rval = UndefinedValue());
// TODO: check if `self.global()` is the right globalscope. // TODO: check if `self.global()` is the right globalscope.
unsafe {
error error
.clone() .clone()
.to_jsval(*cx, &self.global(), rval.handle_mut()) .to_jsval(cx, &self.global(), rval.handle_mut());
};
let promise = Promise::new(&global, can_gc); let promise = Promise::new(&global, can_gc);
promise.reject_native(&rval.handle()); promise.reject_native(&rval.handle());
promise promise
@ -513,7 +510,6 @@ impl ReadableStreamDefaultController {
} }
/// <https://streams.spec.whatwg.org/#rs-default-controller-private-cancel> /// <https://streams.spec.whatwg.org/#rs-default-controller-private-cancel>
#[allow(unsafe_code)]
pub(crate) fn perform_cancel_steps( pub(crate) fn perform_cancel_steps(
&self, &self,
reason: SafeHandleValue, reason: SafeHandleValue,
@ -540,11 +536,9 @@ impl ReadableStreamDefaultController {
let cx = GlobalScope::get_cx(); let cx = GlobalScope::get_cx();
rooted!(in(*cx) let mut rval = UndefinedValue()); rooted!(in(*cx) let mut rval = UndefinedValue());
// TODO: check if `self.global()` is the right globalscope. // TODO: check if `self.global()` is the right globalscope.
unsafe {
error error
.clone() .clone()
.to_jsval(*cx, &self.global(), rval.handle_mut()) .to_jsval(cx, &self.global(), rval.handle_mut());
};
let promise = Promise::new(&global, can_gc); let promise = Promise::new(&global, can_gc);
promise.reject_native(&rval.handle()); promise.reject_native(&rval.handle());
promise promise

View file

@ -245,20 +245,17 @@ impl ReadableStreamDefaultReader {
} }
/// <https://streams.spec.whatwg.org/#abstract-opdef-readablestreamdefaultreaderrelease> /// <https://streams.spec.whatwg.org/#abstract-opdef-readablestreamdefaultreaderrelease>
#[allow(unsafe_code)]
pub(crate) fn release(&self) -> Fallible<()> { pub(crate) fn release(&self) -> Fallible<()> {
// Perform ! ReadableStreamReaderGenericRelease(reader). // Perform ! ReadableStreamReaderGenericRelease(reader).
self.generic_release()?; self.generic_release()?;
// Let e be a new TypeError exception. // Let e be a new TypeError exception.
let cx = GlobalScope::get_cx(); let cx = GlobalScope::get_cx();
rooted!(in(*cx) let mut error = UndefinedValue()); rooted!(in(*cx) let mut error = UndefinedValue());
unsafe {
Error::Type("Reader is released".to_owned()).to_jsval( Error::Type("Reader is released".to_owned()).to_jsval(
*cx, cx,
&self.global(), &self.global(),
error.handle_mut(), error.handle_mut(),
) );
};
// Perform ! ReadableStreamDefaultReaderErrorReadRequests(reader, e). // Perform ! ReadableStreamDefaultReaderErrorReadRequests(reader, e).
self.error_read_requests(error.handle()); self.error_read_requests(error.handle());
@ -363,19 +360,16 @@ impl ReadableStreamDefaultReaderMethods<crate::DomTypeHolder> for ReadableStream
} }
/// <https://streams.spec.whatwg.org/#default-reader-read> /// <https://streams.spec.whatwg.org/#default-reader-read>
#[allow(unsafe_code)]
fn Read(&self, can_gc: CanGc) -> Rc<Promise> { fn Read(&self, can_gc: CanGc) -> Rc<Promise> {
// If this.[[stream]] is undefined, return a promise rejected with a TypeError exception. // If this.[[stream]] is undefined, return a promise rejected with a TypeError exception.
if self.stream.get().is_none() { if self.stream.get().is_none() {
let cx = GlobalScope::get_cx(); let cx = GlobalScope::get_cx();
rooted!(in(*cx) let mut error = UndefinedValue()); rooted!(in(*cx) let mut error = UndefinedValue());
unsafe {
Error::Type("stream is undefined".to_owned()).to_jsval( Error::Type("stream is undefined".to_owned()).to_jsval(
*cx, cx,
&self.global(), &self.global(),
error.handle_mut(), error.handle_mut(),
) );
};
return Promise::new_rejected(&self.global(), cx, error.handle()).unwrap(); return Promise::new_rejected(&self.global(), cx, error.handle()).unwrap();
} }
// Let promise be a new promise. // Let promise be a new promise.

View file

@ -101,13 +101,11 @@ pub(crate) trait ReadableStreamGenericReader {
// Otherwise, set reader.[[closedPromise]] to a promise rejected with a TypeError exception. // Otherwise, set reader.[[closedPromise]] to a promise rejected with a TypeError exception.
let cx = GlobalScope::get_cx(); let cx = GlobalScope::get_cx();
rooted!(in(*cx) let mut error = UndefinedValue()); rooted!(in(*cx) let mut error = UndefinedValue());
unsafe {
Error::Type("Cannot release lock due to stream state.".to_owned()).to_jsval( Error::Type("Cannot release lock due to stream state.".to_owned()).to_jsval(
*cx, cx,
&stream.global(), &stream.global(),
error.handle_mut(), error.handle_mut(),
) );
};
self.set_closed_promise( self.set_closed_promise(
Promise::new_rejected(&stream.global(), cx, error.handle()).unwrap(), Promise::new_rejected(&stream.global(), cx, error.handle()).unwrap(),

View file

@ -76,10 +76,9 @@ use crate::realms::{enter_realm, AlreadyInRealm, InRealm};
use crate::script_runtime::{CanGc, JSContext as SafeJSContext}; use crate::script_runtime::{CanGc, JSContext as SafeJSContext};
use crate::task::TaskBox; use crate::task::TaskBox;
#[allow(unsafe_code)] fn gen_type_error(global: &GlobalScope, string: String) -> RethrowError {
unsafe fn gen_type_error(global: &GlobalScope, string: String) -> RethrowError {
rooted!(in(*GlobalScope::get_cx()) let mut thrown = UndefinedValue()); rooted!(in(*GlobalScope::get_cx()) let mut thrown = UndefinedValue());
Error::Type(string).to_jsval(*GlobalScope::get_cx(), global, thrown.handle_mut()); Error::Type(string).to_jsval(GlobalScope::get_cx(), global, thrown.handle_mut());
RethrowError(RootedTraceableBox::from_box(Heap::boxed(thrown.get()))) RethrowError(RootedTraceableBox::from_box(Heap::boxed(thrown.get())))
} }
@ -1428,8 +1427,7 @@ fn fetch_an_import_module_script_graph(
// Step 2. // Step 2.
if url.is_err() { if url.is_err() {
let specifier_error = let specifier_error = gen_type_error(global, "Wrong module specifier".to_owned());
unsafe { gen_type_error(global, "Wrong module specifier".to_owned()) };
return Err(specifier_error); return Err(specifier_error);
} }