123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211 |
- /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
- #include "JavaScriptParent.h"
- #include "mozilla/dom/ContentParent.h"
- #include "mozilla/dom/ScriptSettings.h"
- #include "nsJSUtils.h"
- #include "nsIScriptError.h"
- #include "jsfriendapi.h"
- #include "jswrapper.h"
- #include "js/Proxy.h"
- #include "js/HeapAPI.h"
- #include "xpcprivate.h"
- #include "mozilla/Casting.h"
- #include "mozilla/Unused.h"
- #include "nsAutoPtr.h"
- using namespace js;
- using namespace JS;
- using namespace mozilla;
- using namespace mozilla::jsipc;
- using namespace mozilla::dom;
- static void
- TraceParent(JSTracer* trc, void* data)
- {
- static_cast<JavaScriptParent*>(data)->trace(trc);
- }
- JavaScriptParent::~JavaScriptParent()
- {
- JS_RemoveExtraGCRootsTracer(danger::GetJSContext(), TraceParent, this);
- }
- bool
- JavaScriptParent::init()
- {
- if (!WrapperOwner::init())
- return false;
- JS_AddExtraGCRootsTracer(danger::GetJSContext(), TraceParent, this);
- return true;
- }
- static bool
- ForbidUnsafeBrowserCPOWs()
- {
- static bool result;
- static bool cached = false;
- if (!cached) {
- cached = true;
- Preferences::AddBoolVarCache(&result, "dom.ipc.cpows.forbid-unsafe-from-browser", false);
- }
- return result;
- }
- // Should we allow CPOWs in aAddonId, even though it's marked as multiprocess
- // compatible? This is controlled by two prefs:
- // If dom.ipc.cpows.forbid-cpows-in-compat-addons is false, then we allow the CPOW.
- // If dom.ipc.cpows.forbid-cpows-in-compat-addons is true:
- // We check if aAddonId is listed in dom.ipc.cpows.allow-cpows-in-compat-addons
- // (which should be a comma-separated string). If it's present there, we allow
- // the CPOW. Otherwise we forbid the CPOW.
- static bool
- ForbidCPOWsInCompatibleAddon(const nsACString& aAddonId)
- {
- bool forbid = Preferences::GetBool("dom.ipc.cpows.forbid-cpows-in-compat-addons", false);
- if (!forbid) {
- return false;
- }
- nsCString allow;
- allow.Assign(',');
- allow.Append(Preferences::GetCString("dom.ipc.cpows.allow-cpows-in-compat-addons"));
- allow.Append(',');
- nsCString searchString(",");
- searchString.Append(aAddonId);
- searchString.Append(',');
- return allow.Find(searchString) == kNotFound;
- }
- bool
- JavaScriptParent::allowMessage(JSContext* cx)
- {
- // If we're running browser code, then we allow all safe CPOWs and forbid
- // unsafe CPOWs based on a pref (which defaults to forbidden). We also allow
- // CPOWs unconditionally in selected globals (based on
- // Cu.permitCPOWsInScope).
- //
- // If we're running add-on code, then we check if the add-on is multiprocess
- // compatible (which eventually translates to a given setting of allowCPOWs
- // on the scopw). If it's not compatible, then we allow the CPOW but
- // warn. If it is marked as compatible, then we check the
- // ForbidCPOWsInCompatibleAddon; see the comment there.
- MessageChannel* channel = GetIPCChannel();
- bool isSafe = channel->IsInTransaction();
- bool warn = !isSafe;
- nsIGlobalObject* global = dom::GetIncumbentGlobal();
- JSObject* jsGlobal = global ? global->GetGlobalJSObject() : nullptr;
- if (jsGlobal) {
- JSAutoCompartment ac(cx, jsGlobal);
- JSAddonId* addonId = JS::AddonIdOfObject(jsGlobal);
- if (!xpc::CompartmentPrivate::Get(jsGlobal)->allowCPOWs) {
- if (!addonId && ForbidUnsafeBrowserCPOWs() && !isSafe) {
- JS_ReportErrorASCII(cx, "unsafe CPOW usage forbidden");
- return false;
- }
- if (addonId) {
- JSFlatString* flat = JS_ASSERT_STRING_IS_FLAT(JS::StringOfAddonId(addonId));
- nsString addonIdString;
- AssignJSFlatString(addonIdString, flat);
- NS_ConvertUTF16toUTF8 addonIdCString(addonIdString);
- if (ForbidCPOWsInCompatibleAddon(addonIdCString)) {
- JS_ReportErrorASCII(cx, "CPOW usage forbidden in this add-on");
- return false;
- }
- warn = true;
- }
- }
- }
- if (!warn)
- return true;
- static bool disableUnsafeCPOWWarnings = PR_GetEnv("DISABLE_UNSAFE_CPOW_WARNINGS");
- if (!disableUnsafeCPOWWarnings) {
- nsCOMPtr<nsIConsoleService> console(do_GetService(NS_CONSOLESERVICE_CONTRACTID));
- if (console && cx) {
- nsAutoString filename;
- uint32_t lineno = 0, column = 0;
- nsJSUtils::GetCallingLocation(cx, filename, &lineno, &column);
- nsCOMPtr<nsIScriptError> error(do_CreateInstance(NS_SCRIPTERROR_CONTRACTID));
- error->Init(NS_LITERAL_STRING("unsafe/forbidden CPOW usage"), filename,
- EmptyString(), lineno, column,
- nsIScriptError::warningFlag, "chrome javascript");
- console->LogMessage(error);
- } else {
- NS_WARNING("Unsafe synchronous IPC message");
- }
- }
- return true;
- }
- void
- JavaScriptParent::trace(JSTracer* trc)
- {
- objects_.trace(trc);
- unwaivedObjectIds_.trace(trc);
- waivedObjectIds_.trace(trc);
- }
- JSObject*
- JavaScriptParent::scopeForTargetObjects()
- {
- // CPWOWs from the child need to point into the parent's unprivileged junk
- // scope so that a compromised child cannot compromise the parent. In
- // practice, this means that a child process can only (a) hold parent
- // objects alive and (b) invoke them if they are callable.
- return xpc::UnprivilegedJunkScope();
- }
- void
- JavaScriptParent::afterProcessTask()
- {
- if (savedNextCPOWNumber_ == nextCPOWNumber_)
- return;
- savedNextCPOWNumber_ = nextCPOWNumber_;
- MOZ_ASSERT(nextCPOWNumber_ > 0);
- if (active())
- Unused << SendDropTemporaryStrongReferences(nextCPOWNumber_ - 1);
- }
- PJavaScriptParent*
- mozilla::jsipc::NewJavaScriptParent()
- {
- JavaScriptParent* parent = new JavaScriptParent();
- if (!parent->init()) {
- delete parent;
- return nullptr;
- }
- return parent;
- }
- void
- mozilla::jsipc::ReleaseJavaScriptParent(PJavaScriptParent* parent)
- {
- static_cast<JavaScriptParent*>(parent)->decref();
- }
- void
- mozilla::jsipc::AfterProcessTask()
- {
- for (auto* cp : ContentParent::AllProcesses(ContentParent::eLive)) {
- if (PJavaScriptParent* p = LoneManagedOrNullAsserts(cp->ManagedPJavaScriptParent()))
- static_cast<JavaScriptParent*>(p)->afterProcessTask();
- }
- }
|