LCOV - code coverage report
Current view: top level - js/ipc - JavaScriptParent.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 28 96 29.2 %
Date: 2017-07-14 16:53:18 Functions: 6 14 42.9 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
       2             :  * vim: set ts=4 sw=4 et tw=80:
       3             :  *
       4             :  * This Source Code Form is subject to the terms of the Mozilla Public
       5             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       6             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       7             : 
       8             : #include "JavaScriptParent.h"
       9             : #include "mozilla/dom/ContentParent.h"
      10             : #include "mozilla/dom/ScriptSettings.h"
      11             : #include "nsJSUtils.h"
      12             : #include "nsIScriptError.h"
      13             : #include "jsfriendapi.h"
      14             : #include "jswrapper.h"
      15             : #include "js/Proxy.h"
      16             : #include "js/HeapAPI.h"
      17             : #include "xpcprivate.h"
      18             : #include "mozilla/Casting.h"
      19             : #include "mozilla/Telemetry.h"
      20             : #include "nsAutoPtr.h"
      21             : 
      22             : using namespace js;
      23             : using namespace JS;
      24             : using namespace mozilla;
      25             : using namespace mozilla::jsipc;
      26             : using namespace mozilla::dom;
      27             : 
      28             : static void
      29           1 : TraceParent(JSTracer* trc, void* data)
      30             : {
      31           1 :     static_cast<JavaScriptParent*>(data)->trace(trc);
      32           1 : }
      33             : 
      34           0 : JavaScriptParent::~JavaScriptParent()
      35             : {
      36           0 :     JS_RemoveExtraGCRootsTracer(danger::GetJSContext(), TraceParent, this);
      37           0 : }
      38             : 
      39             : bool
      40           1 : JavaScriptParent::init()
      41             : {
      42           1 :     if (!WrapperOwner::init())
      43           0 :         return false;
      44             : 
      45           1 :     JS_AddExtraGCRootsTracer(danger::GetJSContext(), TraceParent, this);
      46           1 :     return true;
      47             : }
      48             : 
      49             : static bool
      50           0 : ForbidUnsafeBrowserCPOWs()
      51             : {
      52             :     static bool result;
      53             :     static bool cached = false;
      54           0 :     if (!cached) {
      55           0 :         cached = true;
      56           0 :         Preferences::AddBoolVarCache(&result, "dom.ipc.cpows.forbid-unsafe-from-browser", false);
      57             :     }
      58           0 :     return result;
      59             : }
      60             : 
      61             : // Should we allow CPOWs in aAddonId, even though it's marked as multiprocess
      62             : // compatible? This is controlled by two prefs:
      63             : //   If dom.ipc.cpows.forbid-cpows-in-compat-addons is false, then we allow the CPOW.
      64             : //   If dom.ipc.cpows.forbid-cpows-in-compat-addons is true:
      65             : //     We check if aAddonId is listed in dom.ipc.cpows.allow-cpows-in-compat-addons
      66             : //     (which should be a comma-separated string). If it's present there, we allow
      67             : //     the CPOW. Otherwise we forbid the CPOW.
      68             : static bool
      69           0 : ForbidCPOWsInCompatibleAddon(const nsACString& aAddonId)
      70             : {
      71           0 :     bool forbid = Preferences::GetBool("dom.ipc.cpows.forbid-cpows-in-compat-addons", false);
      72           0 :     if (!forbid) {
      73           0 :         return false;
      74             :     }
      75             : 
      76           0 :     nsCString allow;
      77           0 :     allow.Assign(',');
      78           0 :     allow.Append(Preferences::GetCString("dom.ipc.cpows.allow-cpows-in-compat-addons"));
      79           0 :     allow.Append(',');
      80             : 
      81           0 :     nsCString searchString(",");
      82           0 :     searchString.Append(aAddonId);
      83           0 :     searchString.Append(',');
      84           0 :     return allow.Find(searchString) == kNotFound;
      85             : }
      86             : 
      87             : bool
      88           0 : JavaScriptParent::allowMessage(JSContext* cx)
      89             : {
      90             :     // If we're running browser code, then we allow all safe CPOWs and forbid
      91             :     // unsafe CPOWs based on a pref (which defaults to forbidden). We also allow
      92             :     // CPOWs unconditionally in selected globals (based on
      93             :     // Cu.permitCPOWsInScope).
      94             :     //
      95             :     // If we're running add-on code, then we check if the add-on is multiprocess
      96             :     // compatible (which eventually translates to a given setting of allowCPOWs
      97             :     // on the scopw). If it's not compatible, then we allow the CPOW but
      98             :     // warn. If it is marked as compatible, then we check the
      99             :     // ForbidCPOWsInCompatibleAddon; see the comment there.
     100             : 
     101           0 :     MessageChannel* channel = GetIPCChannel();
     102           0 :     bool isSafe = channel->IsInTransaction();
     103             : 
     104           0 :     bool warn = !isSafe;
     105           0 :     nsIGlobalObject* global = dom::GetIncumbentGlobal();
     106           0 :     JSObject* jsGlobal = global ? global->GetGlobalJSObject() : nullptr;
     107           0 :     if (jsGlobal) {
     108           0 :         JSAutoCompartment ac(cx, jsGlobal);
     109           0 :         JSAddonId* addonId = JS::AddonIdOfObject(jsGlobal);
     110             : 
     111           0 :         if (!xpc::CompartmentPrivate::Get(jsGlobal)->allowCPOWs) {
     112           0 :             if (!addonId && ForbidUnsafeBrowserCPOWs() && !isSafe) {
     113           0 :                 Telemetry::Accumulate(Telemetry::BROWSER_SHIM_USAGE_BLOCKED, 1);
     114           0 :                 JS_ReportErrorASCII(cx, "unsafe CPOW usage forbidden");
     115           0 :                 return false;
     116             :             }
     117             : 
     118           0 :             if (addonId) {
     119           0 :                 JSFlatString* flat = JS_ASSERT_STRING_IS_FLAT(JS::StringOfAddonId(addonId));
     120           0 :                 nsString addonIdString;
     121           0 :                 AssignJSFlatString(addonIdString, flat);
     122           0 :                 NS_ConvertUTF16toUTF8 addonIdCString(addonIdString);
     123           0 :                 Telemetry::Accumulate(Telemetry::ADDON_FORBIDDEN_CPOW_USAGE, addonIdCString);
     124             : 
     125           0 :                 if (ForbidCPOWsInCompatibleAddon(addonIdCString)) {
     126           0 :                     JS_ReportErrorASCII(cx, "CPOW usage forbidden in this add-on");
     127           0 :                     return false;
     128             :                 }
     129             : 
     130           0 :                 warn = true;
     131             :             }
     132             :         }
     133             :     }
     134             : 
     135           0 :     if (!warn)
     136           0 :         return true;
     137             : 
     138           0 :     static bool disableUnsafeCPOWWarnings = PR_GetEnv("DISABLE_UNSAFE_CPOW_WARNINGS");
     139           0 :     if (!disableUnsafeCPOWWarnings) {
     140           0 :         nsCOMPtr<nsIConsoleService> console(do_GetService(NS_CONSOLESERVICE_CONTRACTID));
     141           0 :         if (console && cx) {
     142           0 :             nsAutoString filename;
     143           0 :             uint32_t lineno = 0, column = 0;
     144           0 :             nsJSUtils::GetCallingLocation(cx, filename, &lineno, &column);
     145           0 :             nsCOMPtr<nsIScriptError> error(do_CreateInstance(NS_SCRIPTERROR_CONTRACTID));
     146           0 :             error->Init(NS_LITERAL_STRING("unsafe/forbidden CPOW usage"), filename,
     147           0 :                         EmptyString(), lineno, column,
     148           0 :                         nsIScriptError::warningFlag, "chrome javascript");
     149           0 :             console->LogMessage(error);
     150             :         } else {
     151           0 :             NS_WARNING("Unsafe synchronous IPC message");
     152             :         }
     153             :     }
     154             : 
     155           0 :     return true;
     156             : }
     157             : 
     158             : void
     159           1 : JavaScriptParent::trace(JSTracer* trc)
     160             : {
     161           1 :     objects_.trace(trc);
     162           1 :     unwaivedObjectIds_.trace(trc);
     163           1 :     waivedObjectIds_.trace(trc);
     164           1 : }
     165             : 
     166             : JSObject*
     167           0 : JavaScriptParent::scopeForTargetObjects()
     168             : {
     169             :     // CPOWs from the child need to point into the parent's unprivileged junk
     170             :     // scope so that a compromised child cannot compromise the parent. In
     171             :     // practice, this means that a child process can only (a) hold parent
     172             :     // objects alive and (b) invoke them if they are callable.
     173           0 :     return xpc::UnprivilegedJunkScope();
     174             : }
     175             : 
     176             : void
     177         574 : JavaScriptParent::afterProcessTask()
     178             : {
     179         574 :     if (savedNextCPOWNumber_ == nextCPOWNumber_)
     180         571 :         return;
     181             : 
     182           3 :     savedNextCPOWNumber_ = nextCPOWNumber_;
     183             : 
     184           3 :     MOZ_ASSERT(nextCPOWNumber_ > 0);
     185           3 :     if (active())
     186           3 :         Unused << SendDropTemporaryStrongReferences(nextCPOWNumber_ - 1);
     187             : }
     188             : 
     189             : PJavaScriptParent*
     190           1 : mozilla::jsipc::NewJavaScriptParent()
     191             : {
     192           1 :     JavaScriptParent* parent = new JavaScriptParent();
     193           1 :     if (!parent->init()) {
     194           0 :         delete parent;
     195           0 :         return nullptr;
     196             :     }
     197           1 :     return parent;
     198             : }
     199             : 
     200             : void
     201           0 : mozilla::jsipc::ReleaseJavaScriptParent(PJavaScriptParent* parent)
     202             : {
     203           0 :     static_cast<JavaScriptParent*>(parent)->decref();
     204           0 : }
     205             : 
     206             : void
     207        1227 : mozilla::jsipc::AfterProcessTask()
     208             : {
     209        2080 :     for (auto* cp : ContentParent::AllProcesses(ContentParent::eLive)) {
     210         853 :         if (PJavaScriptParent* p = LoneManagedOrNullAsserts(cp->ManagedPJavaScriptParent()))
     211         574 :             static_cast<JavaScriptParent*>(p)->afterProcessTask();
     212             :     }
     213        1227 : }

Generated by: LCOV version 1.13