/src/mozilla-central/js/ipc/JavaScriptParent.cpp
Line | Count | Source (jump to first uncovered line) |
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 "js/Proxy.h" |
15 | | #include "js/HeapAPI.h" |
16 | | #include "js/Wrapper.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 | | TraceParent(JSTracer* trc, void* data) |
30 | 0 | { |
31 | 0 | static_cast<JavaScriptParent*>(data)->trace(trc); |
32 | 0 | } |
33 | | |
34 | | JavaScriptParent::JavaScriptParent() |
35 | | : savedNextCPOWNumber_(1) |
36 | 0 | { |
37 | 0 | JS_AddExtraGCRootsTracer(danger::GetJSContext(), TraceParent, this); |
38 | 0 | } Unexecuted instantiation: mozilla::jsipc::JavaScriptParent::JavaScriptParent() Unexecuted instantiation: mozilla::jsipc::JavaScriptParent::JavaScriptParent() |
39 | | |
40 | | JavaScriptParent::~JavaScriptParent() |
41 | 0 | { |
42 | 0 | JS_RemoveExtraGCRootsTracer(danger::GetJSContext(), TraceParent, this); |
43 | 0 | } |
44 | | |
45 | | static bool |
46 | | ForbidUnsafeBrowserCPOWs() |
47 | 0 | { |
48 | 0 | static bool result; |
49 | 0 | static bool cached = false; |
50 | 0 | if (!cached) { |
51 | 0 | cached = true; |
52 | 0 | Preferences::AddBoolVarCache(&result, "dom.ipc.cpows.forbid-unsafe-from-browser", false); |
53 | 0 | } |
54 | 0 | return result; |
55 | 0 | } |
56 | | |
57 | | bool |
58 | | JavaScriptParent::allowMessage(JSContext* cx) |
59 | 0 | { |
60 | 0 | MOZ_ASSERT(cx); |
61 | 0 |
|
62 | 0 | // If we're running browser code while running tests (in automation), |
63 | 0 | // then we allow all safe CPOWs and forbid unsafe CPOWs |
64 | 0 | // based on a pref (which defaults to forbidden). |
65 | 0 | // We also allow CPOWs unconditionally in selected globals (based on |
66 | 0 | // Cu.permitCPOWsInScope). |
67 | 0 | // A normal (release) browser build will never allow CPOWs, |
68 | 0 | // excecpt as a token to pass round. |
69 | 0 |
|
70 | 0 | if (!xpc::IsInAutomation()) { |
71 | 0 | JS_ReportErrorASCII(cx, "CPOW usage forbidden"); |
72 | 0 | return false; |
73 | 0 | } |
74 | 0 | |
75 | 0 | MessageChannel* channel = GetIPCChannel(); |
76 | 0 | bool isSafe = channel->IsInTransaction(); |
77 | 0 |
|
78 | 0 | if (isSafe) { |
79 | 0 | return true; |
80 | 0 | } |
81 | 0 | |
82 | 0 | nsIGlobalObject* global = dom::GetIncumbentGlobal(); |
83 | 0 | JS::Rooted<JSObject*> jsGlobal(cx, global ? global->GetGlobalJSObject() : nullptr); |
84 | 0 | if (jsGlobal) { |
85 | 0 | JSAutoRealm ar(cx, jsGlobal); |
86 | 0 |
|
87 | 0 | if (!xpc::CompartmentPrivate::Get(jsGlobal)->allowCPOWs && |
88 | 0 | ForbidUnsafeBrowserCPOWs()) |
89 | 0 | { |
90 | 0 | Telemetry::Accumulate(Telemetry::BROWSER_SHIM_USAGE_BLOCKED, 1); |
91 | 0 | JS_ReportErrorASCII(cx, "unsafe CPOW usage forbidden"); |
92 | 0 | return false; |
93 | 0 | } |
94 | 0 | } |
95 | 0 | |
96 | 0 | static bool disableUnsafeCPOWWarnings = PR_GetEnv("DISABLE_UNSAFE_CPOW_WARNINGS"); |
97 | 0 | if (!disableUnsafeCPOWWarnings) { |
98 | 0 | nsCOMPtr<nsIConsoleService> console(do_GetService(NS_CONSOLESERVICE_CONTRACTID)); |
99 | 0 | if (console) { |
100 | 0 | nsAutoString filename; |
101 | 0 | uint32_t lineno = 0, column = 0; |
102 | 0 | nsJSUtils::GetCallingLocation(cx, filename, &lineno, &column); |
103 | 0 | nsCOMPtr<nsIScriptError> error(do_CreateInstance(NS_SCRIPTERROR_CONTRACTID)); |
104 | 0 | error->Init(NS_LITERAL_STRING("unsafe/forbidden CPOW usage"), filename, |
105 | 0 | EmptyString(), lineno, column, |
106 | 0 | nsIScriptError::warningFlag, "chrome javascript", |
107 | 0 | false /* from private window */); |
108 | 0 | console->LogMessage(error); |
109 | 0 | } else { |
110 | 0 | NS_WARNING("Unsafe synchronous IPC message"); |
111 | 0 | } |
112 | 0 | } |
113 | 0 |
|
114 | 0 | return true; |
115 | 0 | } |
116 | | |
117 | | void |
118 | | JavaScriptParent::trace(JSTracer* trc) |
119 | 0 | { |
120 | 0 | objects_.trace(trc); |
121 | 0 | unwaivedObjectIds_.trace(trc); |
122 | 0 | waivedObjectIds_.trace(trc); |
123 | 0 | } |
124 | | |
125 | | JSObject* |
126 | | JavaScriptParent::scopeForTargetObjects() |
127 | 0 | { |
128 | 0 | // CPOWs from the child need to point into the parent's unprivileged junk |
129 | 0 | // scope so that a compromised child cannot compromise the parent. In |
130 | 0 | // practice, this means that a child process can only (a) hold parent |
131 | 0 | // objects alive and (b) invoke them if they are callable. |
132 | 0 | return xpc::UnprivilegedJunkScope(); |
133 | 0 | } |
134 | | |
135 | | void |
136 | | JavaScriptParent::afterProcessTask() |
137 | 0 | { |
138 | 0 | if (savedNextCPOWNumber_ == nextCPOWNumber_) { |
139 | 0 | return; |
140 | 0 | } |
141 | 0 | |
142 | 0 | savedNextCPOWNumber_ = nextCPOWNumber_; |
143 | 0 |
|
144 | 0 | MOZ_ASSERT(nextCPOWNumber_ > 0); |
145 | 0 | if (active()) { |
146 | 0 | Unused << SendDropTemporaryStrongReferences(nextCPOWNumber_ - 1); |
147 | 0 | } |
148 | 0 | } |
149 | | |
150 | | PJavaScriptParent* |
151 | | mozilla::jsipc::NewJavaScriptParent() |
152 | 0 | { |
153 | 0 | return new JavaScriptParent(); |
154 | 0 | } |
155 | | |
156 | | void |
157 | | mozilla::jsipc::ReleaseJavaScriptParent(PJavaScriptParent* parent) |
158 | 0 | { |
159 | 0 | static_cast<JavaScriptParent*>(parent)->decref(); |
160 | 0 | } |
161 | | |
162 | | void |
163 | | mozilla::jsipc::AfterProcessTask() |
164 | 0 | { |
165 | 0 | for (auto* cp : ContentParent::AllProcesses(ContentParent::eLive)) { |
166 | 0 | if (PJavaScriptParent* p = LoneManagedOrNullAsserts(cp->ManagedPJavaScriptParent())) { |
167 | 0 | static_cast<JavaScriptParent*>(p)->afterProcessTask(); |
168 | 0 | } |
169 | 0 | } |
170 | 0 | } |