mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-27 06:43:32 +00:00
Bug 1809518 - Use stencil parsing to do the Javascript check for ORB in Utility Process r=farre,smaug,tcampbell
This patch allows JS Validator to parse the incoming data into stencil to verify if its a Javascript file. Differential Revision: https://phabricator.services.mozilla.com/D166484
This commit is contained in:
parent
4d890942b8
commit
81c8e77c0d
@ -15,6 +15,28 @@ using namespace mozilla::dom;
|
||||
|
||||
static mozilla::StaticRefPtr<JSOracleChild> sOracleSingletonChild;
|
||||
|
||||
static mozilla::StaticAutoPtr<JSContextHolder> sJSContextHolder;
|
||||
|
||||
/* static */
|
||||
void JSContextHolder::MaybeInit() {
|
||||
if (!sJSContextHolder) {
|
||||
sJSContextHolder = new JSContextHolder();
|
||||
ClearOnShutdown(&sJSContextHolder);
|
||||
}
|
||||
}
|
||||
|
||||
/* static */
|
||||
JSContext* JSOracleChild::JSContext() {
|
||||
MOZ_ASSERT(sJSContextHolder);
|
||||
return sJSContextHolder->mCx;
|
||||
}
|
||||
|
||||
/* static */
|
||||
JSObject* JSOracleChild::JSObject() {
|
||||
MOZ_ASSERT(sJSContextHolder);
|
||||
return sJSContextHolder->mGlobal;
|
||||
}
|
||||
|
||||
JSOracleChild* JSOracleChild::GetSingleton() {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (!sOracleSingletonChild) {
|
||||
@ -30,5 +52,6 @@ already_AddRefed<PJSValidatorChild> JSOracleChild::AllocPJSValidatorChild() {
|
||||
|
||||
void JSOracleChild::Start(Endpoint<PJSOracleChild>&& aEndpoint) {
|
||||
DebugOnly<bool> ok = std::move(aEndpoint).Bind(this);
|
||||
JSContextHolder::MaybeInit();
|
||||
MOZ_ASSERT(ok);
|
||||
}
|
||||
|
@ -9,10 +9,60 @@
|
||||
|
||||
#include "mozilla/dom/PJSOracleChild.h"
|
||||
|
||||
#include "js/CharacterEncoding.h"
|
||||
#include "js/HeapAPI.h"
|
||||
#include "js/Initialization.h"
|
||||
#include "jsapi.h"
|
||||
#include "js/CompilationAndEvaluation.h"
|
||||
#include "js/Context.h"
|
||||
|
||||
namespace mozilla::ipc {
|
||||
class UtilityProcessParent;
|
||||
}
|
||||
|
||||
namespace mozilla::dom {
|
||||
struct JSContextHolder {
|
||||
JSContextHolder() {
|
||||
MOZ_RELEASE_ASSERT(JS_IsInitialized(),
|
||||
"UtilityProcessChild::Init should have JS initialized");
|
||||
|
||||
mCx = JS_NewContext(JS::DefaultHeapMaxBytes);
|
||||
if (!mCx) {
|
||||
MOZ_CRASH("Failed to create JS Context");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!JS::InitSelfHostedCode(mCx)) {
|
||||
MOZ_CRASH("Failed to initialize the runtime's self-hosted code");
|
||||
return;
|
||||
}
|
||||
|
||||
static JSClass jsValidatorGlobalClass = {
|
||||
"JSValidatorGlobal", JSCLASS_GLOBAL_FLAGS, &JS::DefaultGlobalClassOps};
|
||||
|
||||
JS::Rooted<JSObject*> global(
|
||||
mCx, JS_NewGlobalObject(mCx, &jsValidatorGlobalClass, nullptr,
|
||||
JS::FireOnNewGlobalHook, JS::RealmOptions()));
|
||||
|
||||
if (!global) {
|
||||
MOZ_CRASH("Failed to create the global");
|
||||
return;
|
||||
}
|
||||
|
||||
mGlobal.init(mCx, global);
|
||||
}
|
||||
|
||||
~JSContextHolder() {
|
||||
if (mCx) {
|
||||
JS_DestroyContext(mCx);
|
||||
}
|
||||
}
|
||||
|
||||
static void MaybeInit();
|
||||
|
||||
JSContext* mCx;
|
||||
JS::PersistentRooted<JSObject*> mGlobal;
|
||||
};
|
||||
|
||||
class PJSValidatorChild;
|
||||
|
||||
@ -24,6 +74,9 @@ class JSOracleChild final : public PJSOracleChild {
|
||||
|
||||
void Start(Endpoint<PJSOracleChild>&& aEndpoint);
|
||||
|
||||
static struct JSContext* JSContext();
|
||||
static class JSObject* JSObject();
|
||||
|
||||
private:
|
||||
~JSOracleChild() = default;
|
||||
|
||||
|
@ -5,8 +5,18 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "mozilla/dom/JSValidatorChild.h"
|
||||
#include "js/JSON.h"
|
||||
#include "mozilla/dom/JSOracleChild.h"
|
||||
|
||||
#include "mozilla/ipc/Endpoint.h"
|
||||
|
||||
#include "js/experimental/JSStencil.h"
|
||||
#include "js/SourceText.h"
|
||||
#include "js/Exception.h"
|
||||
#include "js/GlobalObject.h"
|
||||
#include "js/CompileOptions.h"
|
||||
#include "js/RealmOptions.h"
|
||||
|
||||
using namespace mozilla::dom;
|
||||
|
||||
mozilla::ipc::IPCResult JSValidatorChild::RecvIsOpaqueResponseAllowed(
|
||||
@ -72,16 +82,42 @@ void JSValidatorChild::Resolve(ValidatorResult aResult) {
|
||||
}
|
||||
|
||||
JSValidatorChild::ValidatorResult JSValidatorChild::ShouldAllowJS() const {
|
||||
// mSourceBytes could be empty when
|
||||
// 1. No OnDataAvailable calls
|
||||
// 2. Failed to allocate shmem
|
||||
|
||||
// The empty document parses as JavaScript, so for clarity we have a condition
|
||||
// separately for that.
|
||||
if (mSourceBytes.IsEmpty()) {
|
||||
return ValidatorResult::JavaScript;
|
||||
}
|
||||
|
||||
JSContext* cx = JSOracleChild::JSContext();
|
||||
if (!cx) {
|
||||
return ValidatorResult::Failure;
|
||||
}
|
||||
|
||||
JS::Rooted<JSObject*> global(cx, JSOracleChild::JSObject());
|
||||
if (!global) {
|
||||
return ValidatorResult::Failure;
|
||||
}
|
||||
|
||||
JS::SourceText<Utf8Unit> srcBuf;
|
||||
if (!srcBuf.init(cx, mSourceBytes.BeginReading(), mSourceBytes.Length(),
|
||||
JS::SourceOwnership::Borrowed)) {
|
||||
JS_ClearPendingException(cx);
|
||||
return ValidatorResult::Failure;
|
||||
}
|
||||
|
||||
JSAutoRealm ar(cx, global);
|
||||
|
||||
// Parse to JavaScript
|
||||
RefPtr<JS::Stencil> stencil =
|
||||
CompileGlobalScriptToStencil(cx, JS::CompileOptions(cx), srcBuf);
|
||||
|
||||
if (!stencil) {
|
||||
JS_ClearPendingException(cx);
|
||||
return ValidatorResult::Other;
|
||||
}
|
||||
|
||||
// The stencil parsing lacks the JSON support, so we have to do the
|
||||
// JSON parsing separately.
|
||||
if (StringBeginsWith(NS_ConvertUTF8toUTF16(mSourceBytes), u"{"_ns)) {
|
||||
return ValidatorResult::JSON;
|
||||
}
|
||||
|
@ -39,6 +39,9 @@ void JSValidatorParent::IsOpaqueResponseAllowed(
|
||||
Tie(data, result) = aResult.ResolveValue();
|
||||
aCallback(std::move(data), result);
|
||||
} else {
|
||||
// For cases like the Utility Process crashes, the promise will be
|
||||
// rejected due to sending failures, and we'll block the request
|
||||
// since we can't validate it.
|
||||
aCallback(Nothing(), ValidatorResult::Failure);
|
||||
}
|
||||
});
|
||||
|
@ -110,6 +110,15 @@ bool UtilityProcessChild::Init(mozilla::ipc::UntypedEndpoint&& aEndpoint,
|
||||
|
||||
mSandbox = (SandboxingKind)aSandboxingKind;
|
||||
|
||||
// At the moment, only ORB uses JSContext in the
|
||||
// Utility Process and ORB uses GENERIC_UTILITY
|
||||
if (mSandbox == SandboxingKind::GENERIC_UTILITY) {
|
||||
JS::DisableJitBackend();
|
||||
if (!JS_Init()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
profiler_set_process_name(nsCString("Utility Process"));
|
||||
|
||||
// Notify the parent process that we have finished our init and that it can
|
||||
@ -117,9 +126,12 @@ bool UtilityProcessChild::Init(mozilla::ipc::UntypedEndpoint&& aEndpoint,
|
||||
SendInitCompleted();
|
||||
|
||||
RunOnShutdown(
|
||||
[] {
|
||||
[sandboxKind = mSandbox] {
|
||||
StaticMutexAutoLock lock(sUtilityProcessChildMutex);
|
||||
sUtilityProcessChild = nullptr;
|
||||
if (sandboxKind == SandboxingKind::GENERIC_UTILITY) {
|
||||
JS_ShutDown();
|
||||
}
|
||||
},
|
||||
ShutdownPhase::XPCOMShutdownFinal);
|
||||
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include "mozilla/RandomNum.h"
|
||||
#include "mozilla/TaggedAnonymousMemory.h"
|
||||
|
||||
#include "jit/JitOptions.h"
|
||||
#include "js/HeapAPI.h"
|
||||
#include "js/Utility.h"
|
||||
#include "util/Memory.h"
|
||||
@ -402,10 +403,12 @@ void InitMemorySubsystem() {
|
||||
numAddressBits = 32;
|
||||
#endif
|
||||
#ifdef RLIMIT_AS
|
||||
rlimit as_limit;
|
||||
if (getrlimit(RLIMIT_AS, &as_limit) == 0 &&
|
||||
as_limit.rlim_max != RLIM_INFINITY) {
|
||||
virtualMemoryLimit = as_limit.rlim_max;
|
||||
if (jit::HasJitBackend()) {
|
||||
rlimit as_limit;
|
||||
if (getrlimit(RLIMIT_AS, &as_limit) == 0 &&
|
||||
as_limit.rlim_max != RLIM_INFINITY) {
|
||||
virtualMemoryLimit = as_limit.rlim_max;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
@ -1,5 +0,0 @@
|
||||
[script-html-correctly-labeled.tentative.sub.html]
|
||||
expected:
|
||||
if (os == "android") and fission: [OK, TIMEOUT]
|
||||
[CORB-blocked script has no syntax errors]
|
||||
expected: FAIL
|
@ -2,21 +2,9 @@
|
||||
prefs: [browser.opaqueResponseBlocking:true, browser.opaqueResponseBlocking.javascriptValidator:true]
|
||||
expected:
|
||||
if (os == "android") and fission: [OK, TIMEOUT]
|
||||
[CORB-blocks 'text/html' that starts with the following JSON parser breaker: )\]}']
|
||||
expected: FAIL
|
||||
|
||||
[CORB-blocks 'application/javascript' that starts with the following JSON parser breaker: )\]}']
|
||||
expected: FAIL
|
||||
|
||||
[CORB-blocks 'text/xml' that starts with the following JSON parser breaker: )\]}']
|
||||
expected: FAIL
|
||||
|
||||
[CORB-blocks 'text/plain' that starts with the following JSON parser breaker: )\]}']
|
||||
expected: FAIL
|
||||
|
||||
[CORB-blocks 'text/json' that starts with the following JSON parser breaker: )\]}']
|
||||
expected: FAIL
|
||||
|
||||
[CORB-blocks 'application/javascript' that starts with the following JSON parser breaker: {}&&]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -1 +1 @@
|
||||
prefs: [browser.opaqueResponseBlocking:true]
|
||||
prefs: [browser.opaqueResponseBlocking:true, browser.opaqueResponseBlocking.javascriptValidator:true]
|
||||
|
@ -1,30 +0,0 @@
|
||||
[known-mime-type.sub.any.worker.html]
|
||||
prefs: [browser.opaqueResponseBlocking.javascriptValidator:true]
|
||||
[ORB should block opaque font/ttf]
|
||||
expected: FAIL
|
||||
|
||||
[ORB should block opaque text/plain]
|
||||
expected: FAIL
|
||||
|
||||
[ORB should block opaque application/json]
|
||||
expected:
|
||||
if not debug and (os == "mac"): [PASS, FAIL]
|
||||
if not debug and (os == "linux"): [PASS, FAIL]
|
||||
if not debug and (os == "android"): [PASS, FAIL]
|
||||
|
||||
|
||||
[known-mime-type.sub.any.html]
|
||||
prefs: [browser.opaqueResponseBlocking.javascriptValidator:true]
|
||||
expected:
|
||||
if (os == "android") and fission: TIMEOUT
|
||||
[ORB should block opaque font/ttf]
|
||||
expected: FAIL
|
||||
|
||||
[ORB should block opaque text/plain]
|
||||
expected: FAIL
|
||||
|
||||
[ORB should block opaque application/json]
|
||||
expected:
|
||||
if not debug and (os == "linux"): [PASS, FAIL]
|
||||
if not debug and (os == "android"): [PASS, FAIL]
|
||||
if not debug and (os == "mac"): [PASS, FAIL]
|
@ -0,0 +1 @@
|
||||
{}
|
@ -29,7 +29,17 @@ promise_test(
|
||||
TypeError,
|
||||
fetchORB(`${path}/data.json`, null, contentType("application/json"))
|
||||
),
|
||||
"ORB should block opaque application/json"
|
||||
"ORB should block opaque application/json (non-empty)"
|
||||
);
|
||||
|
||||
promise_test(
|
||||
t =>
|
||||
promise_rejects_js(
|
||||
t,
|
||||
TypeError,
|
||||
fetchORB(`${path}/empty.json`, null, contentType("application/json"))
|
||||
),
|
||||
"ORB should block opaque application/json (empty)"
|
||||
);
|
||||
|
||||
promise_test(async () => {
|
||||
|
Loading…
Reference in New Issue
Block a user