Skip to content

Commit

Permalink
Bug 1905239 - Add new parameters to HostEnsureCanCompileStrings hook.…
Browse files Browse the repository at this point in the history
… r=tschuster

Currently, we do this via isRuntimeCodeGenEnabled whose single argument
is equivalent to codeString in "Dynamic Code Brand Checks" spec [1]. We
extend this hook to accept new parameters from that spec and adjust
PerformEval and CreateDynamicFunction accordingly. We don't change the
behavior for PerformShadowRealmEval [2] and WASM, i.e. we keep dummy
parameters.

[1] https://tc39.es/proposal-dynamic-code-brand-checks
[2] tc39/proposal-shadowrealm#414

Differential Revision: https://phabricator.services.mozilla.com/D229588
  • Loading branch information
fred-wang committed Dec 11, 2024
1 parent 84f0cb7 commit a72defa
Show file tree
Hide file tree
Showing 12 changed files with 361 additions and 37 deletions.
24 changes: 17 additions & 7 deletions caps/nsScriptSecurityManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -462,7 +464,12 @@ NS_IMPL_ISUPPORTS(nsScriptSecurityManager, nsIScriptSecurityManager)
///////////////// Security Checks /////////////////

bool nsScriptSecurityManager::ContentSecurityPolicyPermitsJSAction(
JSContext* cx, JS::RuntimeCode aKind, JS::Handle<JSString*> aCode) {
JSContext* cx, JS::RuntimeCode aKind, JS::Handle<JSString*> aCodeString,
JS::CompilationType aCompilationType,
JS::Handle<JS::StackGCVector<JSString*>> aParameterStrings,
JS::Handle<JSString*> aBodyString,
JS::Handle<JS::StackGCVector<JS::Value>> aParameterArgs,
JS::Handle<JS::Value> aBodyArg, bool* aOutCanCompileStrings) {
MOZ_ASSERT(cx == nsContentUtils::GetCurrentJSContext());

nsCOMPtr<nsIPrincipal> subjectPrincipal = nsContentUtils::SubjectPrincipal();
Expand All @@ -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;
}
}

Expand All @@ -503,6 +511,7 @@ bool nsScriptSecurityManager::ContentSecurityPolicyPermitsJSAction(
}
// don't do anything unless there's a CSP
if (!csp) {
*aOutCanCompileStrings = true;
return true;
}
}
Expand All @@ -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))) {
Expand All @@ -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 =
Expand All @@ -559,7 +568,8 @@ bool nsScriptSecurityManager::ContentSecurityPolicyPermitsJSAction(
caller.mLine, caller.mColumn, u""_ns, u""_ns);
}

return evalOK;
*aOutCanCompileStrings = evalOK;
return true;
}

// static
Expand Down
11 changes: 8 additions & 3 deletions caps/nsScriptSecurityManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ class SystemPrincipal;

namespace JS {
enum class RuntimeCode;
enum class CompilationType;
} // namespace JS

/////////////////////////////
Expand Down Expand Up @@ -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<JSString*> aCode);
static bool ContentSecurityPolicyPermitsJSAction(
JSContext* aCx, JS::RuntimeCode aKind, JS::Handle<JSString*> aCodeString,
JS::CompilationType aCompilationType,
JS::Handle<JS::StackGCVector<JSString*>> aParameterStrings,
JS::Handle<JSString*> aBodyString,
JS::Handle<JS::StackGCVector<JS::Value>> aParameterArgs,
JS::Handle<JS::Value> aBodyArg, bool* aOutCanCompileStrings);

static bool JSPrincipalsSubsume(JSPrincipals* first, JSPrincipals* second);

Expand Down
20 changes: 14 additions & 6 deletions dom/workers/RuntimeService.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -499,8 +501,13 @@ class LogViolationDetailsRunnable final : public WorkerMainThreadRunnable {
~LogViolationDetailsRunnable() = default;
};

bool ContentSecurityPolicyAllows(JSContext* aCx, JS::RuntimeCode aKind,
JS::Handle<JSString*> aCode) {
bool ContentSecurityPolicyAllows(
JSContext* aCx, JS::RuntimeCode aKind, JS::Handle<JSString*> aCodeString,
JS::CompilationType aCompilationType,
JS::Handle<JS::StackGCVector<JSString*>> aParameterStrings,
JS::Handle<JSString*> aBodyString,
JS::Handle<JS::StackGCVector<JS::Value>> aParameterArgs,
JS::Handle<JS::Value> aBodyArg, bool* aOutCanCompileStrings) {
WorkerPrivate* worker = GetWorkerPrivateFromContext(aCx);
worker->AssertIsOnWorkerThread();

Expand All @@ -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();
Expand All @@ -542,7 +549,8 @@ bool ContentSecurityPolicyAllows(JSContext* aCx, JS::RuntimeCode aKind,
}
}

return evalOK;
*aOutCanCompileStrings = evalOK;
return true;
}

void CTypesActivityCallback(JSContext* aCx, JS::CTypesActivityType aType) {
Expand Down
27 changes: 21 additions & 6 deletions js/public/Principals.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<JSString*> codeString,
JS::CompilationType compilationType,
JS::Handle<JS::StackGCVector<JSString*>> parameterStrings,
JS::Handle<JSString*> bodyString,
JS::Handle<JS::StackGCVector<JS::Value>> parameterArgs,
JS::Handle<JS::Value> bodyArg, bool* outCanCompileStrings);

/*
* Provide a string of code from an Object argument, to be used by eval.
Expand Down
12 changes: 11 additions & 1 deletion js/src/builtin/Eval.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<JSString*> parameterStrings(cx);
JS::RootedVector<Value> 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;
Expand Down
11 changes: 10 additions & 1 deletion js/src/builtin/ShadowRealm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,16 @@ static bool PerformShadowRealmEval(JSContext* cx, Handle<JSString*> sourceText,
MOZ_ASSERT(callerRealm != evalRealm);

// Step 1. Perform ? HostEnsureCanCompileStrings(callerRealm, evalRealm).
if (!cx->isRuntimeCodeGenEnabled(JS::RuntimeCode::JS, sourceText)) {
JS::RootedVector<JSString*> parameterStrings(cx);
JS::RootedVector<Value> 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;
Expand Down
Loading

0 comments on commit a72defa

Please sign in to comment.