tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit a8e957529a2aeb0af1aaac76a56906af1b1f554b
parent 5f39ebf46d97002cd0b938a3bc9754a0d4c2b5aa
Author: Iain Ireland <iireland@mozilla.com>
Date:   Wed, 22 Oct 2025 18:11:07 +0000

Bug 1995289: Don't call scripted proxy get trap for private fields r=jandem

Differential Revision: https://phabricator.services.mozilla.com/D269297

Diffstat:
Ajs/src/jit-test/tests/bug1995289.js | 42++++++++++++++++++++++++++++++++++++++++++
Mjs/src/jit/BaselineCacheIRCompiler.cpp | 10++++++++++
Mjs/src/jit/CacheIR.cpp | 10++++++++++
Mjs/src/jit/IonCacheIRCompiler.cpp | 10++++++++++
Mjs/src/vm/SymbolType.h | 1+
5 files changed, 73 insertions(+), 0 deletions(-)

diff --git a/js/src/jit-test/tests/bug1995289.js b/js/src/jit-test/tests/bug1995289.js @@ -0,0 +1,42 @@ +class Identity { + constructor(target) { + return target; + } +} + +class TargetHandler extends Identity { + #proxy; + + constructor(target, proxy) { + super(target); + this.#proxy = proxy; + } + + static getProxy(obj) { + return obj.#proxy; + } +} + +class ReactiveHandler extends TargetHandler { + #priv; + + constructor(target, proxy) { + // Both the target and the proxy have the "#proxy" private field that stores the reference to the proxy + new TargetHandler(target, proxy); + super(proxy, proxy); + } + + get(t, k, r) { throw "oops"; } + + defineProperty(t, k, desc) { + ReactiveHandler.getProxy(t).#priv; + return Reflect.defineProperty(t, k, desc); + } +} + +const target = {}; +const proxy = new ReactiveHandler(target, new Proxy(target, ReactiveHandler.prototype)); + +for (var i = 0; i < 20; i++) { + proxy[i] = i; +} diff --git a/js/src/jit/BaselineCacheIRCompiler.cpp b/js/src/jit/BaselineCacheIRCompiler.cpp @@ -3766,6 +3766,16 @@ bool BaselineCacheIRCompiler::emitCallScriptedProxyGetShared( stubFrame.storeTracedValue(masm, target); if constexpr (std::is_same_v<IdType, ValOperandId>) { stubFrame.storeTracedValue(masm, idVal); +# ifdef DEBUG + Label notPrivateSymbol; + masm.branchTestSymbol(Assembler::NotEqual, idVal, &notPrivateSymbol); + masm.unboxSymbol(idVal, scratch); + masm.branch32( + Assembler::NotEqual, Address(scratch, JS::Symbol::offsetOfCode()), + Imm32(uint32_t(JS::SymbolCode::PrivateNameSymbol)), &notPrivateSymbol); + masm.assumeUnreachable("Unexpected private field in callScriptedProxy"); + masm.bind(&notPrivateSymbol); +# endif } else { // We need to either trace the id here or grab the ICStubReg back from // FramePointer + sizeof(void*) after the call in order to load it again. diff --git a/js/src/jit/CacheIR.cpp b/js/src/jit/CacheIR.cpp @@ -1677,6 +1677,16 @@ AttachDecision GetPropIRGenerator::tryAttachScriptedProxy( } } + // Private fields of proxies are stored on the expando, and don't fire traps. + // Note that we don't need to guard against this in CacheIR, because there's + // no way to generate bytecode that will *sometimes* load a private field; + // either it's accessing a private field, or it isn't. We assert in the + // CacheIR implementation of callScriptedProxy(GetResult|GetByValueResult) + // that we don't see any private field symbols. + if (idVal_.isSymbol() && idVal_.toSymbol()->isPrivateName()) { + return AttachDecision::NoAction; + } + JSObject* handlerObj = ScriptedProxyHandler::handlerObject(obj); if (!handlerObj) { return AttachDecision::NoAction; diff --git a/js/src/jit/IonCacheIRCompiler.cpp b/js/src/jit/IonCacheIRCompiler.cpp @@ -1056,6 +1056,16 @@ bool IonCacheIRCompiler::emitCallScriptedProxyGetShared( if constexpr (std::is_same_v<IdType, ValOperandId>) { // Same for the id, assuming it's not baked in storeTracedValue(masm, idVal); +# ifdef DEBUG + Label notPrivateSymbol; + masm.branchTestSymbol(Assembler::NotEqual, idVal, &notPrivateSymbol); + masm.unboxSymbol(idVal, scratch); + masm.branch32( + Assembler::NotEqual, Address(scratch, JS::Symbol::offsetOfCode()), + Imm32(uint32_t(JS::SymbolCode::PrivateNameSymbol)), &notPrivateSymbol); + masm.assumeUnreachable("Unexpected private field in callScriptedProxy"); + masm.bind(&notPrivateSymbol); +# endif } uint32_t framePushedBeforeArgs = masm.framePushed(); diff --git a/js/src/vm/SymbolType.h b/js/src/vm/SymbolType.h @@ -110,6 +110,7 @@ class Symbol #endif static constexpr size_t offsetOfHash() { return offsetof(Symbol, hash_); } + static constexpr size_t offsetOfCode() { return offsetof(Symbol, code_); } }; } /* namespace JS */