tor-browser

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

commit 3aebb6379edcbff17a326efe55a49aedeb54f9b8
parent 9d7920d565f5b935bd800ac3cb9a6f0261d2a97f
Author: Atila Butkovits <abutkovits@mozilla.com>
Date:   Tue,  4 Nov 2025 02:34:36 +0200

Revert "Bug 1989115 - Remove UnsafeABIStrictness::AllowThrownExceptions r=mgaudet" for causing failures at Printer.cpp.

This reverts commit 1131839caa75339b375d8997ae46840a69aa4f78.

Revert "Bug 1989115 - Implement OOM stack trace capture r=jandem"

This reverts commit 51f685e3a63038eb4b7550d1e11194ec8eab0ce2.

Revert "Bug 1989115 - Add FixedBufferPrinter r=mgaudet"

This reverts commit be6b5737dce2b1f0de100036026dc92c39a01189.

Revert "Bug 1989115 - Track unsafe call with ABI state in release r=mgaudet,rhunt"

This reverts commit ce1cb61ef9dcc89f90cc41a4c6e6526cc9852415.

Diffstat:
Mjs/public/Printer.h | 22----------------------
Mjs/src/builtin/TestingFunctions.cpp | 32--------------------------------
Djs/src/jit-test/tests/test_oom_comprehensive.js | 33---------------------------------
Mjs/src/jit/MacroAssembler.cpp | 12+++++++++++-
Mjs/src/jit/MacroAssembler.h | 2+-
Mjs/src/jsapi-tests/moz.build | 1-
Djs/src/jsapi-tests/testPrinter.cpp | 111-------------------------------------------------------------------------------
Mjs/src/vm/JSContext.cpp | 66+++++++++++-------------------------------------------------------
Mjs/src/vm/JSContext.h | 31+++++++++++++++----------------
Mjs/src/vm/Printer.cpp | 8--------
Mmodules/libpref/init/StaticPrefList.yaml | 7-------
11 files changed, 38 insertions(+), 287 deletions(-)

diff --git a/js/public/Printer.h b/js/public/Printer.h @@ -323,28 +323,6 @@ class JS_PUBLIC_API JSSprinter : public StringPrinter { JSString* release(JSContext* cx) { return releaseJS(cx); } }; -// FixedBufferPrinter, print to a fixed-size buffer. The string in the buffer -// will always be null-terminated after being passed to the constructor. -class FixedBufferPrinter final : public GenericPrinter { - private: - // The first char in the buffer where put will append the next string - char* buffer_; - // The remaining size available in the buffer - size_t size_; - - public: - constexpr FixedBufferPrinter(char* buf, size_t size) - : buffer_(buf), size_(size) { - MOZ_ASSERT(buffer_); - memset(buffer_, 0, size_); - } - - // Puts |len| characters from |s| at the current position. - // If the buffer fills up, this won't do anything. - void put(const char* s, size_t len) override; - using GenericPrinter::put; // pick up |put(const char* s);| -}; - // Fprinter, print a string directly into a file. class JS_PUBLIC_API Fprinter final : public GenericPrinter { private: diff --git a/js/src/builtin/TestingFunctions.cpp b/js/src/builtin/TestingFunctions.cpp @@ -10037,32 +10037,6 @@ static bool TestingFunc_SupportDifferentialTesting(JSContext* cx, unsigned argc, return true; } -static bool GetLastOOMStackTrace(JSContext* cx, unsigned argc, Value* vp) { - CallArgs args = CallArgsFromVp(argc, vp); - - if (!cx->hasOOMStackTrace()) { - args.rval().setNull(); - return true; - } - - const char* stackTrace = cx->getOOMStackTrace(); - if (!stackTrace) { - args.rval().setNull(); - return true; - } - - JSString* str = JS_NewStringCopyZ(cx, stackTrace); - if (!str) { - return false; - } - - // Clear the stored OOM stack trace after retrieving it once. - cx->unsetOOMStackTrace(); - - args.rval().setString(str); - return true; -} - // clang-format off static const JSFunctionSpecWithHelp TestingFunctions[] = { JS_FN_HELP("gc", ::GC, 0, 0, @@ -11181,12 +11155,6 @@ JS_FN_HELP("isSmallFunction", IsSmallFunction, 1, 0, "popAllFusesInRealm()", " Pops all the fuses in the current realm"), - JS_FN_HELP("getLastOOMStackTrace", GetLastOOMStackTrace, 0, 0, -"getLastOOMStackTrace()", -" Returns the stack trace captured from the most recent out-of-memory exception,\n" -" or null if no OOM stack trace is available. The stack trace shows the JavaScript\n" -" call stack at the time the out-of-memory condition occurred."), - JS_FN_HELP("popAllFusesInRuntime", PopAllFusesInRuntime, 0, 0, "popAllFusesInRuntime()", " Pops all the fuses in the runtime"), diff --git a/js/src/jit-test/tests/test_oom_comprehensive.js b/js/src/jit-test/tests/test_oom_comprehensive.js @@ -1,32 +0,0 @@ -// |jit-test| --setpref=experimental.capture_oom_stack_trace=true; skip-if: !this.hasOwnProperty("getLastOOMStackTrace") - -function testStack() { - function deepFunction() { - function evenDeeper() { - throwOutOfMemory(); - } - return evenDeeper(); - } - return deepFunction(); -} - -// Clear any existing trace -var initialTrace = getLastOOMStackTrace(); -assertEq(initialTrace, null); - -try { - testStack(); - assertEq(true, false, "Expected an OOM exception"); -} catch (e) { - print("✓ Exception caught: " + e); - - // Check for captured stack trace - var finalTrace = getLastOOMStackTrace(); - assertEq(finalTrace !== null, true, "Expected a stack trace after OOM"); - - print(finalTrace); - - // Detailed analysis - var lines = finalTrace.split('\n').filter(line => line.trim()); - assertEq(finalTrace.includes("#"), true); -} -\ No newline at end of file diff --git a/js/src/jit/MacroAssembler.cpp b/js/src/jit/MacroAssembler.cpp @@ -4885,6 +4885,7 @@ void MacroAssembler::setupAlignedABICall() { dynamicAlignment_ = false; } +#ifdef JS_CHECK_UNSAFE_CALL_WITH_ABI void MacroAssembler::wasmCheckUnsafeCallWithABIPre() { // Set the JSContext::inUnsafeCallWithABI flag. loadPtr(Address(InstanceReg, wasm::Instance::offsetOfCx()), @@ -4894,7 +4895,6 @@ void MacroAssembler::wasmCheckUnsafeCallWithABIPre() { store32(Imm32(1), flagAddr); } -#ifdef JS_CHECK_UNSAFE_CALL_WITH_ABI void MacroAssembler::wasmCheckUnsafeCallWithABIPost() { // Check JSContext::inUnsafeCallWithABI was cleared as expected. Label ok; @@ -4986,6 +4986,7 @@ void MacroAssembler::callWithABINoProfiler(void* fun, ABIType result, uint32_t stackAdjust; callWithABIPre(&stackAdjust); +#ifdef JS_CHECK_UNSAFE_CALL_WITH_ABI if (check == CheckUnsafeCallWithABI::Check) { // Set the JSContext::inUnsafeCallWithABI flag. push(ReturnReg); @@ -4996,6 +4997,7 @@ void MacroAssembler::callWithABINoProfiler(void* fun, ABIType result, // On arm64, SP may be < PSP now (that's OK). // eg testcase: tests/bug1375074.js } +#endif call(ImmPtr(fun)); @@ -5027,9 +5029,13 @@ CodeOffset MacroAssembler::callWithABI(wasm::BytecodeOffset bytecode, // The instance register is used in builtin thunks and must be set. bool needsBuiltinThunk = wasm::NeedsBuiltinThunk(imm); +#ifdef JS_CHECK_UNSAFE_CALL_WITH_ABI // The builtin thunk exits the JIT activation, if we don't have one we must // use AutoUnsafeCallWithABI inside the builtin and check that here. bool checkUnsafeCallWithABI = !needsBuiltinThunk; +#else + bool checkUnsafeCallWithABI = false; +#endif if (needsBuiltinThunk || checkUnsafeCallWithABI) { if (instanceOffset) { loadPtr(Address(getStackPointer(), *instanceOffset + stackAdjust), @@ -5039,9 +5045,11 @@ CodeOffset MacroAssembler::callWithABI(wasm::BytecodeOffset bytecode, } } +#ifdef JS_CHECK_UNSAFE_CALL_WITH_ABI if (checkUnsafeCallWithABI) { wasmCheckUnsafeCallWithABIPre(); } +#endif CodeOffset raOffset = call( wasm::CallSiteDesc(bytecode.offset(), wasm::CallSiteKind::Symbolic), imm); @@ -6390,12 +6398,14 @@ CodeOffset MacroAssembler::wasmCallBuiltinInstanceMethod( MOZ_CRASH("Unknown abi passing style for pointer"); } +#ifdef JS_CHECK_UNSAFE_CALL_WITH_ABI // The builtin thunk exits the JIT activation, if we don't have one we must // use AutoUnsafeCallWithABI inside the builtin and check that here. bool checkUnsafeCallWithABI = !wasm::NeedsBuiltinThunk(builtin); if (checkUnsafeCallWithABI) { wasmCheckUnsafeCallWithABIPre(); } +#endif CodeOffset ret = call(desc, builtin); diff --git a/js/src/jit/MacroAssembler.h b/js/src/jit/MacroAssembler.h @@ -838,9 +838,9 @@ class MacroAssembler : public MacroAssemblerSpecific { void callWithABIPost(uint32_t stackAdjust, ABIType result, bool callFromWasm = false) PER_ARCH; +#ifdef JS_CHECK_UNSAFE_CALL_WITH_ABI // Set the JSContext::inUnsafeCallWithABI flag using InstanceReg. void wasmCheckUnsafeCallWithABIPre(); -#ifdef JS_CHECK_UNSAFE_CALL_WITH_ABI // Check JSContext::inUnsafeCallWithABI was cleared as expected. void wasmCheckUnsafeCallWithABIPost(); #endif diff --git a/js/src/jsapi-tests/moz.build b/js/src/jsapi-tests/moz.build @@ -108,7 +108,6 @@ UNIFIED_SOURCES += [ "testParserAtom.cpp", "testPersistentRooted.cpp", "testPreserveJitCode.cpp", - "testPrinter.cpp", "testPrintf.cpp", "testPrivateGCThingValue.cpp", "testProfileStrings.cpp", diff --git a/js/src/jsapi-tests/testPrinter.cpp b/js/src/jsapi-tests/testPrinter.cpp @@ -1,111 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- - * vim: set ts=8 sts=2 et sw=2 tw=80: - */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "js/Printer.h" // FixedBufferPrinter - -#include "jsapi-tests/tests.h" - -using namespace js; - -struct TestBuffer { - const char ASCII_ACK = (char)6; - - char* buffer; - size_t size; - - // len is the buffer size, including terminating null - explicit TestBuffer(const size_t len) : buffer(new char[len + 3]), size(len) { - buffer[size] = ASCII_ACK; // to detect overflow - buffer[size + 1] = ASCII_ACK; - buffer[size + 2] = ASCII_ACK; - } - - ~TestBuffer() { delete[] buffer; } - - // test has written past the end of the buffer - bool hasOverflowed() { - return buffer[size] != ASCII_ACK || buffer[size + 1] != ASCII_ACK || - buffer[size + 2] != ASCII_ACK; - } - - bool matches(const char* expected) { return strcmp(buffer, expected) == 0; } -}; - -BEGIN_TEST(testFixedBufferPrinter) { - // empty buffer - { - TestBuffer actual(0); - FixedBufferPrinter fbp(actual.buffer, 0); - fbp.put("will not fit"); - CHECK(!actual.hasOverflowed()); - } - // buffer is initially null-terminated - { - TestBuffer actual(10); - // make sure the buffer is not null-terminated - memset(actual.buffer, '!', actual.size); - FixedBufferPrinter fbp(actual.buffer, 10); - CHECK(!actual.hasOverflowed()); - CHECK(actual.matches("")); - } - // one put that fits - { - TestBuffer actual(50); - FixedBufferPrinter fbp(actual.buffer, actual.size); - const char* expected = "expected string fits"; - fbp.put(expected); - CHECK(!actual.hasOverflowed()); - CHECK(actual.matches(expected)); - } - // unterminated string in put - { - TestBuffer actual(50); - FixedBufferPrinter fbp(actual.buffer, actual.size); - const char* expected = "okBAD"; - fbp.put(expected, 2); - CHECK(!actual.hasOverflowed()); - CHECK(actual.matches("ok")); - } - // one put that more than fills the buffer - { - TestBuffer actual(16); - FixedBufferPrinter fbp(actual.buffer, actual.size); - const char* expected = "expected string overflow"; - fbp.put(expected); - CHECK(!actual.hasOverflowed()); - CHECK(actual.matches("expected string")); - } - // maintains position over multiple puts that fit - { - TestBuffer actual(16); - FixedBufferPrinter fbp(actual.buffer, actual.size); - fbp.put("expected "); - fbp.put("string"); - CHECK(actual.matches("expected string")); - } - // multiple puts, last one more than fills the buffer - { - TestBuffer actual(9); - FixedBufferPrinter fbp(actual.buffer, actual.size); - fbp.put("expected"); - fbp.put("overflow"); - CHECK(!actual.hasOverflowed()); - CHECK(actual.matches("expected")); - } - // put after buffer is full doesn't overflow - { - TestBuffer actual(2); - FixedBufferPrinter fbp(actual.buffer, actual.size); - fbp.put("exp"); - fbp.put("overflow"); - CHECK(!actual.hasOverflowed()); - CHECK(actual.matches("e")); - } - - return true; -} -END_TEST(testFixedBufferPrinter) diff --git a/js/src/vm/JSContext.cpp b/js/src/vm/JSContext.cpp @@ -57,9 +57,7 @@ #include "util/NativeStack.h" #include "util/Text.h" #include "util/WindowsWrapper.h" -#include "js/friend/DumpFunctions.h" // for stack trace utilities -#include "js/Printer.h" // for FixedBufferPrinter -#include "vm/BytecodeUtil.h" // JSDVG_IGNORE_STACK +#include "vm/BytecodeUtil.h" // JSDVG_IGNORE_STACK #include "vm/ErrorObject.h" #include "vm/ErrorReporting.h" #include "vm/FrameIter.h" @@ -281,13 +279,6 @@ void JSContext::onOutOfMemory() { runtime()->hadOutOfMemory = true; gc::AutoSuppressGC suppressGC(this); - // Capture stack trace before doing anything else that might use memory. - // If we're in an unsafe ABI context, we don't need to capture a stack trace - // because the function will explicitly recover from OOM. - if (!inUnsafeCallWithABI) { - captureOOMStackTrace(); - } - /* Report the oom. */ if (JS::OutOfMemoryCallback oomCallback = runtime()->oomCallback) { oomCallback(this, runtime()->oomCallbackData); @@ -1212,8 +1203,10 @@ JSContext::JSContext(JSRuntime* runtime, const JS::ContextOptions& options) activation_(this, nullptr), profilingActivation_(nullptr), noExecuteDebuggerTop(this, nullptr), +#ifdef JS_CHECK_UNSAFE_CALL_WITH_ABI inUnsafeCallWithABI(this, false), hasAutoUnsafeCallWithABI(this, false), +#endif #ifdef DEBUG liveArraySortDataInstances(this, 0), #endif @@ -1275,18 +1268,10 @@ JSContext::JSContext(JSRuntime* runtime, const JS::ContextOptions& options) canSkipEnqueuingJobs(this, false), promiseRejectionTrackerCallback(this, nullptr), promiseRejectionTrackerCallbackData(this, nullptr), - oomStackTraceBuffer_(this, nullptr), - oomStackTraceBufferValid_(this, false), insideExclusiveDebuggerOnEval(this, nullptr), microTaskQueues(this) { MOZ_ASSERT(static_cast<JS::RootingContext*>(this) == JS::RootingContext::get(this)); - - if (JS::Prefs::experimental_capture_oom_stack_trace()) { - // Allocate pre-allocated buffer for OOM stack traces - oomStackTraceBuffer_ = - static_cast<char*>(js_calloc(OOMStackTraceBufferSize)); - } } #ifdef ENABLE_WASM_JSPI @@ -1326,41 +1311,9 @@ JSContext::~JSContext() { irregexp::DestroyIsolate(isolate.ref()); } - // Free the pre-allocated OOM stack trace buffer - if (oomStackTraceBuffer_) { - js_free(oomStackTraceBuffer_); - } - TlsContext.set(nullptr); } -void JSContext::unsetOOMStackTrace() { oomStackTraceBufferValid_ = false; } - -const char* JSContext::getOOMStackTrace() const { - if (!oomStackTraceBufferValid_ || !oomStackTraceBuffer_) { - return nullptr; - } - return oomStackTraceBuffer_; -} - -bool JSContext::hasOOMStackTrace() const { return oomStackTraceBufferValid_; } - -void JSContext::captureOOMStackTrace() { - // Clear any existing stack trace - oomStackTraceBufferValid_ = false; - - if (!oomStackTraceBuffer_) { - return; // Buffer not available - } - - // Write directly to pre-allocated buffer to avoid any memory allocation - FixedBufferPrinter fbp(oomStackTraceBuffer_, OOMStackTraceBufferSize); - js::DumpBacktrace(this, fbp); - MOZ_ASSERT(strlen(oomStackTraceBuffer_) < OOMStackTraceBufferSize); - - oomStackTraceBufferValid_ = true; -} - void JSContext::setRuntime(JSRuntime* rt) { MOZ_ASSERT(!resolvingList); MOZ_ASSERT(!compartment()); @@ -1730,6 +1683,8 @@ void JSContext::suspendExecutionTracing() { #endif +#ifdef JS_CHECK_UNSAFE_CALL_WITH_ABI + AutoUnsafeCallWithABI::AutoUnsafeCallWithABI(UnsafeABIStrictness strictness) : cx_(TlsContext.get()), nested_(cx_ ? cx_->hasAutoUnsafeCallWithABI : false), @@ -1738,7 +1693,6 @@ AutoUnsafeCallWithABI::AutoUnsafeCallWithABI(UnsafeABIStrictness strictness) // This is a helper thread doing Ion or Wasm compilation - nothing to do. return; } -#ifdef JS_CHECK_UNSAFE_CALL_WITH_ABI switch (strictness) { case UnsafeABIStrictness::NoExceptions: MOZ_ASSERT(!JS_IsExceptionPending(cx_)); @@ -1747,8 +1701,10 @@ AutoUnsafeCallWithABI::AutoUnsafeCallWithABI(UnsafeABIStrictness strictness) case UnsafeABIStrictness::AllowPendingExceptions: checkForPendingException_ = !JS_IsExceptionPending(cx_); break; + case UnsafeABIStrictness::AllowThrownExceptions: + checkForPendingException_ = false; + break; } -#endif cx_->hasAutoUnsafeCallWithABI = true; } @@ -1757,16 +1713,16 @@ AutoUnsafeCallWithABI::~AutoUnsafeCallWithABI() { if (!cx_) { return; } -#ifdef JS_CHECK_UNSAFE_CALL_WITH_ABI MOZ_ASSERT(cx_->hasAutoUnsafeCallWithABI); - MOZ_ASSERT_IF(checkForPendingException_, !JS_IsExceptionPending(cx_)); -#endif if (!nested_) { cx_->hasAutoUnsafeCallWithABI = false; cx_->inUnsafeCallWithABI = false; } + MOZ_ASSERT_IF(checkForPendingException_, !JS_IsExceptionPending(cx_)); } +#endif // JS_CHECK_UNSAFE_CALL_WITH_ABI + #ifdef __wasi__ JS_PUBLIC_API void js::IncWasiRecursionDepth(JSContext* cx) { ++JS::RootingContext::get(cx)->wasiRecursionDepth; diff --git a/js/src/vm/JSContext.h b/js/src/vm/JSContext.h @@ -442,9 +442,11 @@ struct JS_PUBLIC_API JSContext : public JS::RootingContext, return offsetof(JSContext, jitActivation); } +#ifdef JS_CHECK_UNSAFE_CALL_WITH_ABI static size_t offsetOfInUnsafeCallWithABI() { return offsetof(JSContext, inUnsafeCallWithABI); } +#endif public: js::InterpreterStack& interpreterStack() { @@ -483,8 +485,10 @@ struct JS_PUBLIC_API JSContext : public JS::RootingContext, */ js::ContextData<js::EnterDebuggeeNoExecute*> noExecuteDebuggerTop; +#ifdef JS_CHECK_UNSAFE_CALL_WITH_ABI js::ContextData<uint32_t> inUnsafeCallWithABI; js::ContextData<bool> hasAutoUnsafeCallWithABI; +#endif #ifdef DEBUG js::ContextData<uint32_t> liveArraySortDataInstances; @@ -684,12 +688,6 @@ struct JS_PUBLIC_API JSContext : public JS::RootingContext, #endif } - // OOM stack trace buffer management - void unsetOOMStackTrace(); - const char* getOOMStackTrace() const; - bool hasOOMStackTrace() const; - void captureOOMStackTrace(); - js::ContextData<int32_t> reportGranularity; /* see vm/Probes.h */ js::ContextData<js::AutoResolving*> resolvingList; @@ -962,14 +960,6 @@ struct JS_PUBLIC_API JSContext : public JS::RootingContext, promiseRejectionTrackerCallback; js::ContextData<void*> promiseRejectionTrackerCallbackData; - // Pre-allocated buffer for storing out-of-memory stack traces. - // This buffer is allocated during context initialization to avoid - // allocation during OOM conditions. The buffer stores a formatted - // stack trace string that can be retrieved by privileged JavaScript. - static constexpr size_t OOMStackTraceBufferSize = 4096; - js::ContextData<char*> oomStackTraceBuffer_; - js::ContextData<bool> oomStackTraceBufferValid_; - JSObject* getIncumbentGlobal(JSContext* cx); bool enqueuePromiseJob(JSContext* cx, js::HandleFunction job, js::HandleObject promise, @@ -1194,7 +1184,11 @@ class MOZ_RAII AutoNoteExclusiveDebuggerOnEval { } }; -enum UnsafeABIStrictness { NoExceptions, AllowPendingExceptions }; +enum UnsafeABIStrictness { + NoExceptions, + AllowPendingExceptions, + AllowThrownExceptions +}; // Should be used in functions called directly from JIT code (with // masm.callWithABI). This assert invariants in debug builds. Resets @@ -1212,17 +1206,22 @@ enum UnsafeABIStrictness { NoExceptions, AllowPendingExceptions }; // the function is not called with a pending exception, and that it does not // throw an exception itself. class MOZ_RAII AutoUnsafeCallWithABI { +#ifdef JS_CHECK_UNSAFE_CALL_WITH_ABI JSContext* cx_; bool nested_; -#ifdef JS_CHECK_UNSAFE_CALL_WITH_ABI bool checkForPendingException_; #endif JS::AutoCheckCannotGC nogc; public: +#ifdef JS_CHECK_UNSAFE_CALL_WITH_ABI explicit AutoUnsafeCallWithABI( UnsafeABIStrictness strictness = UnsafeABIStrictness::NoExceptions); ~AutoUnsafeCallWithABI(); +#else + explicit AutoUnsafeCallWithABI( + UnsafeABIStrictness unused_ = UnsafeABIStrictness::NoExceptions) {} +#endif }; template <typename T> diff --git a/js/src/vm/Printer.cpp b/js/src/vm/Printer.cpp @@ -497,14 +497,6 @@ void LSprinter::clear() { hadOOM_ = false; } -void FixedBufferPrinter::put(const char* s, size_t len) { - snprintf(buffer_, size_, "%.*s", int(len), s); - size_t written = std::min(len, size_); - MOZ_ASSERT(size_ - written >= 0); - size_ -= written; - buffer_ += written; -} - void LSprinter::put(const char* s, size_t len) { if (hadOutOfMemory()) { return; diff --git a/modules/libpref/init/StaticPrefList.yaml b/modules/libpref/init/StaticPrefList.yaml @@ -8997,13 +8997,6 @@ mirror: always set_spidermonkey_pref: startup - # Capture stack traces for OOM -- name: javascript.options.experimental.capture_oom_stack_trace - type: bool - value: true - mirror: always - set_spidermonkey_pref: startup - #endif // NIGHTLY_BUILD # Whether to Baseline-compile self-hosted functions the first time they are