author | Jeff Walden <jwalden@mit.edu> |
Tue, 18 Aug 2020 20:50:18 +0000 | |
changeset 609849 | deb00e023f3b91410ce7bcc1897c9eb6e30ff465 |
parent 609848 | eeff6597f16f39192c4e05c5155b09163b719aa6 |
child 609850 | 254c794085e14436bb27255a528e8f940b20193d |
push id | 13553 |
push user | ffxbld-merge |
push date | Mon, 24 Aug 2020 12:51:36 +0000 |
treeherder | mozilla-beta@a54f8b5d0977 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | arai |
bugs | 1502355 |
milestone | 81.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
js/src/vm/Compartment-inl.h | file | annotate | diff | comparison | revisions | |
js/src/vm/JSObject.h | file | annotate | diff | comparison | revisions |
--- a/js/src/vm/Compartment-inl.h +++ b/js/src/vm/Compartment-inl.h @@ -18,16 +18,18 @@ #include "gc/Marking.h" #include "js/CallArgs.h" #include "js/Wrapper.h" #include "vm/Iteration.h" #include "vm/JSObject.h" #include "vm/JSContext-inl.h" +struct JSClass; + inline js::StringWrapperMap::Ptr JS::Compartment::lookupWrapper( JSString* str) const { return zone()->crossZoneStringWrappers().lookup(str); } inline bool JS::Compartment::wrap(JSContext* cx, JS::MutableHandleValue vp) { /* Only GC things have to be wrapped or copied. */ if (!vp.isGCThing()) { @@ -147,40 +149,91 @@ MOZ_MUST_USE T* UnwrapAndTypeCheckValueS if (!obj || !obj->is<T>()) { throwTypeError(); return nullptr; } return &obj->as<T>(); } +template <class ErrorCallback> +MOZ_MUST_USE JSObject* UnwrapAndTypeCheckValueSlowPath( + JSContext* cx, HandleValue value, const JSClass* clasp, + ErrorCallback throwTypeError) { + JSObject* obj = nullptr; + if (value.isObject()) { + obj = &value.toObject(); + if (IsWrapper(obj)) { + obj = CheckedUnwrapStatic(obj); + if (!obj) { + ReportAccessDenied(cx); + return nullptr; + } + } + } + + if (!obj || !obj->hasClass(clasp)) { + throwTypeError(); + return nullptr; + } + + return obj; +} + } // namespace detail /** * Remove all wrappers from `val` and try to downcast the result to class `T`. * * DANGER: The result may not be same-compartment with `cx`. * * This calls `throwTypeError` if the value isn't an object, cannot be * unwrapped, or isn't an instance of the expected type. `throwTypeError` must * in fact throw a TypeError (or OOM trying). */ template <class T, class ErrorCallback> inline MOZ_MUST_USE T* UnwrapAndTypeCheckValue(JSContext* cx, HandleValue value, ErrorCallback throwTypeError) { + cx->check(value); + static_assert(!std::is_convertible_v<T*, Wrapper*>, "T can't be a Wrapper type; this function discards wrappers"); - cx->check(value); + if (value.isObject() && value.toObject().is<T>()) { return &value.toObject().as<T>(); } + return detail::UnwrapAndTypeCheckValueSlowPath<T>(cx, value, throwTypeError); } /** + * Remove all wrappers from |val| and try to downcast the result to an object of + * the class |clasp|. + * + * DANGER: The result may not be same-compartment with |cx|. + * + * This calls |throwTypeError| if the value isn't an object, cannot be + * unwrapped, or isn't an instance of the expected type. |throwTypeError| must + * in fact throw a TypeError (or OOM trying). + */ +template <class ErrorCallback> +inline MOZ_MUST_USE JSObject* UnwrapAndTypeCheckValue( + JSContext* cx, HandleValue value, const JSClass* clasp, + ErrorCallback throwTypeError) { + cx->check(value); + + if (value.isObject() && value.toObject().hasClass(clasp)) { + return &value.toObject(); + } + + return detail::UnwrapAndTypeCheckValueSlowPath(cx, value, clasp, + throwTypeError); +} + +/** * Remove all wrappers from `args.thisv()` and try to downcast the result to * class `T`. * * DANGER: The result may not be same-compartment with `cx`. * * This throws a TypeError if the value isn't an object, cannot be unwrapped, * or isn't an instance of the expected type. */ @@ -229,17 +282,17 @@ inline MOZ_MUST_USE T* UnwrapAndTypeChec * `obj` is a wrapper for such an object, this tries to unwrap the object and * return a pointer to it. If access is denied, or `obj` was a wrapper but has * been nuked, this reports an error and returns null. * * In all other cases, the behavior is undefined, so call this only if `obj` is * known to have been an object of class T, or a wrapper to a T, at some point. */ template <class T> -MOZ_MUST_USE T* UnwrapAndDowncastObject(JSContext* cx, JSObject* obj) { +inline MOZ_MUST_USE T* UnwrapAndDowncastObject(JSContext* cx, JSObject* obj) { static_assert(!std::is_convertible_v<T*, Wrapper*>, "T can't be a Wrapper type; this function discards wrappers"); if (IsProxy(obj)) { if (JS_IsDeadWrapper(obj)) { JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT); return nullptr; @@ -253,25 +306,70 @@ MOZ_MUST_USE T* UnwrapAndDowncastObject( return nullptr; } } return &obj->as<T>(); } /** + * Unwrap an object of a known (but not compile-time-known) class. + * + * If |obj| is an object with class |clasp|, this returns |obj|. If |obj| is a + * wrapper for such an object, this tries to unwrap the object and return a + * pointer to it. If access is denied, or |obj| was a wrapper but has been + * nuked, this reports an error and returns null. + * + * In all other cases, the behavior is undefined, so call this only if |obj| is + * known to have had class |clasp|, or been a wrapper to such an object, at some + * point. + */ +inline MOZ_MUST_USE JSObject* UnwrapAndDowncastObject(JSContext* cx, + JSObject* obj, + const JSClass* clasp) { + if (IsProxy(obj)) { + if (JS_IsDeadWrapper(obj)) { + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, + JSMSG_DEAD_OBJECT); + return nullptr; + } + + // It would probably be OK to do an unchecked unwrap here, but we allow + // arbitrary security policies, so check anyway. + obj = obj->maybeUnwrapAs(clasp); + if (!obj) { + ReportAccessDenied(cx); + return nullptr; + } + } + + MOZ_ASSERT(obj->hasClass(clasp)); + return obj; +} + +/** * Unwrap a value of a known type. See UnwrapAndDowncastObject. */ template <class T> inline MOZ_MUST_USE T* UnwrapAndDowncastValue(JSContext* cx, const Value& value) { return UnwrapAndDowncastObject<T>(cx, &value.toObject()); } /** + * Unwrap an object of a known (but not compile-time-known) class. See + * UnwrapAndDowncastObject. + */ +inline MOZ_MUST_USE JSObject* UnwrapAndDowncastValue(JSContext* cx, + const Value& value, + const JSClass* clasp) { + return UnwrapAndDowncastObject(cx, &value.toObject(), clasp); +} + +/** * Read a private slot that is known to point to a particular type of object. * * Some internal slots specified in various standards effectively have static * types. For example, the [[ownerReadableStream]] slot of a stream reader is * guaranteed to be a ReadableStream. However, because of compartments, we * sometimes store a cross-compartment wrapper in that slot. And since wrappers * can be nuked, that wrapper may become a dead object proxy. *
--- a/js/src/vm/JSObject.h +++ b/js/src/vm/JSObject.h @@ -542,21 +542,29 @@ class JSObject * whether js::Nuke* could have been called in the meantime, check again. */ template <class T> T& unwrapAs(); /* * Tries to unwrap and downcast to class T. Returns nullptr if (and only if) a * wrapper with a security policy is involved. Crashes in all builds if the - * (possibly unwrapped) object is not of class T (for example because it's a + * (possibly unwrapped) object is not of class T (for example, because it's a * dead wrapper). */ template <class T> - T* maybeUnwrapAs(); + inline T* maybeUnwrapAs(); + + /* + * Tries to unwrap and downcast to an object with class |clasp|. Returns + * nullptr if (and only if) a wrapper with a security policy is involved. + * Crashes in all builds if the (possibly unwrapped) object doesn't have class + * |clasp| (for example, because it's a dead wrapper). + */ + inline JSObject* maybeUnwrapAs(const JSClass* clasp); /* * Tries to unwrap and downcast to class T. Returns nullptr if a wrapper with * a security policy is involved or if the object does not have class T. */ template <class T> T* maybeUnwrapIf(); @@ -640,17 +648,17 @@ T& JSObject::unwrapAs() { // CheckedUnwrap, this does not need to repeat the security check. JSObject* unwrapped = js::UncheckedUnwrap(this); MOZ_ASSERT(js::CheckedUnwrapStatic(this) == unwrapped, "check that the security check we skipped really is redundant"); return unwrapped->as<T>(); } template <class T> -T* JSObject::maybeUnwrapAs() { +inline T* JSObject::maybeUnwrapAs() { static_assert(!std::is_convertible_v<T*, js::Wrapper*>, "T can't be a Wrapper type; this function discards wrappers"); if (is<T>()) { return &as<T>(); } JSObject* unwrapped = js::CheckedUnwrapStatic(this); @@ -660,16 +668,33 @@ T* JSObject::maybeUnwrapAs() { if (MOZ_LIKELY(unwrapped->is<T>())) { return &unwrapped->as<T>(); } MOZ_CRASH("Invalid object. Dead wrapper?"); } +inline JSObject* JSObject::maybeUnwrapAs(const JSClass* clasp) { + if (hasClass(clasp)) { + return this; + } + + JSObject* unwrapped = js::CheckedUnwrapStatic(this); + if (!unwrapped) { + return nullptr; + } + + if (MOZ_LIKELY(unwrapped->hasClass(clasp))) { + return unwrapped; + } + + MOZ_CRASH("Invalid object. Dead wrapper?"); +} + template <class T> T* JSObject::maybeUnwrapIf() { static_assert(!std::is_convertible_v<T*, js::Wrapper*>, "T can't be a Wrapper type; this function discards wrappers"); if (is<T>()) { return &as<T>(); }