diff --git a/caps/nsScriptSecurityManager.cpp b/caps/nsScriptSecurityManager.cpp index 3f5f157c64a6..abb2586fdbfa 100644 --- a/caps/nsScriptSecurityManager.cpp +++ b/caps/nsScriptSecurityManager.cpp @@ -73,6 +73,8 @@ #include "nsJSUtils.h" #include "nsILoadInfo.h" #include "js/ColumnNumber.h" // JS::ColumnNumberOneOrigin +#include "js/GCVector.h" +#include "js/Value.h" // This should be probably defined on some other place... but I couldn't find it #define WEBAPPS_PERM_NAME "webapps-manage" @@ -462,7 +464,12 @@ NS_IMPL_ISUPPORTS(nsScriptSecurityManager, nsIScriptSecurityManager) ///////////////// Security Checks ///////////////// bool nsScriptSecurityManager::ContentSecurityPolicyPermitsJSAction( - JSContext* cx, JS::RuntimeCode aKind, JS::Handle aCode) { + JSContext* cx, JS::RuntimeCode aKind, JS::Handle aCodeString, + JS::CompilationType aCompilationType, + JS::Handle> aParameterStrings, + JS::Handle aBodyString, + JS::Handle> aParameterArgs, + JS::Handle aBodyArg, bool* aOutCanCompileStrings) { MOZ_ASSERT(cx == nsContentUtils::GetCurrentJSContext()); nsCOMPtr subjectPrincipal = nsContentUtils::SubjectPrincipal(); @@ -477,13 +484,14 @@ bool nsScriptSecurityManager::ContentSecurityPolicyPermitsJSAction( if (contextForbidsEval) { nsAutoJSString scriptSample; if (aKind == JS::RuntimeCode::JS && - NS_WARN_IF(!scriptSample.init(cx, aCode))) { + NS_WARN_IF(!scriptSample.init(cx, aCodeString))) { return false; } if (!nsContentSecurityUtils::IsEvalAllowed( cx, subjectPrincipal->IsSystemPrincipal(), scriptSample)) { - return false; + *aOutCanCompileStrings = false; + return true; } } @@ -503,6 +511,7 @@ bool nsScriptSecurityManager::ContentSecurityPolicyPermitsJSAction( } // don't do anything unless there's a CSP if (!csp) { + *aOutCanCompileStrings = true; return true; } } @@ -522,7 +531,8 @@ bool nsScriptSecurityManager::ContentSecurityPolicyPermitsJSAction( nsresult rv = csp->GetAllowsEval(&reportViolation, &evalOK); if (NS_FAILED(rv)) { NS_WARNING("CSP: failed to get allowsEval"); - return true; // fail open to not break sites. + *aOutCanCompileStrings = true; // fail open to not break sites. + return true; } } else { if (NS_FAILED(csp->GetAllowsWasmEval(&reportViolation, &evalOK))) { @@ -545,8 +555,7 @@ bool nsScriptSecurityManager::ContentSecurityPolicyPermitsJSAction( auto caller = JSCallingLocation::Get(cx); nsAutoJSString scriptSample; if (aKind == JS::RuntimeCode::JS && - NS_WARN_IF(!scriptSample.init(cx, aCode))) { - JS_ClearPendingException(cx); + NS_WARN_IF(!scriptSample.init(cx, aCodeString))) { return false; } uint16_t violationType = @@ -559,7 +568,8 @@ bool nsScriptSecurityManager::ContentSecurityPolicyPermitsJSAction( caller.mLine, caller.mColumn, u""_ns, u""_ns); } - return evalOK; + *aOutCanCompileStrings = evalOK; + return true; } // static diff --git a/caps/nsScriptSecurityManager.h b/caps/nsScriptSecurityManager.h index 5400c9f2254c..5bb44a815bda 100644 --- a/caps/nsScriptSecurityManager.h +++ b/caps/nsScriptSecurityManager.h @@ -28,6 +28,7 @@ class SystemPrincipal; namespace JS { enum class RuntimeCode; +enum class CompilationType; } // namespace JS ///////////////////////////// @@ -91,9 +92,13 @@ class nsScriptSecurityManager final : public nsIScriptSecurityManager { virtual ~nsScriptSecurityManager(); // Decides, based on CSP, whether or not eval() and stuff can be executed. - static bool ContentSecurityPolicyPermitsJSAction(JSContext* cx, - JS::RuntimeCode kind, - JS::Handle aCode); + static bool ContentSecurityPolicyPermitsJSAction( + JSContext* aCx, JS::RuntimeCode aKind, JS::Handle aCodeString, + JS::CompilationType aCompilationType, + JS::Handle> aParameterStrings, + JS::Handle aBodyString, + JS::Handle> aParameterArgs, + JS::Handle aBodyArg, bool* aOutCanCompileStrings); static bool JSPrincipalsSubsume(JSPrincipals* first, JSPrincipals* second); diff --git a/dom/workers/RuntimeService.cpp b/dom/workers/RuntimeService.cpp index 996b56991667..0181610deb35 100644 --- a/dom/workers/RuntimeService.cpp +++ b/dom/workers/RuntimeService.cpp @@ -26,8 +26,10 @@ #include "jsfriendapi.h" #include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_* #include "js/ContextOptions.h" +#include "js/GCVector.h" #include "js/Initialization.h" #include "js/LocaleSensitive.h" +#include "js/Value.h" #include "js/WasmFeatures.h" #include "mozilla/ArrayUtils.h" #include "mozilla/Atomics.h" @@ -499,8 +501,13 @@ class LogViolationDetailsRunnable final : public WorkerMainThreadRunnable { ~LogViolationDetailsRunnable() = default; }; -bool ContentSecurityPolicyAllows(JSContext* aCx, JS::RuntimeCode aKind, - JS::Handle aCode) { +bool ContentSecurityPolicyAllows( + JSContext* aCx, JS::RuntimeCode aKind, JS::Handle aCodeString, + JS::CompilationType aCompilationType, + JS::Handle> aParameterStrings, + JS::Handle aBodyString, + JS::Handle> aParameterArgs, + JS::Handle aBodyArg, bool* aOutCanCompileStrings) { WorkerPrivate* worker = GetWorkerPrivateFromContext(aCx); worker->AssertIsOnWorkerThread(); @@ -509,14 +516,14 @@ bool ContentSecurityPolicyAllows(JSContext* aCx, JS::RuntimeCode aKind, uint16_t violationType; nsAutoJSString scriptSample; if (aKind == JS::RuntimeCode::JS) { - if (NS_WARN_IF(!scriptSample.init(aCx, aCode))) { - JS_ClearPendingException(aCx); + if (NS_WARN_IF(!scriptSample.init(aCx, aCodeString))) { return false; } if (!nsContentSecurityUtils::IsEvalAllowed( aCx, worker->UsesSystemPrincipal(), scriptSample)) { - return false; + *aOutCanCompileStrings = false; + return true; } evalOK = worker->IsEvalAllowed(); @@ -542,7 +549,8 @@ bool ContentSecurityPolicyAllows(JSContext* aCx, JS::RuntimeCode aKind, } } - return evalOK; + *aOutCanCompileStrings = evalOK; + return true; } void CTypesActivityCallback(JSContext* aCx, JS::CTypesActivityType aType) { diff --git a/js/public/Principals.h b/js/public/Principals.h index 1c633b132bd0..bce823b3062c 100644 --- a/js/public/Principals.h +++ b/js/public/Principals.h @@ -77,20 +77,35 @@ typedef bool (*JSSubsumesOp)(JSPrincipals* first, JSPrincipals* second); namespace JS { enum class RuntimeCode { JS, WASM }; +enum class CompilationType { DirectEval, IndirectEval, Function, Undefined }; } // namespace JS /* * Used to check if a CSP instance wants to disable eval() and friends. * See JSContext::isRuntimeCodeGenEnabled() in vm/JSContext.cpp. * - * `code` is the JavaScript source code passed to eval/Function, but nullptr - * for Wasm. + * codeString, compilationType, parameterStrings, bodyString, parameterArgs, + * and bodyArg are defined in the "Dynamic Code Brand Checks" spec + * (see https://tc39.es/proposal-dynamic-code-brand-checks). * - * Returning `false` from this callback will prevent the execution/compilation - * of the code. + * An Undefined compilationType is used for cases that are not covered by that + * spec and unused parameters are null/empty. Currently, this includes Wasm + * (only check if compilation is enabled) and ShadowRealmEval (only check + * codeString). + * + * `outCanCompileStrings` is set to false if this callback prevents the + * execution/compilation of the code and to true otherwise. + * + * Return false on failure, true on success. The |outCanCompileStrings| + * parameter should not be modified in case of failure. */ -typedef bool (*JSCSPEvalChecker)(JSContext* cx, JS::RuntimeCode kind, - JS::HandleString code); +typedef bool (*JSCSPEvalChecker)( + JSContext* cx, JS::RuntimeCode kind, JS::Handle codeString, + JS::CompilationType compilationType, + JS::Handle> parameterStrings, + JS::Handle bodyString, + JS::Handle> parameterArgs, + JS::Handle bodyArg, bool* outCanCompileStrings); /* * Provide a string of code from an Object argument, to be used by eval. diff --git a/js/src/builtin/Eval.cpp b/js/src/builtin/Eval.cpp index c23accdc5399..c5257cc35796 100644 --- a/js/src/builtin/Eval.cpp +++ b/js/src/builtin/Eval.cpp @@ -257,7 +257,17 @@ static bool EvalKernel(JSContext* cx, HandleValue v, EvalType evalType, } // Steps 6-8. - if (!cx->isRuntimeCodeGenEnabled(JS::RuntimeCode::JS, str)) { + JS::RootedVector parameterStrings(cx); + JS::RootedVector parameterArgs(cx); + bool canCompileStrings = false; + if (!cx->isRuntimeCodeGenEnabled( + JS::RuntimeCode::JS, str, + evalType == DIRECT_EVAL ? JS::CompilationType::DirectEval + : JS::CompilationType::IndirectEval, + parameterStrings, str, parameterArgs, v, &canCompileStrings)) { + return false; + } + if (!canCompileStrings) { JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CSP_BLOCKED_EVAL); return false; diff --git a/js/src/builtin/ShadowRealm.cpp b/js/src/builtin/ShadowRealm.cpp index 31ef507f5311..e4009a3f3777 100644 --- a/js/src/builtin/ShadowRealm.cpp +++ b/js/src/builtin/ShadowRealm.cpp @@ -196,7 +196,16 @@ static bool PerformShadowRealmEval(JSContext* cx, Handle sourceText, MOZ_ASSERT(callerRealm != evalRealm); // Step 1. Perform ? HostEnsureCanCompileStrings(callerRealm, evalRealm). - if (!cx->isRuntimeCodeGenEnabled(JS::RuntimeCode::JS, sourceText)) { + JS::RootedVector parameterStrings(cx); + JS::RootedVector parameterArgs(cx); + bool canCompileStrings = false; + if (!cx->isRuntimeCodeGenEnabled(JS::RuntimeCode::JS, sourceText, + JS::CompilationType::Undefined, + parameterStrings, nullptr, parameterArgs, + NullHandleValue, &canCompileStrings)) { + return false; + } + if (!canCompileStrings) { JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CSP_BLOCKED_SHADOWREALM); return false; diff --git a/js/src/jsapi-tests/testDynamicCodeBrandChecks.cpp b/js/src/jsapi-tests/testDynamicCodeBrandChecks.cpp index b114a0d05dc9..ca6fa1d5b7ec 100644 --- a/js/src/jsapi-tests/testDynamicCodeBrandChecks.cpp +++ b/js/src/jsapi-tests/testDynamicCodeBrandChecks.cpp @@ -92,3 +92,184 @@ BEGIN_TEST(testDynamicCodeBrandChecks_CustomHostGetCodeForEval) { return true; } END_TEST(testDynamicCodeBrandChecks_CustomHostGetCodeForEval) + +// This snippet defines a TrustedType that wraps some trustedCode string and +// stringifies to that string, as well as a helper to create a fake instance +// that can stringify to a different string. +const char* customTypesSnippet = + "function TrustedType(aTrustedCode) { this.trustedCode = aTrustedCode; };" + "TrustedType.prototype.toString = function() { return this.trustedCode; };" + "function CreateFakeTrustedType(aTrustedCode, aString) {" + " let fake = new TrustedType(aTrustedCode);" + " fake.toString = () => { return aString; };" + " return fake;" + "};"; + +BEGIN_TEST(testDynamicCodeBrandChecks_CustomHostEnsureCanCompileStrings) { + JSSecurityCallbacks securityCallbacksWithCustomHostEnsureCanCompileStrings = { + StringifiedObjectsMatchTrustedCodeProperties, // contentSecurityPolicyAllows + ExtractTrustedCodeStringProperty, // codeForEvalGets + nullptr // subsumes + }; + JS_SetSecurityCallbacks( + cx, &securityCallbacksWithCustomHostEnsureCanCompileStrings); + JS::RootedValue v(cx); + + EXEC(customTypesSnippet); + + // String arguments are evaluated. + EVAL("eval('5*8');", &v); + CHECK(v.isNumber() && v.toNumber() == 40); + EVAL("(new Function('a', 'b', 'return a * b'))(6, 7);", &v); + CHECK(v.isNumber() && v.toNumber() == 42); + + // The same works with TrustedType wrappers. + EVAL("eval(new TrustedType('5*8'));", &v); + CHECK(v.isNumber() && v.toNumber() == 40); + EVAL( + "(new Function(new TrustedType('a'), new TrustedType('b'), new " + "TrustedType('return a * b')))(6, 7);", + &v); + CHECK(v.isNumber() && v.toNumber() == 42); + + // new Function fails if one of the stringified argument does not match the + // trustedCode property. + CHECK(!execDontReport( + "new Function(CreateFakeTrustedType('a', 'c'), 'b', 'return b');", + __FILE__, __LINE__)); + cx->clearPendingException(); + CHECK(!execDontReport( + "new Function('a', CreateFakeTrustedType('b', 'c'), 'return a');", + __FILE__, __LINE__)); + cx->clearPendingException(); + CHECK( + !execDontReport("new Function('a', 'b', CreateFakeTrustedType('return a " + "* b', 'return a + b'));", + __FILE__, __LINE__)); + cx->clearPendingException(); + + // new Function also fails if StringifiedObjectsMatchTrustedCodeProperties + // returns false. + CHECK(!execDontReport("new Function('a', 'b', new TrustedType(undefined));", + __FILE__, __LINE__)); + cx->clearPendingException(); + + // PerformEval relies on ExtractTrustedCodeProperty rather than toString() to + // obtain the code to execute, so StringifiedObjectsMatchTrustedCodeProperties + // will always allow the code execution for the specified security callbacks. + EVAL("eval(CreateFakeTrustedType('5*8', '6*7'));", &v); + CHECK(v.isNumber() && v.toNumber() == 40); + EVAL("eval(new TrustedType(undefined));", &v); + CHECK(v.isObject()); + JS::RootedObject obj(cx, &v.toObject()); + JS::RootedValue trustedCode(cx); + CHECK(JS_GetProperty(cx, obj, "trustedCode", &trustedCode)); + CHECK(trustedCode.isUndefined()); + + return true; +} + +// This is a HostEnsureCanCompileStrings() implementation similar to some checks +// described in the CSP spec: verify that aBodyString and aParameterStrings +// match the corresponding trustedCode property on aBodyArg and aParameterArgs +// objects. See https://w3c.github.io/webappsec-csp/#can-compile-strings +static bool StringifiedObjectsMatchTrustedCodeProperties( + JSContext* aCx, JS::RuntimeCode aKind, JS::Handle aCodeString, + JS::CompilationType aCompilationType, + JS::Handle> aParameterStrings, + JS::Handle aBodyString, + JS::Handle> aParameterArgs, + JS::Handle aBodyArg, bool* aOutCanCompileStrings) { + bool isTrusted = true; + auto comparePropertyAndString = [&aCx, &isTrusted]( + JS::Handle aValue, + JS::Handle aString) { + if (!aValue.isObject()) { + // Just trust non-Objects. + return true; + } + JS::RootedObject obj(aCx, &aValue.toObject()); + + JS::RootedString trustedCode(aCx); + if (!ExtractTrustedCodeStringProperty(aCx, obj, &trustedCode)) { + // Propagate the failure. + return false; + } + if (!trustedCode) { + // Emulate a failure if trustedCode is undefined. + JS_ReportErrorASCII(aCx, "test failed, trustedCode property is undefined"); + return false; + } + bool equals; + if (!EqualStrings(aCx, trustedCode, aString, &equals)) { + // Propagate the failure. + return false; + } + if (!equals) { + isTrusted = false; + } + return true; + }; + if (!comparePropertyAndString(aBodyArg, aBodyString)) { + // Propagate the failure. + return false; + } + if (isTrusted) { + MOZ_ASSERT(aParameterArgs.length() == aParameterStrings.length()); + for (size_t index = 0; index < aParameterArgs.length(); index++) { + if (!comparePropertyAndString(aParameterArgs[index], + aParameterStrings[index])) { + // Propagate the failure. + return false; + } + if (!isTrusted) { + break; + } + } + } + // Allow compilation if arguments are trusted. + *aOutCanCompileStrings = isTrusted; + return true; +} + +END_TEST(testDynamicCodeBrandChecks_CustomHostEnsureCanCompileStrings) + +BEGIN_TEST(testDynamicCodeBrandChecks_RejectObjectForEval) { + JSSecurityCallbacks securityCallbacksRejectObjectBody = { + DisallowObjectsAndFailOtherwise, // contentSecurityPolicyAllows + ExtractTrustedCodeStringProperty, // codeForEvalGets + nullptr // subsumes + }; + + JS_SetSecurityCallbacks(cx, &securityCallbacksRejectObjectBody); + JS::RootedValue v(cx); + + EXEC(customTypesSnippet); + + // With the specified security callbacks, eval() will always fail. + CHECK(!execDontReport("eval('5*8))", __FILE__, __LINE__)); + cx->clearPendingException(); + CHECK(!execDontReport("eval(new TrustedType('5*8'))", __FILE__, __LINE__)); + cx->clearPendingException(); + + return true; +} + +static bool DisallowObjectsAndFailOtherwise( + JSContext* aCx, JS::RuntimeCode aKind, JS::Handle aCodeString, + JS::CompilationType aCompilationType, + JS::Handle> aParameterStrings, + JS::Handle aBodyString, + JS::Handle> aParameterArgs, + JS::Handle aBodyArg, bool* aOutCanCompileStrings) { + if (aBodyArg.isObject()) { + // Disallow compilation for objects. + *aOutCanCompileStrings = false; + return true; + } + // Otherwise, emulate a failure. + JS_ReportErrorASCII(aCx, "aBodyArg is not an Object"); + return false; +} + +END_TEST(testDynamicCodeBrandChecks_RejectObjectForEval) diff --git a/js/src/vm/JSContext.cpp b/js/src/vm/JSContext.cpp index 238987e4d20f..1bef06ced378 100644 --- a/js/src/vm/JSContext.cpp +++ b/js/src/vm/JSContext.cpp @@ -1233,15 +1233,24 @@ bool JSContext::isThrowingDebuggeeWouldRun() { JSEXN_DEBUGGEEWOULDRUN; } -bool JSContext::isRuntimeCodeGenEnabled(JS::RuntimeCode kind, - HandleString code) { +bool JSContext::isRuntimeCodeGenEnabled( + JS::RuntimeCode kind, JS::Handle codeString, + JS::CompilationType compilationType, + JS::Handle> parameterStrings, + JS::Handle bodyString, + JS::Handle> parameterArgs, + JS::Handle bodyArg, bool* outCanCompileStrings) { // Make sure that the CSP callback is installed and that it permits runtime // code generation. if (JSCSPEvalChecker allows = runtime()->securityCallbacks->contentSecurityPolicyAllows) { - return allows(this, kind, code); + return allows(this, kind, codeString, compilationType, parameterStrings, + bodyString, parameterArgs, bodyArg, outCanCompileStrings); } + // Default implementation from the "Dynamic Code Brand Checks" spec. + // https://tc39.es/proposal-dynamic-code-brand-checks/#sec-hostensurecancompilestrings + *outCanCompileStrings = true; return true; } diff --git a/js/src/vm/JSContext.h b/js/src/vm/JSContext.h index a480d9d017b0..3dba78ecec58 100644 --- a/js/src/vm/JSContext.h +++ b/js/src/vm/JSContext.h @@ -803,7 +803,13 @@ struct JS_PUBLIC_API JSContext : public JS::RootingContext, // Checks if the page's Content-Security-Policy (CSP) allows // runtime code generation "unsafe-eval", or "wasm-unsafe-eval" for Wasm. - bool isRuntimeCodeGenEnabled(JS::RuntimeCode kind, js::HandleString code); + bool isRuntimeCodeGenEnabled( + JS::RuntimeCode kind, JS::Handle codeString, + JS::CompilationType compilationType, + JS::Handle> parameterStrings, + JS::Handle bodyString, + JS::Handle> parameterArgs, + JS::Handle bodyArg, bool* outCanCompileStrings); // Get code to be used by eval for Object argument. bool getCodeForEval(JS::HandleObject code, diff --git a/js/src/vm/JSFunction.cpp b/js/src/vm/JSFunction.cpp index b55e5e308f1d..32319d22a2f4 100644 --- a/js/src/vm/JSFunction.cpp +++ b/js/src/vm/JSFunction.cpp @@ -1346,19 +1346,32 @@ static bool CreateDynamicFunction(JSContext* cx, const CallArgs& args, return false; } + JS::RootedVector parameterStrings(cx); + JS::RootedVector parameterArgs(cx); if (args.length() > 1) { RootedString str(cx); // Steps 10, 14.d. unsigned n = args.length() - 1; + if (!parameterStrings.reserve(n) || !parameterArgs.reserve(n)) { + return false; + } for (unsigned i = 0; i < n; i++) { + if (!parameterArgs.append(args[i])) { + return false; + } + // Steps 14.a-b, 14.d.i-ii. str = ToString(cx, args[i]); if (!str) { return false; } + if (!parameterStrings.append(str)) { + return false; + } + // Steps 14.b, 14.d.iii. if (!sb.append(str)) { return false; @@ -1386,10 +1399,13 @@ static bool CreateDynamicFunction(JSContext* cx, const CallArgs& args, return false; } + JS::RootedValue bodyArg(cx); + RootedString bodyString(cx); if (args.length() > 0) { // Steps 13, 14.e, 15. - RootedString body(cx, ToString(cx, args[args.length() - 1])); - if (!body || !sb.append(body)) { + bodyArg = args[args.length() - 1]; + bodyString = ToString(cx, bodyArg); + if (!bodyString || !sb.append(bodyString)) { return false; } } @@ -1410,7 +1426,14 @@ static bool CreateDynamicFunction(JSContext* cx, const CallArgs& args, } // Block this call if security callbacks forbid it. - if (!cx->isRuntimeCodeGenEnabled(JS::RuntimeCode::JS, functionText)) { + bool canCompileStrings = false; + if (!cx->isRuntimeCodeGenEnabled(JS::RuntimeCode::JS, functionText, + JS::CompilationType::Function, + parameterStrings, bodyString, parameterArgs, + bodyArg, &canCompileStrings)) { + return false; + } + if (!canCompileStrings) { JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CSP_BLOCKED_FUNCTION); return false; diff --git a/js/src/wasm/WasmJS.cpp b/js/src/wasm/WasmJS.cpp index f399178794f7..d91da4a4d469 100644 --- a/js/src/wasm/WasmJS.cpp +++ b/js/src/wasm/WasmJS.cpp @@ -1619,7 +1619,16 @@ bool WasmModuleObject::construct(JSContext* cx, unsigned argc, Value* vp) { return false; } - if (!cx->isRuntimeCodeGenEnabled(JS::RuntimeCode::WASM, nullptr)) { + JS::RootedVector parameterStrings(cx); + JS::RootedVector parameterArgs(cx); + bool canCompileStrings = false; + if (!cx->isRuntimeCodeGenEnabled(JS::RuntimeCode::WASM, nullptr, + JS::CompilationType::Undefined, + parameterStrings, nullptr, parameterArgs, + NullHandleValue, &canCompileStrings)) { + return false; + } + if (!canCompileStrings) { JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CSP_BLOCKED_WASM, "WebAssembly.Module"); return false; @@ -4437,7 +4446,16 @@ static bool WebAssembly_compile(JSContext* cx, unsigned argc, Value* vp) { CallArgs callArgs = CallArgsFromVp(argc, vp); - if (!cx->isRuntimeCodeGenEnabled(JS::RuntimeCode::WASM, nullptr)) { + JS::RootedVector parameterStrings(cx); + JS::RootedVector parameterArgs(cx); + bool canCompileStrings = false; + if (!cx->isRuntimeCodeGenEnabled(JS::RuntimeCode::WASM, nullptr, + JS::CompilationType::Undefined, + parameterStrings, nullptr, parameterArgs, + NullHandleValue, &canCompileStrings)) { + return RejectWithPendingException(cx, promise, callArgs); + } + if (!canCompileStrings) { JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CSP_BLOCKED_WASM, "WebAssembly.compile"); return RejectWithPendingException(cx, promise, callArgs); @@ -4521,7 +4539,16 @@ static bool WebAssembly_instantiate(JSContext* cx, unsigned argc, Value* vp) { return false; } } else { - if (!cx->isRuntimeCodeGenEnabled(JS::RuntimeCode::WASM, nullptr)) { + JS::RootedVector parameterStrings(cx); + JS::RootedVector parameterArgs(cx); + bool canCompileStrings = false; + if (!cx->isRuntimeCodeGenEnabled(JS::RuntimeCode::WASM, nullptr, + JS::CompilationType::Undefined, + parameterStrings, nullptr, parameterArgs, + NullHandleValue, &canCompileStrings)) { + return RejectWithPendingException(cx, promise, callArgs); + } + if (!canCompileStrings) { JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CSP_BLOCKED_WASM, "WebAssembly.instantiate"); @@ -5114,7 +5141,16 @@ static bool WebAssembly_compileStreaming(JSContext* cx, unsigned argc, CallArgs callArgs = CallArgsFromVp(argc, vp); - if (!cx->isRuntimeCodeGenEnabled(JS::RuntimeCode::WASM, nullptr)) { + JS::RootedVector parameterStrings(cx); + JS::RootedVector parameterArgs(cx); + bool canCompileStrings = false; + if (!cx->isRuntimeCodeGenEnabled(JS::RuntimeCode::WASM, nullptr, + JS::CompilationType::Undefined, + parameterStrings, nullptr, parameterArgs, + NullHandleValue, &canCompileStrings)) { + return RejectWithPendingException(cx, resultPromise, callArgs); + } + if (!canCompileStrings) { JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CSP_BLOCKED_WASM, "WebAssembly.compileStreaming"); @@ -5147,7 +5183,16 @@ static bool WebAssembly_instantiateStreaming(JSContext* cx, unsigned argc, CallArgs callArgs = CallArgsFromVp(argc, vp); - if (!cx->isRuntimeCodeGenEnabled(JS::RuntimeCode::WASM, nullptr)) { + JS::RootedVector parameterStrings(cx); + JS::RootedVector parameterArgs(cx); + bool canCompileStrings = false; + if (!cx->isRuntimeCodeGenEnabled(JS::RuntimeCode::WASM, nullptr, + JS::CompilationType::Undefined, + parameterStrings, nullptr, parameterArgs, + NullHandleValue, &canCompileStrings)) { + return RejectWithPendingException(cx, resultPromise, callArgs); + } + if (!canCompileStrings) { JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CSP_BLOCKED_WASM, "WebAssembly.instantiateStreaming"); diff --git a/js/src/wasm/WasmModule.cpp b/js/src/wasm/WasmModule.cpp index 3557e708a716..cb60c402fdf5 100644 --- a/js/src/wasm/WasmModule.cpp +++ b/js/src/wasm/WasmModule.cpp @@ -206,7 +206,16 @@ JSObject* Module::createObject(JSContext* cx) const { return nullptr; } - if (!cx->isRuntimeCodeGenEnabled(JS::RuntimeCode::WASM, nullptr)) { + JS::RootedVector parameterStrings(cx); + JS::RootedVector parameterArgs(cx); + bool canCompileStrings = false; + if (!cx->isRuntimeCodeGenEnabled(JS::RuntimeCode::WASM, nullptr, + JS::CompilationType::Undefined, + parameterStrings, nullptr, parameterArgs, + NullHandleValue, &canCompileStrings)) { + return nullptr; + } + if (!canCompileStrings) { JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CSP_BLOCKED_WASM, "WebAssembly.Module"); return nullptr;