From 9a1036bf48d735ae8189b258c1149f20f4097f59 Mon Sep 17 00:00:00 2001 From: Jan de Mooij Date: Tue, 25 Jan 2022 10:13:04 +0000 Subject: [PATCH] Bug 1732362 part 3 - Add an API to disable the JIT backend completely. r=iain This adds a mechanism to permanently disable the JIT backend for the process. This lets us improve the sandbox for the socket process. Depends on D136723 Differential Revision: https://phabricator.services.mozilla.com/D136724 --- js/public/Initialization.h | 9 +++++++ .../tests/basic/disable-jit-backend.js | 19 ++++++++++++++ js/src/jit/JitOptions.cpp | 4 +++ js/src/jit/JitOptions.h | 12 ++++++--- js/src/jit/ProcessExecutableMemory.cpp | 3 +++ js/src/shell/fuzz-flags.txt | 1 + js/src/shell/js.cpp | 8 ++++++ js/src/vm/Initialization.cpp | 25 ++++++++++++++----- js/src/vm/JSScript.cpp | 16 +++++------- js/src/vm/RegExpShared.h | 6 +---- js/src/wasm/WasmJS.cpp | 6 ++++- 11 files changed, 84 insertions(+), 25 deletions(-) create mode 100644 js/src/jit-test/tests/basic/disable-jit-backend.js diff --git a/js/public/Initialization.h b/js/public/Initialization.h index c15616a30aab..b2c41938f228 100644 --- a/js/public/Initialization.h +++ b/js/public/Initialization.h @@ -144,6 +144,15 @@ JS_PUBLIC_API bool InitSelfHostedCode(JSContext* cx, SelfHostedCache cache = nullptr, SelfHostedWriter writer = nullptr); +/* + * Permanently disable the JIT backend for this process. This disables the JS + * Baseline Interpreter, JIT compilers, regular expression JIT and support for + * WebAssembly. + * + * If called, this *must* be called before JS_Init. + */ +JS_PUBLIC_API void DisableJitBackend(); + } // namespace JS /** diff --git a/js/src/jit-test/tests/basic/disable-jit-backend.js b/js/src/jit-test/tests/basic/disable-jit-backend.js new file mode 100644 index 000000000000..2eb0fa84eb0a --- /dev/null +++ b/js/src/jit-test/tests/basic/disable-jit-backend.js @@ -0,0 +1,19 @@ +// |jit-test| --no-jit-backend + +assertEq(wasmIsSupportedByHardware(), false); +assertEq(wasmIsSupported(), false); +assertEq(isAsmJSCompilationAvailable(), false); + +function test() { + var sum = 0; + for (var i = 0; i < 15; i++) { + sum += i; + } + return sum; +} +assertEq(test(), 105); + +var re = /[\d][a-z]{3}[\d]/; +for (var i = 0; i < 10; i++) { + assertEq(re.exec("123foo456")[0], "3foo4"); +} diff --git a/js/src/jit/JitOptions.cpp b/js/src/jit/JitOptions.cpp index 1f3ae935d4c5..318f2c6b637a 100644 --- a/js/src/jit/JitOptions.cpp +++ b/js/src/jit/JitOptions.cpp @@ -154,6 +154,10 @@ DefaultJitOptions::DefaultJitOptions() { // Toggles whether functions may be entered at loop headers. SET_DEFAULT(osr, true); + // Whether the JIT backend (used by JITs, Wasm, Baseline Interpreter) has been + // disabled for this process. See JS::DisableJitBackend. + SET_DEFAULT(disableJitBackend, false); + // Whether to enable extra code to perform dynamic validations. SET_DEFAULT(runExtraChecks, false); diff --git a/js/src/jit/JitOptions.h b/js/src/jit/JitOptions.h index bc57a177bbb8..1103fae79912 100644 --- a/js/src/jit/JitOptions.h +++ b/js/src/jit/JitOptions.h @@ -39,6 +39,7 @@ struct DefaultJitOptions { #endif bool checkRangeAnalysis; bool runExtraChecks; + bool disableJitBackend; bool disableAma; bool disableEaa; bool disableEdgeCaseAnalysis; @@ -136,14 +137,19 @@ struct DefaultJitOptions { extern DefaultJitOptions JitOptions; -inline bool IsBaselineInterpreterEnabled() { -#ifdef JS_CODEGEN_NONE +inline bool HasJitBackend() { +#if defined(JS_CODEGEN_NONE) return false; #else - return JitOptions.baselineInterpreter && JitOptions.supportsFloatingPoint; + return !JitOptions.disableJitBackend; #endif } +inline bool IsBaselineInterpreterEnabled() { + return HasJitBackend() && JitOptions.baselineInterpreter && + JitOptions.supportsFloatingPoint; +} + } // namespace jit extern mozilla::Atomic fuzzingSafe; diff --git a/js/src/jit/ProcessExecutableMemory.cpp b/js/src/jit/ProcessExecutableMemory.cpp index 6e0fac5792bf..f19eaa4efe84 100644 --- a/js/src/jit/ProcessExecutableMemory.cpp +++ b/js/src/jit/ProcessExecutableMemory.cpp @@ -23,6 +23,7 @@ # include "jit/arm64/vixl/Cpu-vixl.h" #endif #include "jit/FlushICache.h" // js::jit::FlushICache +#include "jit/JitOptions.h" #include "threading/LockGuard.h" #include "threading/Mutex.h" #include "util/Memory.h" @@ -545,6 +546,7 @@ class ProcessExecutableMemory { pages_.init(); MOZ_RELEASE_ASSERT(!initialized()); + MOZ_RELEASE_ASSERT(HasJitBackend()); MOZ_RELEASE_ASSERT(gc::SystemPageSize() <= ExecutableCodePageSize); void* p = ReserveProcessExecutableMemory(MaxCodeBytesPerProcess); @@ -603,6 +605,7 @@ void* ProcessExecutableMemory::allocate(size_t bytes, ProtectionSetting protection, MemCheckKind checkKind) { MOZ_ASSERT(initialized()); + MOZ_ASSERT(HasJitBackend()); MOZ_ASSERT(bytes > 0); MOZ_ASSERT((bytes % ExecutableCodePageSize) == 0); diff --git a/js/src/shell/fuzz-flags.txt b/js/src/shell/fuzz-flags.txt index d42c38ac701a..fd1c96fb4041 100644 --- a/js/src/shell/fuzz-flags.txt +++ b/js/src/shell/fuzz-flags.txt @@ -53,6 +53,7 @@ --no-warp-generator --no-warp-async --large-arraybuffers +--no-jit-backend # GC-related # These 2 flags can cause the shell to slow down diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index 8020f1a7177d..79b5c2ea49d0 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -12245,6 +12245,9 @@ int main(int argc, char** argv) { "Disable functions that cause " "artificial OOMs") || !op.addBoolOption('\0', "no-threads", "Disable helper threads") || + !op.addBoolOption( + '\0', "no-jit-backend", + "Disable the JIT backend completely for this process") || #ifdef DEBUG !op.addBoolOption('\0', "dump-entrained-variables", "Print variables which are " @@ -12345,6 +12348,11 @@ int main(int argc, char** argv) { return EXIT_SUCCESS; } + // Note: DisableJitBackend must be called before JS_InitWithFailureDiagnostic. + if (op.getBoolOption("no-jit-backend")) { + JS::DisableJitBackend(); + } + // Start the engine. if (const char* message = JS_InitWithFailureDiagnostic()) { fprintf(gErrFile->fp, "JS_Init failed: %s\n", message); diff --git a/js/src/vm/Initialization.cpp b/js/src/vm/Initialization.cpp index 109185131388..3278dc6aef75 100644 --- a/js/src/vm/Initialization.cpp +++ b/js/src/vm/Initialization.cpp @@ -24,6 +24,7 @@ #include "jit/AtomicOperations.h" #include "jit/Ion.h" #include "jit/JitCommon.h" +#include "jit/JitOptions.h" #include "jit/ProcessExecutableMemory.h" #include "js/Utility.h" #include "threading/ProtectedData.h" // js::AutoNoteSingleThreadedRegion @@ -175,7 +176,9 @@ JS_PUBLIC_API const char* JS::detail::InitWithFailureDiagnostic( js::coverage::InitLCov(); - RETURN_IF_FAIL(js::jit::InitProcessExecutableMemory()); + if (js::jit::HasJitBackend()) { + RETURN_IF_FAIL(js::jit::InitProcessExecutableMemory()); + } RETURN_IF_FAIL(js::MemoryProtectionExceptionHandler::install()); @@ -242,11 +245,11 @@ JS_PUBLIC_API bool JS::InitSelfHostedCode(JSContext* cx, SelfHostedCache cache, return false; } -#ifndef JS_CODEGEN_NONE - if (!rt->createJitRuntime(cx)) { - return false; + if (js::jit::HasJitBackend()) { + if (!rt->createJitRuntime(cx)) { + return false; + } } -#endif return true; } @@ -304,7 +307,9 @@ JS_PUBLIC_API void JS_ShutDown(void) { js::FinishDateTimeState(); if (!JSRuntime::hasLiveRuntimes()) { - js::jit::ReleaseProcessExecutableMemory(); + if (js::jit::HasJitBackend()) { + js::jit::ReleaseProcessExecutableMemory(); + } MOZ_ASSERT(!js::LiveMappedBufferCount()); } @@ -333,3 +338,11 @@ JS_PUBLIC_API bool JS_SetICUMemoryFunctions(JS_ICUAllocFn allocFn, (defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_X86)) void JS::SetAVXEnabled() { js::jit::CPUInfo::SetAVXEnabled(); } #endif + +JS_PUBLIC_API void JS::DisableJitBackend() { + MOZ_ASSERT(libraryInitState == InitState::Uninitialized, + "DisableJitBackend must be called before JS_Init"); + MOZ_ASSERT(!JSRuntime::hasLiveRuntimes(), + "DisableJitBackend must be called before creating a JSContext"); + js::jit::JitOptions.disableJitBackend = true; +} diff --git a/js/src/vm/JSScript.cpp b/js/src/vm/JSScript.cpp index 9937621855ad..3befcee58be9 100644 --- a/js/src/vm/JSScript.cpp +++ b/js/src/vm/JSScript.cpp @@ -2112,11 +2112,9 @@ void JSScript::relazify(JSRuntime* rt) { js::Scope* scope = enclosingScope(); UniquePtr scriptData; -#ifndef JS_CODEGEN_NONE // Any JIT compiles should have been released, so we already point to the // interpreter trampoline which supports lazy scripts. - MOZ_ASSERT(isUsingInterpreterTrampoline(rt)); -#endif + MOZ_ASSERT_IF(jit::HasJitBackend(), isUsingInterpreterTrampoline(rt)); // Without bytecode, the script counts are invalid so destroy them if they // still exist. @@ -2363,11 +2361,10 @@ bool JSScript::fullyInitFromStencil( // here will be released by the UniquePtr. Rooted> lazyData(cx); -#ifndef JS_CODEGEN_NONE // Whether we are a newborn script or an existing lazy script, we should // already be pointing to the interpreter trampoline. - MOZ_ASSERT(script->isUsingInterpreterTrampoline(cx->runtime())); -#endif + MOZ_ASSERT_IF(jit::HasJitBackend(), + script->isUsingInterpreterTrampoline(cx->runtime())); // If we are using an existing lazy script, record enough info to be able to // rollback on failure. @@ -3131,11 +3128,10 @@ BaseScript* BaseScript::New(JSContext* cx, JS::Handle function, return nullptr; } -#ifndef JS_CODEGEN_NONE - uint8_t* stubEntry = cx->runtime()->jitRuntime()->interpreterStub().value; -#else uint8_t* stubEntry = nullptr; -#endif + if (jit::HasJitBackend()) { + stubEntry = cx->runtime()->jitRuntime()->interpreterStub().value; + } MOZ_ASSERT_IF(function, function->compartment() == sourceObject->compartment()); diff --git a/js/src/vm/RegExpShared.h b/js/src/vm/RegExpShared.h index 1d7c8dd77f70..d8c2b27cf216 100644 --- a/js/src/vm/RegExpShared.h +++ b/js/src/vm/RegExpShared.h @@ -48,11 +48,7 @@ enum RegExpRunStatus : int32_t { }; inline bool IsNativeRegExpEnabled() { -#ifdef JS_CODEGEN_NONE - return false; -#else - return jit::JitOptions.nativeRegExp; -#endif + return jit::HasJitBackend() && jit::JitOptions.nativeRegExp; } /* diff --git a/js/src/wasm/WasmJS.cpp b/js/src/wasm/WasmJS.cpp index 4c2c71477b8a..9af5cd74c33e 100644 --- a/js/src/wasm/WasmJS.cpp +++ b/js/src/wasm/WasmJS.cpp @@ -382,10 +382,14 @@ bool wasm::ThreadsAvailable(JSContext* cx) { } bool wasm::HasPlatformSupport(JSContext* cx) { -#if !MOZ_LITTLE_ENDIAN() || defined(JS_CODEGEN_NONE) || defined(__wasi__) +#if !MOZ_LITTLE_ENDIAN() return false; #else + if (!HasJitBackend()) { + return false; + } + if (gc::SystemPageSize() > wasm::PageSize) { return false; }