UtilityProcessTest.cpp (8329B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 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 7 #if defined(ENABLE_TESTS) 8 # include "mozilla/ipc/UtilityProcessManager.h" 9 # include "mozilla/ipc/UtilityProcessTest.h" 10 # include "mozilla/dom/ChromeUtilsBinding.h" 11 # include "mozilla/dom/Promise.h" 12 # include "mozilla/ProcInfo.h" 13 # include "mozilla/IntentionalCrash.h" 14 15 # ifdef XP_WIN 16 # include <handleapi.h> 17 # include <processthreadsapi.h> 18 # include <tlhelp32.h> 19 20 # include "mozilla/WinHandleWatcher.h" 21 # include "nsISupports.h" 22 # include "nsWindowsHelpers.h" 23 # endif 24 25 namespace mozilla::ipc { 26 27 static UtilityActorName UtilityActorNameFromString( 28 const nsACString& aStringName) { 29 using namespace mozilla::dom; 30 auto idlName = StringToEnum<UtilityActorName>(aStringName); 31 if (idlName.isSome()) { 32 return idlName.value(); 33 } 34 MOZ_CRASH("Unknown utility actor name"); 35 } 36 37 // Find the utility process with the given actor or any utility process if 38 // aActorName is Nothing(). 39 static SandboxingKind FindUtilityProcessWithActor( 40 const Maybe<UtilityActorName>& aActorName) { 41 RefPtr<UtilityProcessManager> utilityProc = 42 UtilityProcessManager::GetSingleton(); 43 MOZ_ASSERT(utilityProc, "No UtilityprocessManager?"); 44 45 for (size_t i = 0; i < SandboxingKind::COUNT; ++i) { 46 auto sbKind = static_cast<SandboxingKind>(i); 47 if (!utilityProc->Process(sbKind)) { 48 continue; 49 } 50 if (aActorName.isNothing()) { 51 return sbKind; 52 } 53 for (auto actor : utilityProc->GetActors(sbKind)) { 54 if (actor == aActorName.ref()) { 55 return sbKind; 56 } 57 } 58 } 59 60 return SandboxingKind::COUNT; 61 } 62 63 # ifdef XP_WIN 64 namespace { 65 // Promise implementation for `UntilChildProcessDead`. 66 // 67 // Resolves the provided JS promise when the provided Windows HANDLE becomes 68 // signaled. 69 class WinHandlePromiseImpl final { 70 public: 71 NS_INLINE_DECL_REFCOUNTING(WinHandlePromiseImpl) 72 73 using HandlePtr = mozilla::UniqueFileHandle; 74 75 // Takes ownership of aHandle. 76 static void Create(mozilla::UniqueFileHandle handle, 77 RefPtr<mozilla::dom::Promise> promise) { 78 MOZ_ASSERT(handle); 79 MOZ_ASSERT(promise); 80 81 RefPtr obj{new WinHandlePromiseImpl(std::move(handle), std::move(promise))}; 82 83 // WARNING: This creates an owning-reference cycle: (self -> HandleWatcher 84 // -> Runnable -> self). `obj` will therefore only be destroyed when and 85 // if the HANDLE is signaled. 86 obj->watcher.Watch(obj->handle.get(), GetCurrentSerialEventTarget(), 87 NewRunnableMethod("WinHandlePromiseImpl::Resolve", obj, 88 &WinHandlePromiseImpl::Resolve)); 89 } 90 91 private: 92 WinHandlePromiseImpl(mozilla::UniqueFileHandle handle, 93 RefPtr<mozilla::dom::Promise> promise) 94 : handle(std::move(handle)), promise(std::move(promise)) {} 95 96 ~WinHandlePromiseImpl() { watcher.Stop(); } 97 98 void Resolve() { promise->MaybeResolveWithUndefined(); } 99 100 mozilla::UniqueFileHandle handle; 101 HandleWatcher watcher; 102 RefPtr<mozilla::dom::Promise> promise; 103 }; 104 105 } // namespace 106 # endif 107 108 NS_IMETHODIMP 109 UtilityProcessTest::StartProcess(const nsTArray<nsCString>& aActorsToRegister, 110 JSContext* aCx, 111 mozilla::dom::Promise** aOutPromise) { 112 NS_ENSURE_ARG(aOutPromise); 113 *aOutPromise = nullptr; 114 nsIGlobalObject* global = xpc::CurrentNativeGlobal(aCx); 115 if (NS_WARN_IF(!global)) { 116 return NS_ERROR_FAILURE; 117 } 118 119 ErrorResult erv; 120 RefPtr<dom::Promise> promise = dom::Promise::Create(global, erv); 121 if (NS_WARN_IF(erv.Failed())) { 122 return erv.StealNSResult(); 123 } 124 125 RefPtr<UtilityProcessManager> utilityProc = 126 UtilityProcessManager::GetSingleton(); 127 MOZ_ASSERT(utilityProc, "No UtilityprocessManager?"); 128 129 auto actors = aActorsToRegister.Clone(); 130 131 utilityProc->LaunchProcess(SandboxingKind::GENERIC_UTILITY) 132 ->Then( 133 GetCurrentSerialEventTarget(), __func__, 134 [promise, utilityProc, actors = std::move(actors)] { 135 RefPtr<UtilityProcessParent> utilityParent = 136 utilityProc->GetProcessParent(SandboxingKind::GENERIC_UTILITY); 137 Maybe<int32_t> utilityPid = 138 utilityProc->ProcessPid(SandboxingKind::GENERIC_UTILITY); 139 for (size_t i = 0; i < actors.Length(); ++i) { 140 auto uan = UtilityActorNameFromString(actors[i]); 141 utilityProc->RegisterActor(utilityParent, uan); 142 } 143 if (utilityPid.isSome()) { 144 promise->MaybeResolve(*utilityPid); 145 } else { 146 promise->MaybeReject(NS_ERROR_NOT_AVAILABLE); 147 } 148 }, 149 [promise](LaunchError aError) { 150 MOZ_ASSERT_UNREACHABLE( 151 "UtilityProcessTest; failure to get Utility process"); 152 promise->MaybeReject(NS_ERROR_FAILURE); 153 }); 154 155 promise.forget(aOutPromise); 156 return NS_OK; 157 } 158 159 NS_IMETHODIMP 160 UtilityProcessTest::NoteIntentionalCrash(uint32_t aPid) { 161 mozilla::NoteIntentionalCrash("utility", aPid); 162 return NS_OK; 163 } 164 165 NS_IMETHODIMP 166 UtilityProcessTest::UntilChildProcessDead( 167 uint32_t pid, JSContext* cx, ::mozilla::dom::Promise** aOutPromise) { 168 NS_ENSURE_ARG(aOutPromise); 169 *aOutPromise = nullptr; 170 171 # ifdef XP_WIN 172 if (pid == 0) { 173 return NS_ERROR_INVALID_ARG; 174 } 175 176 nsIGlobalObject* global = xpc::CurrentNativeGlobal(cx); 177 if (NS_WARN_IF(!global)) { 178 return NS_ERROR_FAILURE; 179 } 180 181 ErrorResult erv; 182 RefPtr<dom::Promise> promise = dom::Promise::Create(global, erv); 183 if (NS_WARN_IF(erv.Failed())) { 184 return erv.StealNSResult(); 185 } 186 187 // Get a fresh handle to the child process with the specified PID. 188 mozilla::UniqueFileHandle handle; 189 { 190 bool failed = false; 191 GeckoChildProcessHost::GetAll([&](GeckoChildProcessHost* aProc) { 192 if (handle || failed) { 193 return; 194 } 195 if (aProc->GetChildProcessId() != pid) { 196 return; 197 } 198 199 HANDLE handle_ = nullptr; 200 if (!::DuplicateHandle( 201 ::GetCurrentProcess(), aProc->GetChildProcessHandle(), 202 ::GetCurrentProcess(), &handle_, SYNCHRONIZE, FALSE, 0)) { 203 failed = true; 204 } else { 205 handle.reset(handle_); 206 } 207 }); 208 209 if (failed || !handle) { 210 return NS_ERROR_FAILURE; 211 } 212 } 213 214 // Create and attach the resolver for the promise, giving the handle over to 215 // it. 216 WinHandlePromiseImpl::Create(std::move(handle), promise); 217 218 promise.forget(aOutPromise); 219 220 return NS_OK; 221 # else // !defined(XP_WIN) 222 return NS_ERROR_NOT_IMPLEMENTED; 223 # endif 224 } 225 226 NS_IMETHODIMP 227 UtilityProcessTest::StopProcess(const char* aActorName) { 228 using namespace mozilla::dom; 229 230 SandboxingKind sbKind; 231 if (aActorName) { 232 const nsDependentCString actorStringName(aActorName); 233 UtilityActorName actorName = UtilityActorNameFromString(actorStringName); 234 sbKind = FindUtilityProcessWithActor(Some(actorName)); 235 } else { 236 sbKind = FindUtilityProcessWithActor(Nothing()); 237 } 238 239 if (sbKind == SandboxingKind::COUNT) { 240 MOZ_ASSERT_UNREACHABLE( 241 "Attempted to stop process for actor when no " 242 "such process exists"); 243 return NS_ERROR_FAILURE; 244 } 245 246 RefPtr<UtilityProcessManager> utilityProc = 247 UtilityProcessManager::GetSingleton(); 248 MOZ_ASSERT(utilityProc, "No UtilityprocessManager?"); 249 250 utilityProc->CleanShutdown(sbKind); 251 Maybe<int32_t> utilityPid = utilityProc->ProcessPid(sbKind); 252 MOZ_RELEASE_ASSERT(utilityPid.isNothing(), 253 "Should not have a utility process PID anymore"); 254 255 return NS_OK; 256 } 257 258 NS_IMETHODIMP 259 UtilityProcessTest::TestTelemetryProbes() { 260 RefPtr<UtilityProcessManager> utilityProc = 261 UtilityProcessManager::GetSingleton(); 262 MOZ_ASSERT(utilityProc, "No UtilityprocessManager?"); 263 264 for (RefPtr<UtilityProcessParent>& parent : 265 utilityProc->GetAllProcessesProcessParent()) { 266 (void)parent->SendTestTelemetryProbes(); 267 } 268 269 return NS_OK; 270 } 271 272 NS_IMPL_ISUPPORTS(UtilityProcessTest, nsIUtilityProcessTest) 273 274 } // namespace mozilla::ipc 275 #endif // defined(ENABLE_TESTS)