This is an automated email from the git hooks/post-receive script.
richard pushed a commit to branch tor-browser-91.11.0esr-11.5-1 in repository tor-browser.
commit e8f8421e34d7d33ed129d6942a277785657233dc Author: Jan de Mooij jdemooij@mozilla.com AuthorDate: Tue Jun 7 15:40:44 2022 +0000
Bug 1771084 part 2 - Add Realm option to freeze builtins. r=tcampbell,nika a=RyanVM
Differential Revision: https://phabricator.services.mozilla.com/D147281 --- js/public/Realm.h | 6 +++++ js/public/RealmOptions.h | 10 ++++++++ js/src/jit-test/tests/basic/freeze-builtins.js | 21 ++++++++++++++++ js/src/shell/js.cpp | 10 ++++++++ js/src/vm/GlobalObject.cpp | 33 ++++++++++++++++++++++++-- js/src/vm/Realm.cpp | 17 +++++++++++++ 6 files changed, 95 insertions(+), 2 deletions(-)
diff --git a/js/public/Realm.h b/js/public/Realm.h index c3b0d20901bee..501c8af65f6ff 100644 --- a/js/public/Realm.h +++ b/js/public/Realm.h @@ -94,6 +94,12 @@ extern JS_PUBLIC_API JSObject* GetRealmGlobalOrNull(Realm* realm); // for Number). extern JS_PUBLIC_API bool InitRealmStandardClasses(JSContext* cx);
+// If the current realm has the non-standard freezeBuiltins option set to true, +// freeze the constructor object and seal the prototype. +extern JS_PUBLIC_API bool MaybeFreezeCtorAndPrototype(JSContext* cx, + HandleObject ctor, + HandleObject maybeProto); + /* * Ways to get various per-Realm objects. All the getters declared below operate * on the JSContext's current Realm. diff --git a/js/public/RealmOptions.h b/js/public/RealmOptions.h index 5a1609bb981d7..f57b0e209dd33 100644 --- a/js/public/RealmOptions.h +++ b/js/public/RealmOptions.h @@ -252,6 +252,15 @@ class JS_PUBLIC_API RealmCreationOptions { return *this; }
+ // Non-standard option to freeze certain builtin constructors and seal their + // prototypes. Also defines these constructors on the global as non-writable + // and non-configurable. + bool freezeBuiltins() const { return freezeBuiltins_; } + RealmCreationOptions& setFreezeBuiltins(bool flag) { + freezeBuiltins_ = flag; + return *this; + } + uint64_t profilerRealmID() const { return profilerRealmID_; } RealmCreationOptions& setProfilerRealmID(uint64_t id) { profilerRealmID_ = id; @@ -282,6 +291,7 @@ class JS_PUBLIC_API RealmCreationOptions { bool propertyErrorMessageFix_ = false; bool iteratorHelpers_ = false; bool secureContext_ = false; + bool freezeBuiltins_ = false; };
/** diff --git a/js/src/jit-test/tests/basic/freeze-builtins.js b/js/src/jit-test/tests/basic/freeze-builtins.js new file mode 100644 index 0000000000000..7f59c4b203c61 --- /dev/null +++ b/js/src/jit-test/tests/basic/freeze-builtins.js @@ -0,0 +1,21 @@ +var g = newGlobal({freezeBuiltins: true}); + +g.evaluate("" + function checkFrozen(name) { + // Check constructor on the global is non-writable/non-configurable. + let desc = Object.getOwnPropertyDescriptor(this, name); + assertEq(desc.writable, false); + assertEq(desc.configurable, false); + + // Constructor must be frozen. + let ctor = desc.value; + assertEq(Object.isFrozen(ctor), true); + + // Prototype must be sealed. + if (ctor.prototype) { + assertEq(Object.isSealed(ctor.prototype), true); + } +}); + +g.checkFrozen("Object"); +g.checkFrozen("Array"); +g.checkFrozen("Function"); diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index 24dd27e27f8a2..4ed85b779274f 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -7344,6 +7344,13 @@ static bool NewGlobal(JSContext* cx, unsigned argc, Value* vp) { creationOptions.setCoopAndCoepEnabled(v.toBoolean()); }
+ if (!JS_GetProperty(cx, opts, "freezeBuiltins", &v)) { + return false; + } + if (v.isBoolean()) { + creationOptions.setFreezeBuiltins(v.toBoolean()); + } + // On the web, the SharedArrayBuffer constructor is not installed as a // global property in pages that aren't isolated in a separate process (and // thus can't allow the structured cloning of shared memory). Specify false @@ -9790,6 +9797,9 @@ static const JSFunctionSpecWithHelp shell_functions[] = { " (default false).\n" " useWindowProxy: the global will be created with a WindowProxy attached. In this\n" " case, the WindowProxy will be returned.\n" +" freezeBuiltins: certain builtin constructors will be frozen when created and\n" +" their prototypes will be sealed. These constructors will be defined on the\n" +" global as non-configurable and non-writable.\n" " immutablePrototype: whether the global's prototype is immutable.\n" " principal: if present, its value converted to a number must be an\n" " integer that fits in 32 bits; use that as the new realm's\n" diff --git a/js/src/vm/GlobalObject.cpp b/js/src/vm/GlobalObject.cpp index e60c9f64d8650..842403eed2ff5 100644 --- a/js/src/vm/GlobalObject.cpp +++ b/js/src/vm/GlobalObject.cpp @@ -209,6 +209,27 @@ bool GlobalObject::skipDeselectedConstructor(JSContext* cx, JSProtoKey key) { } }
+static bool ShouldFreezeBuiltin(JSProtoKey key) { + switch (key) { + case JSProto_Object: + case JSProto_Array: + case JSProto_Function: + return true; + default: + return false; + } +} + +static unsigned GetAttrsForResolvedGlobal(GlobalObject* global, + JSProtoKey key) { + unsigned attrs = JSPROP_RESOLVING; + if (global->realm()->creationOptions().freezeBuiltins() && + ShouldFreezeBuiltin(key)) { + attrs |= JSPROP_PERMANENT | JSPROP_READONLY; + } + return attrs; +} + /* static*/ bool GlobalObject::resolveConstructor(JSContext* cx, Handle<GlobalObject*> global, @@ -326,7 +347,8 @@ bool GlobalObject::resolveConstructor(JSContext* cx, if (isObjectOrFunction) { if (clasp->specShouldDefineConstructor()) { RootedValue ctorValue(cx, ObjectValue(*ctor)); - if (!DefineDataProperty(cx, global, id, ctorValue, JSPROP_RESOLVING)) { + unsigned attrs = GetAttrsForResolvedGlobal(global, key); + if (!DefineDataProperty(cx, global, id, ctorValue, attrs)) { return false; } } @@ -371,6 +393,12 @@ bool GlobalObject::resolveConstructor(JSContext* cx, } }
+ if (ShouldFreezeBuiltin(key)) { + if (!JS::MaybeFreezeCtorAndPrototype(cx, ctor, proto)) { + return false; + } + } + if (!isObjectOrFunction) { // Any operations that modifies the global object should be placed // after any other fallible operations. @@ -396,7 +424,8 @@ bool GlobalObject::resolveConstructor(JSContext* cx,
if (shouldReallyDefine) { RootedValue ctorValue(cx, ObjectValue(*ctor)); - if (!DefineDataProperty(cx, global, id, ctorValue, JSPROP_RESOLVING)) { + unsigned attrs = GetAttrsForResolvedGlobal(global, key); + if (!DefineDataProperty(cx, global, id, ctorValue, attrs)) { return false; } } diff --git a/js/src/vm/Realm.cpp b/js/src/vm/Realm.cpp index 1885dec878399..53b7670cda33d 100644 --- a/js/src/vm/Realm.cpp +++ b/js/src/vm/Realm.cpp @@ -760,6 +760,23 @@ JS_PUBLIC_API bool JS::InitRealmStandardClasses(JSContext* cx) { return GlobalObject::initStandardClasses(cx, cx->global()); }
+JS_PUBLIC_API bool JS::MaybeFreezeCtorAndPrototype(JSContext* cx, + HandleObject ctor, + HandleObject maybeProto) { + if (MOZ_LIKELY(!cx->realm()->creationOptions().freezeBuiltins())) { + return true; + } + if (!SetIntegrityLevel(cx, ctor, IntegrityLevel::Frozen)) { + return false; + } + if (maybeProto) { + if (!SetIntegrityLevel(cx, maybeProto, IntegrityLevel::Sealed)) { + return false; + } + } + return true; +} + JS_PUBLIC_API JSObject* JS::GetRealmObjectPrototype(JSContext* cx) { CHECK_THREAD(cx); return GlobalObject::getOrCreateObjectPrototype(cx, cx->global());