JavaScriptParent.cpp 6.5 KB

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