SandboxTest.cpp (13822B)
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 https://mozilla.org/MPL/2.0/. */ 6 7 #include "SandboxTest.h" 8 9 #include "mozilla/Components.h" 10 #include "mozilla/Preferences.h" 11 #include "SandboxTestingParent.h" 12 #include "SandboxTestingChild.h" 13 #include "mozilla/dom/ContentParent.h" 14 #include "mozilla/gfx/GPUProcessManager.h" 15 #include "mozilla/gfx/GPUChild.h" 16 #include "mozilla/net/SocketProcessParent.h" 17 #include "mozilla/RDDProcessManager.h" 18 #include "mozilla/RDDChild.h" 19 #include "mozilla/ipc/UtilityProcessManager.h" 20 #include "mozilla/ipc/UtilityProcessParent.h" 21 #include "mozilla/ipc/UtilityProcessSandboxing.h" 22 #include "GMPService.h" 23 #include "mozilla/gmp/GMPTypes.h" 24 #include "mozilla/ipc/Endpoint.h" 25 #include "nsIOService.h" 26 27 #ifdef XP_WIN 28 # include "nsAppDirectoryServiceDefs.h" 29 #endif 30 31 using namespace mozilla; 32 using namespace mozilla::ipc; 33 using namespace mozilla::dom; 34 35 namespace mozilla { 36 37 NS_IMPL_ISUPPORTS(SandboxTest, mozISandboxTest) 38 39 inline void UnsetEnvVariable(const nsCString& aEnvVarName) { 40 nsCString aEnvVarNameFull = aEnvVarName + "="_ns; 41 int rv_unset = 42 #ifdef XP_UNIX 43 unsetenv(aEnvVarName.get()); 44 #endif // XP_UNIX 45 #ifdef XP_WIN 46 _putenv(aEnvVarNameFull.get()); 47 #endif // XP_WIN 48 MOZ_ASSERT(rv_unset == 0, "Error unsetting env var"); 49 } 50 51 GeckoProcessType GeckoProcessStringToType(const nsCString& aString) { 52 for (GeckoProcessType type = GeckoProcessType(0); 53 type < GeckoProcessType::GeckoProcessType_End; 54 type = GeckoProcessType(type + 1)) { 55 if (aString == XRE_GeckoProcessTypeToString(type)) { 56 return type; 57 } 58 } 59 return GeckoProcessType::GeckoProcessType_Invalid; 60 } 61 62 // Set up tests on remote process connected to the given actor. 63 // The actor must handle the InitSandboxTesting message. 64 template <typename Actor> 65 void InitializeSandboxTestingActors( 66 Actor* aActor, 67 const RefPtr<SandboxTest::ProcessPromise::Private>& aProcessPromise) { 68 MOZ_ASSERT(aActor, "Should have provided an IPC actor"); 69 Endpoint<PSandboxTestingParent> sandboxTestingParentEnd; 70 Endpoint<PSandboxTestingChild> sandboxTestingChildEnd; 71 nsresult rv = PSandboxTesting::CreateEndpoints(&sandboxTestingParentEnd, 72 &sandboxTestingChildEnd); 73 if (NS_FAILED(rv)) { 74 aProcessPromise->Reject(NS_ERROR_FAILURE, __func__); 75 return; 76 } 77 78 // GMPlugin binds us to the GMP Thread, so we need IPC's Send to be done on 79 // the same thread 80 (void)aActor->SendInitSandboxTesting(std::move(sandboxTestingChildEnd)); 81 // But then the SandboxTestingParent::Create() call needs to be on the main 82 // thread 83 NS_DispatchToMainThread(NS_NewRunnableFunction( 84 "SandboxTestingParent::Create", 85 [stpE = std::move(sandboxTestingParentEnd), aProcessPromise]() mutable { 86 return aProcessPromise->Resolve( 87 SandboxTestingParent::Create(std::move(stpE)), __func__); 88 })); 89 } 90 91 NS_IMETHODIMP 92 SandboxTest::StartTests(const nsTArray<nsCString>& aProcessesList) { 93 MOZ_ASSERT(NS_IsMainThread()); 94 95 #if defined(XP_WIN) 96 nsCOMPtr<nsIFile> testFile; 97 NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(testFile)); 98 MOZ_ASSERT(testFile); 99 nsCOMPtr<nsIFile> testChromeFile; 100 testFile->Clone(getter_AddRefs(testChromeFile)); 101 testChromeFile->Append(u"chrome"_ns); 102 testChromeFile->Exists(&mChromeDirExisted); 103 testFile->Append(u"sandboxTest.txt"_ns); 104 testChromeFile->Append(u"sandboxTest.txt"_ns); 105 MOZ_ALWAYS_SUCCEEDS(testFile->Create(nsIFile::NORMAL_FILE_TYPE, 0666)); 106 MOZ_ALWAYS_SUCCEEDS(testChromeFile->Create(nsIFile::NORMAL_FILE_TYPE, 0666)); 107 #endif 108 109 for (const auto& processTypeName : aProcessesList) { 110 SandboxingKind sandboxingKind = SandboxingKind::COUNT; 111 GeckoProcessType type = GeckoProcessType::GeckoProcessType_Invalid; 112 if (processTypeName.Find(":") != kNotFound) { 113 int32_t pos = processTypeName.Find(":"); 114 nsCString processType = nsCString(Substring(processTypeName, 0, pos)); 115 nsCString sandboxKindStr = nsCString( 116 Substring(processTypeName, pos + 1, processTypeName.Length())); 117 118 nsresult err; 119 uint64_t sbVal = (uint64_t)(sandboxKindStr.ToDouble(&err)); 120 if (NS_FAILED(err)) { 121 NS_WARNING("Unable to get SandboxingKind"); 122 return NS_ERROR_ILLEGAL_VALUE; 123 } 124 125 if (sbVal >= SandboxingKind::COUNT) { 126 NS_WARNING("Invalid sandboxing kind"); 127 return NS_ERROR_ILLEGAL_VALUE; 128 } 129 130 if (!processType.Equals( 131 XRE_GeckoProcessTypeToString(GeckoProcessType_Utility))) { 132 NS_WARNING("Expected utility process type"); 133 return NS_ERROR_ILLEGAL_VALUE; 134 } 135 136 sandboxingKind = (SandboxingKind)sbVal; 137 type = GeckoProcessType_Utility; 138 } else { 139 type = GeckoProcessStringToType(processTypeName); 140 141 if (type == GeckoProcessType::GeckoProcessType_Invalid) { 142 NS_WARNING("Invalid process type"); 143 return NS_ERROR_ILLEGAL_VALUE; 144 } 145 } 146 147 RefPtr<ProcessPromise::Private> processPromise = 148 MakeRefPtr<ProcessPromise::Private>(__func__); 149 150 switch (type) { 151 case GeckoProcessType_Content: { 152 nsTArray<ContentParent*> parents; 153 ContentParent::GetAll(parents); 154 if (parents[0]) { 155 InitializeSandboxTestingActors(parents[0], processPromise); 156 } else { 157 processPromise->Reject(NS_ERROR_FAILURE, __func__); 158 MOZ_ASSERT_UNREACHABLE("SandboxTest; failure to get Content process"); 159 } 160 break; 161 } 162 163 case GeckoProcessType_GPU: { 164 gfx::GPUProcessManager* gpuProc = gfx::GPUProcessManager::Get(); 165 gfx::GPUChild* gpuChild = gpuProc ? gpuProc->GetGPUChild() : nullptr; 166 if (gpuChild) { 167 InitializeSandboxTestingActors(gpuChild, processPromise); 168 } else { 169 processPromise->Reject(NS_OK, __func__); 170 } 171 break; 172 } 173 174 case GeckoProcessType_RDD: { 175 RDDProcessManager* rddProc = RDDProcessManager::Get(); 176 rddProc->LaunchRDDProcess()->Then( 177 GetMainThreadSerialEventTarget(), __func__, 178 [processPromise, rddProc]() { 179 RDDChild* rddChild = rddProc ? rddProc->GetRDDChild() : nullptr; 180 if (rddChild) { 181 return InitializeSandboxTestingActors(rddChild, processPromise); 182 } 183 return processPromise->Reject(NS_ERROR_FAILURE, __func__); 184 }, 185 [processPromise](nsresult aError) { 186 MOZ_ASSERT_UNREACHABLE("SandboxTest; failure to get RDD process"); 187 return processPromise->Reject(aError, __func__); 188 }); 189 break; 190 } 191 192 case GeckoProcessType_GMPlugin: { 193 UnsetEnvVariable("MOZ_DISABLE_GMP_SANDBOX"_ns); 194 RefPtr<gmp::GeckoMediaPluginService> service = 195 gmp::GeckoMediaPluginService::GetGeckoMediaPluginService(); 196 MOZ_ASSERT(service, "We have a GeckoMediaPluginService"); 197 198 RefPtr<SandboxTest> self = this; 199 nsCOMPtr<nsISerialEventTarget> thread = service->GetGMPThread(); 200 nsresult rv = thread->Dispatch(NS_NewRunnableFunction( 201 "SandboxTest::GMPlugin", [self, processPromise, service, thread]() { 202 service->GetContentParentForTest()->Then( 203 thread, __func__, 204 [self, processPromise]( 205 const RefPtr<gmp::GMPContentParentCloseBlocker>& 206 wrapper) { 207 RefPtr<gmp::GMPContentParent> parent = wrapper->mParent; 208 MOZ_ASSERT(parent, 209 "Wrapper should wrap a valid parent if we're in " 210 "this path."); 211 if (!parent) { 212 return processPromise->Reject(NS_ERROR_ILLEGAL_VALUE, 213 __func__); 214 } 215 NS_DispatchToMainThread(NS_NewRunnableFunction( 216 "SandboxTesting::Wrapper", [self, wrapper]() { 217 self->mGMPContentParentWrapper = wrapper; 218 })); 219 return InitializeSandboxTestingActors(parent.get(), 220 processPromise); 221 }, 222 [processPromise](const MediaResult& rv) { 223 return processPromise->Reject(NS_ERROR_FAILURE, __func__); 224 }); 225 })); 226 NS_ENSURE_SUCCESS(rv, rv); 227 break; 228 } 229 230 case GeckoProcessType_Socket: { 231 // mochitest harness force this variable, but we actually do not want 232 // that 233 UnsetEnvVariable("MOZ_DISABLE_SOCKET_PROCESS"_ns); 234 235 nsresult rv_pref = 236 Preferences::SetBool("network.process.enabled", true); 237 MOZ_ASSERT(rv_pref == NS_OK, "Error enforcing pref"); 238 239 MOZ_ASSERT(net::gIOService, "No gIOService?"); 240 241 net::gIOService->CallOrWaitForSocketProcess([processPromise]() { 242 // If socket process was previously disabled by env, 243 // nsIOService code will take some time before it creates the new 244 // process and it triggers this callback 245 RefPtr<net::SocketProcessParent> parent = 246 net::SocketProcessParent::GetSingleton(); 247 if (parent) { 248 return InitializeSandboxTestingActors(parent.get(), processPromise); 249 } 250 return processPromise->Reject(NS_ERROR_FAILURE, __func__); 251 }); 252 break; 253 } 254 255 case GeckoProcessType_Utility: { 256 RefPtr<UtilityProcessManager> utilityProc = 257 UtilityProcessManager::GetSingleton(); 258 utilityProc->LaunchProcess(sandboxingKind) 259 ->Then( 260 GetMainThreadSerialEventTarget(), __func__, 261 [processPromise, utilityProc, sandboxingKind]() { 262 RefPtr<UtilityProcessParent> utilityParent = 263 utilityProc 264 ? utilityProc->GetProcessParent(sandboxingKind) 265 : nullptr; 266 if (utilityParent) { 267 return InitializeSandboxTestingActors(utilityParent.get(), 268 processPromise); 269 } 270 return processPromise->Reject(NS_ERROR_FAILURE, __func__); 271 }, 272 [processPromise](LaunchError const&) { 273 MOZ_ASSERT_UNREACHABLE( 274 "SandboxTest; failure to get Utility process"); 275 return processPromise->Reject(NS_ERROR_FAILURE, __func__); 276 }); 277 break; 278 } 279 280 default: 281 MOZ_ASSERT_UNREACHABLE( 282 "SandboxTest does not yet support this process type"); 283 return NS_ERROR_NOT_IMPLEMENTED; 284 } 285 286 RefPtr<SandboxTest> self = this; 287 RefPtr<ProcessPromise> aPromise(processPromise); 288 aPromise->Then( 289 GetMainThreadSerialEventTarget(), __func__, 290 [self](RefPtr<SandboxTestingParent> aValue) { 291 self->mSandboxTestingParents.AppendElement(std::move(aValue)); 292 return NS_OK; 293 }, 294 [](nsresult aError) { 295 if (aError == NS_OK) { 296 // There is no such process for this OS. Report test done. 297 nsCOMPtr<nsIObserverService> observerService = 298 mozilla::services::GetObserverService(); 299 MOZ_RELEASE_ASSERT(observerService); 300 observerService->NotifyObservers(nullptr, "sandbox-test-done", 301 nullptr); 302 return NS_OK; 303 } 304 MOZ_ASSERT_UNREACHABLE("SandboxTest; failure to get a process"); 305 return NS_ERROR_FAILURE; 306 }); 307 } 308 return NS_OK; 309 } 310 311 NS_IMETHODIMP 312 SandboxTest::FinishTests() { 313 if (mGMPContentParentWrapper) { 314 RefPtr<gmp::GeckoMediaPluginService> service = 315 gmp::GeckoMediaPluginService::GetGeckoMediaPluginService(); 316 MOZ_ASSERT(service, "We have a GeckoMediaPluginService"); 317 318 nsCOMPtr<nsISerialEventTarget> thread = service->GetGMPThread(); 319 nsresult rv = thread->Dispatch(NS_NewRunnableFunction( 320 "SandboxTest::FinishTests", 321 [wrapper = std::move(mGMPContentParentWrapper)]() { 322 // Release mGMPContentWrapper's reference. We hold this to keep an 323 // active reference on the CloseBlocker produced by GMPService, 324 // otherwise it would automatically shutdown the GMPlugin thread we 325 // started. 326 // If somehow it does not work as expected, then tests will fail 327 // because of leaks happening on GMPService and others. 328 })); 329 NS_ENSURE_SUCCESS(rv, rv); 330 } 331 332 for (RefPtr<SandboxTestingParent>& stp : mSandboxTestingParents) { 333 SandboxTestingParent::Destroy(stp.forget()); 334 } 335 336 // Make sure there is no leftover for test --verify to run without failure 337 mSandboxTestingParents.Clear(); 338 339 #if defined(XP_WIN) 340 nsCOMPtr<nsIFile> testFile; 341 NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(testFile)); 342 MOZ_ASSERT(testFile); 343 nsCOMPtr<nsIFile> testChromeFile; 344 testFile->Clone(getter_AddRefs(testChromeFile)); 345 testChromeFile->Append(u"chrome"_ns); 346 testFile->Append(u"sandboxTest.txt"_ns); 347 if (mChromeDirExisted) { 348 // Chrome dir existed, just delete test file. 349 testChromeFile->Append(u"sandboxTest.txt"_ns); 350 } 351 testFile->Remove(false); 352 testChromeFile->Remove(true); 353 #endif 354 355 return NS_OK; 356 } 357 358 } // namespace mozilla 359 360 NS_IMPL_COMPONENT_FACTORY(mozISandboxTest) { 361 return MakeAndAddRef<SandboxTest>().downcast<nsISupports>(); 362 }