From aa70c31991de9c3385a68f2da0883207d5397965 Mon Sep 17 00:00:00 2001 From: Marco Castelluccio Date: Fri, 13 Dec 2024 03:43:40 +0000 Subject: [PATCH] Bug 1905239 - Introduce HostGetCodeForEval hook for PerformEval. r=tschuster See https://tc39.es/proposal-dynamic-code-brand-checks Differential Revision: https://phabricator.services.mozilla.com/D229477 UltraBlame original commit: b575160525c1fe6d5beab39f995e88ba83df06a3 --- caps/nsScriptSecurityManager.cpp | 1 + js/public/Principals.h | 29 + js/src/builtin/Eval.cpp | 70 ++- js/src/jsapi-tests/moz.build | 5 + .../testDynamicCodeBrandChecks.cpp | 592 ++++++++++++++++++ js/src/jsapi-tests/testStructuredClone.cpp | 1 + js/src/shell/js.cpp | 1 + js/src/vm/JSContext.cpp | 55 ++ js/src/vm/JSContext.h | 19 + 9 files changed, 763 insertions(+), 10 deletions(-) create mode 100644 js/src/jsapi-tests/testDynamicCodeBrandChecks.cpp diff --git a/caps/nsScriptSecurityManager.cpp b/caps/nsScriptSecurityManager.cpp index d4ec75da2522c..07ad4cdddc435 100644 --- a/caps/nsScriptSecurityManager.cpp +++ b/caps/nsScriptSecurityManager.cpp @@ -6987,6 +6987,7 @@ securityCallbacks = { ContentSecurityPolicyPermitsJSAction +nullptr JSPrincipalsSubsume } ; diff --git a/js/public/Principals.h b/js/public/Principals.h index a84c1ad2e2ff4..d68f7c2ae1f85 100644 --- a/js/public/Principals.h +++ b/js/public/Principals.h @@ -244,12 +244,41 @@ HandleString code ) ; +typedef +bool +( +* +JSCodeForEvalOp +) +( +JSContext +* +cx +JS +: +: +HandleObject +code +JS +: +: +MutableHandle +< +JSString +* +> +outCode +) +; struct JSSecurityCallbacks { JSCSPEvalChecker contentSecurityPolicyAllows ; +JSCodeForEvalOp +codeForEvalGets +; JSSubsumesOp subsumes ; diff --git a/js/src/builtin/Eval.cpp b/js/src/builtin/Eval.cpp index 408db73e66f4d..d30de400eb5d4 100644 --- a/js/src/builtin/Eval.cpp +++ b/js/src/builtin/Eval.cpp @@ -1312,9 +1312,14 @@ cx env ) ; +RootedString +str +( +cx +) +; if ( -! v . isString @@ -1322,24 +1327,33 @@ isString ) ) { -vp +str += +v . -set +toString ( -v ) ; -return -true -; } -RootedString -str +else +if +( +v +. +isObject +( +) +) +{ +RootedObject +obj ( cx +& v . -toString +toObject ( ) ) @@ -1350,6 +1364,42 @@ if cx - > +getCodeForEval +( +obj +& +str +) +) +{ +return +false +; +} +} +if +( +! +str +) +{ +vp +. +set +( +v +) +; +return +true +; +} +if +( +! +cx +- +> isRuntimeCodeGenEnabled ( JS diff --git a/js/src/jsapi-tests/moz.build b/js/src/jsapi-tests/moz.build index 96db338d2465e..f654584135e10 100644 --- a/js/src/jsapi-tests/moz.build +++ b/js/src/jsapi-tests/moz.build @@ -190,6 +190,11 @@ testDifferentNewTargetInvokeConstructor cpp " " +testDynamicCodeBrandChecks +. +cpp +" +" testEmptyWindowIsOmitted . cpp diff --git a/js/src/jsapi-tests/testDynamicCodeBrandChecks.cpp b/js/src/jsapi-tests/testDynamicCodeBrandChecks.cpp new file mode 100644 index 0000000000000..b04150a0c7f2f --- /dev/null +++ b/js/src/jsapi-tests/testDynamicCodeBrandChecks.cpp @@ -0,0 +1,592 @@ +# +include +" +jsapi +- +tests +/ +tests +. +h +" +BEGIN_TEST +( +testDynamicCodeBrandChecks_DefaultHostGetCodeForEval +) +{ +JS +: +: +RootedValue +v +( +cx +) +; +EVAL +( +" +eval +( +' +5 +* +8 +' +) +; +" +& +v +) +; +CHECK +( +v +. +isNumber +( +) +& +& +v +. +toNumber +( +) += += +40 +) +; +EVAL +( +" +eval +( +{ +myProp +: +41 +} +) +; +" +& +v +) +; +CHECK +( +v +. +isObject +( +) +) +; +JS +: +: +RootedObject +obj +( +cx +& +v +. +toObject +( +) +) +; +JS +: +: +RootedValue +myProp +( +cx +) +; +CHECK +( +JS_GetProperty +( +cx +obj +" +myProp +" +& +myProp +) +) +; +CHECK +( +myProp +. +isNumber +( +) +& +& +myProp +. +toNumber +( +) += += +41 +) +; +EVAL +( +" +eval +( +{ +trustedCode +: +' +6 +* +7 +' +} +) +. +trustedCode +; +" +& +v +) +; +CHECK +( +v +. +isString +( +) +) +; +JSString +* +str += +v +. +toString +( +) +; +CHECK +( +JS_LinearStringEqualsLiteral +( +JS_ASSERT_STRING_IS_LINEAR +( +str +) +" +6 +* +7 +" +) +) +; +EVAL +( +" +eval +( +{ +trustedCode +: +42 +} +) +. +trustedCode +; +" +& +v +) +; +CHECK +( +v +. +isNumber +( +) +& +& +v +. +toNumber +( +) += += +42 +) +; +return +true +; +} +END_TEST +( +testDynamicCodeBrandChecks_DefaultHostGetCodeForEval +) +static +bool +ExtractTrustedCodeStringProperty +( +JSContext +* +aCx +JS +: +: +Handle +< +JSObject +* +> +aCode +JS +: +: +MutableHandle +< +JSString +* +> +outCode +) +{ +JS +: +: +RootedValue +value +( +aCx +) +; +if +( +! +JS_GetProperty +( +aCx +aCode +" +trustedCode +" +& +value +) +) +{ +return +false +; +} +if +( +value +. +isUndefined +( +) +) +{ +outCode +. +set +( +nullptr +) +; +return +true +; +} +if +( +value +. +isString +( +) +) +{ +outCode +. +set +( +value +. +toString +( +) +) +; +return +true +; +} +JS_ReportErrorASCII +( +aCx +" +Unsupported +value +for +trustedCode +property +" +) +; +return +false +; +} +BEGIN_TEST +( +testDynamicCodeBrandChecks_CustomHostGetCodeForEval +) +{ +JSSecurityCallbacks +securityCallbacksWithEvalAcceptingObject += +{ +nullptr +ExtractTrustedCodeStringProperty +nullptr +} +; +JS_SetSecurityCallbacks +( +cx +& +securityCallbacksWithEvalAcceptingObject +) +; +JS +: +: +RootedValue +v +( +cx +) +; +EVAL +( +" +eval +( +' +5 +* +8 +' +) +; +" +& +v +) +; +CHECK +( +v +. +isNumber +( +) +& +& +v +. +toNumber +( +) += += +40 +) +; +EVAL +( +" +eval +( +{ +myProp +: +41 +} +) +; +" +& +v +) +; +CHECK +( +v +. +isObject +( +) +) +; +JS +: +: +RootedObject +obj +( +cx +& +v +. +toObject +( +) +) +; +JS +: +: +RootedValue +myProp +( +cx +) +; +CHECK +( +JS_GetProperty +( +cx +obj +" +myProp +" +& +myProp +) +) +; +CHECK +( +myProp +. +isNumber +( +) +& +& +myProp +. +toNumber +( +) += += +41 +) +; +EVAL +( +" +eval +( +{ +trustedCode +: +' +6 +* +7 +' +} +) +; +" +& +v +) +; +CHECK +( +v +. +isNumber +( +) +& +& +v +. +toNumber +( +) += += +6 +* +7 +) +; +CHECK +( +! +execDontReport +( +" +eval +( +{ +trustedCode +: +6 +* +7 +} +) +; +" +__FILE__ +__LINE__ +) +) +; +cx +- +> +clearPendingException +( +) +; +return +true +; +} +END_TEST +( +testDynamicCodeBrandChecks_CustomHostGetCodeForEval +) diff --git a/js/src/jsapi-tests/testStructuredClone.cpp b/js/src/jsapi-tests/testStructuredClone.cpp index 37f95e784d6b0..4676e3e1713e0 100644 --- a/js/src/jsapi-tests/testStructuredClone.cpp +++ b/js/src/jsapi-tests/testStructuredClone.cpp @@ -1473,6 +1473,7 @@ securityCallbacks = { nullptr +nullptr subsumes } ; diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index a5d06cfc03117..bb8ae231f9be7 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -5455,6 +5455,7 @@ securityCallbacks = { nullptr +nullptr subsumes } ; diff --git a/js/src/vm/JSContext.cpp b/js/src/vm/JSContext.cpp index 91687a071a7f6..b83fbddf55f46 100644 --- a/js/src/vm/JSContext.cpp +++ b/js/src/vm/JSContext.cpp @@ -6387,6 +6387,61 @@ return true ; } +bool +JSContext +: +: +getCodeForEval +( +HandleObject +code +JS +: +: +MutableHandle +< +JSString +* +> +outCode +) +{ +if +( +JSCodeForEvalOp +gets += +runtime +( +) +- +> +securityCallbacks +- +> +codeForEvalGets +) +{ +return +gets +( +this +code +outCode +) +; +} +outCode +. +set +( +nullptr +) +; +return +true +; +} size_t JSContext : diff --git a/js/src/vm/JSContext.h b/js/src/vm/JSContext.h index fce99b0160c54..ea8d94f8a2f24 100644 --- a/js/src/vm/JSContext.h +++ b/js/src/vm/JSContext.h @@ -3495,6 +3495,25 @@ HandleString code ) ; +bool +getCodeForEval +( +JS +: +: +HandleObject +code +JS +: +: +MutableHandle +< +JSString +* +> +outCode +) +; size_t sizeOfExcludingThis (