GMPProcessParent.cpp (12042B)
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * vim: sw=2 ts=2 et : 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 #include "GMPProcessParent.h" 8 9 #include "GMPUtils.h" 10 #include "nsIRunnable.h" 11 #ifdef XP_WIN 12 # include "WinUtils.h" 13 #endif 14 #include <string> 15 16 #include "GMPLog.h" 17 #include "base/process_util.h" 18 #include "base/string_util.h" 19 #include "mozilla/GeckoArgs.h" 20 #include "mozilla/StaticPrefs_media.h" 21 #include "mozilla/ipc/ProcessChild.h" 22 #include "mozilla/ipc/ProcessUtils.h" 23 #include "nsFmtString.h" 24 25 #if defined(XP_MACOSX) && defined(MOZ_SANDBOX) 26 # include "mozilla/Omnijar.h" 27 # include "mozilla/Preferences.h" 28 # include "mozilla/Sandbox.h" 29 # include "mozilla/SandboxSettings.h" 30 # include "nsMacUtilsImpl.h" 31 #endif 32 33 using std::string; 34 using std::vector; 35 36 using mozilla::gmp::GMPProcessParent; 37 using mozilla::ipc::GeckoChildProcessHost; 38 39 namespace mozilla::gmp { 40 41 #if defined(XP_MACOSX) && defined(MOZ_SANDBOX) 42 bool GMPProcessParent::sLaunchWithMacSandbox = true; 43 bool GMPProcessParent::sMacSandboxGMPLogging = false; 44 # if defined(DEBUG) 45 bool GMPProcessParent::sIsMainThreadInitDone = false; 46 # endif 47 #endif 48 49 #if defined(XP_MACOSX) && defined(MOZ_SANDBOX) 50 /* static */ 51 void GMPProcessParent::InitStaticMainThread() { 52 // The GMPProcessParent constructor is called off the 53 // main thread. Do main thread initialization here. 54 MOZ_ASSERT(NS_IsMainThread()); 55 sMacSandboxGMPLogging = 56 Preferences::GetBool("security.sandbox.logging.enabled") || 57 PR_GetEnv("MOZ_SANDBOX_GMP_LOGGING") || PR_GetEnv("MOZ_SANDBOX_LOGGING"); 58 GMP_LOG_DEBUG("GMPProcessParent::InitStaticMainThread: sandbox logging=%s", 59 sMacSandboxGMPLogging ? "true" : "false"); 60 # if defined(DEBUG) 61 sIsMainThreadInitDone = true; 62 # endif 63 } 64 #endif 65 66 GMPProcessParent::GMPProcessParent(const std::string& aGMPPath) 67 : GeckoChildProcessHost(GeckoProcessType_GMPlugin), 68 mGMPPath(aGMPPath), 69 mUseXpcom(StaticPrefs::media_gmp_use_minimal_xpcom()) 70 #if defined(XP_MACOSX) && defined(MOZ_SANDBOX) 71 , 72 mRequiresWindowServer(false) 73 #endif 74 { 75 MOZ_COUNT_CTOR(GMPProcessParent); 76 #if defined(XP_MACOSX) && defined(MOZ_SANDBOX) 77 MOZ_ASSERT(sIsMainThreadInitDone == true); 78 mDisableOSActivityMode = sLaunchWithMacSandbox; 79 #endif 80 } 81 82 GMPProcessParent::~GMPProcessParent() { MOZ_COUNT_DTOR(GMPProcessParent); } 83 84 bool GMPProcessParent::Launch(int32_t aTimeoutMs) { 85 class PrefSerializerRunnable final : public Runnable { 86 public: 87 PrefSerializerRunnable() 88 : Runnable("GMPProcessParent::PrefSerializerRunnable"), 89 mMonitor("GMPProcessParent::PrefSerializerRunnable::mMonitor") {} 90 91 NS_IMETHOD Run() override { 92 auto prefSerializer = MakeUnique<ipc::SharedPreferenceSerializer>(); 93 bool success = 94 prefSerializer->SerializeToSharedMemory(GeckoProcessType_GMPlugin, 95 /* remoteType */ ""_ns); 96 97 MonitorAutoLock lock(mMonitor); 98 MOZ_ASSERT(!mComplete); 99 if (success) { 100 mPrefSerializer = std::move(prefSerializer); 101 } 102 mComplete = true; 103 lock.Notify(); 104 return NS_OK; 105 } 106 107 void Wait(int32_t aTimeoutMs, 108 UniquePtr<ipc::SharedPreferenceSerializer>& aOut) { 109 MonitorAutoLock lock(mMonitor); 110 111 TimeDuration timeout = TimeDuration::FromMilliseconds(aTimeoutMs); 112 while (!mComplete) { 113 if (lock.Wait(timeout) == CVStatus::Timeout) { 114 return; 115 } 116 } 117 118 aOut = std::move(mPrefSerializer); 119 } 120 121 private: 122 Monitor mMonitor; 123 UniquePtr<ipc::SharedPreferenceSerializer> mPrefSerializer 124 MOZ_GUARDED_BY(mMonitor); 125 bool mComplete MOZ_GUARDED_BY(mMonitor) = false; 126 }; 127 128 nsresult rv; 129 geckoargs::ChildProcessArgs args; 130 UniquePtr<ipc::SharedPreferenceSerializer> prefSerializer; 131 132 ipc::ProcessChild::AddPlatformBuildID(args); 133 134 if (mUseXpcom) { 135 // Dispatch our runnable to the main thread to grab the serialized prefs. We 136 // can only do this on the main thread, and unfortunately we are the only 137 // process that launches from the non-main thread. 138 auto prefTask = MakeRefPtr<PrefSerializerRunnable>(); 139 rv = NS_DispatchToMainThread(prefTask); 140 if (NS_WARN_IF(NS_FAILED(rv))) { 141 return false; 142 } 143 144 // We don't want to release our thread context while we wait for the main 145 // thread to process the prefs. We already block when waiting for the launch 146 // of the process itself to finish, and the state machine assumes this call 147 // is blocking. This is also important for the buffering of pref updates, 148 // since we know any tasks dispatched with updates won't run until we launch 149 // (or fail to launch) the process. 150 prefTask->Wait(aTimeoutMs, prefSerializer); 151 if (NS_WARN_IF(!prefSerializer)) { 152 return false; 153 } 154 155 prefSerializer->AddSharedPrefCmdLineArgs(*this, args); 156 } 157 158 geckoargs::sPluginNativeEvent.Put( 159 StaticPrefs::media_gmp_use_native_event_processing(), args); 160 161 #ifdef ALLOW_GECKO_CHILD_PROCESS_ARCH 162 GMP_LOG_DEBUG("GMPProcessParent::Launch() mLaunchArch: %d", mLaunchArch); 163 # if defined(XP_MACOSX) 164 mLaunchOptions->arch = mLaunchArch; 165 if (mLaunchArch == base::PROCESS_ARCH_X86_64) { 166 mLaunchOptions->env_map["MOZ_SHMEM_PAGESIZE_16K"] = 1; 167 } 168 # endif 169 #endif 170 171 // Resolve symlinks in the plugin path. The sandbox prevents 172 // resolving symlinks in the child process if access to link 173 // source file is denied. 174 #ifdef XP_WIN 175 nsAutoString normalizedPath; 176 #else 177 nsAutoCString normalizedPath; 178 #endif 179 rv = NormalizePath(mGMPPath.c_str(), normalizedPath); 180 if (NS_WARN_IF(NS_FAILED(rv))) { 181 GMP_LOG_DEBUG( 182 "GMPProcessParent::Launch: " 183 "plugin path normaliziation failed for path: %s", 184 mGMPPath.c_str()); 185 } 186 187 #ifdef XP_WIN 188 std::wstring wGMPPath; 189 if (NS_SUCCEEDED(rv)) { 190 wGMPPath = normalizedPath.get(); 191 } else { 192 wGMPPath = UTF8ToWide(mGMPPath.c_str()); 193 } 194 195 // The sandbox doesn't allow file system rules where the paths contain 196 // symbolic links or junction points. Sometimes the Users folder has been 197 // moved to another drive using a junction point, so allow for this specific 198 // case. See bug 1236680 for details. 199 if (NS_WARN_IF( 200 !widget::WinUtils::ResolveJunctionPointsAndSymLinks(wGMPPath))) { 201 GMP_LOG_DEBUG("ResolveJunctionPointsAndSymLinks failed for GMP path=%S", 202 wGMPPath.c_str()); 203 return false; 204 } 205 GMP_LOG_DEBUG("GMPProcessParent::Launch() resolved path to %S", 206 wGMPPath.c_str()); 207 208 # ifdef MOZ_SANDBOX 209 // If the GMP path is a network path that is not mapped to a drive letter, 210 // then we need to fix the path format for the sandbox rule. 211 wchar_t volPath[MAX_PATH]; 212 if (::GetVolumePathNameW(wGMPPath.c_str(), volPath, MAX_PATH) && 213 ::GetDriveTypeW(volPath) == DRIVE_REMOTE && 214 wGMPPath.compare(0, 2, L"\\\\") == 0) { 215 std::wstring sandboxGMPPath(wGMPPath); 216 sandboxGMPPath.insert(1, L"??\\UNC"); 217 mAllowedFilesRead.push_back(sandboxGMPPath + L"\\*"); 218 } else { 219 mAllowedFilesRead.push_back(wGMPPath + L"\\*"); 220 } 221 # endif 222 223 std::string gmpPath = WideToUTF8(wGMPPath); 224 geckoargs::sPluginPath.Put(gmpPath.c_str(), args); 225 #else 226 if (NS_SUCCEEDED(rv)) { 227 geckoargs::sPluginPath.Put(normalizedPath.get(), args); 228 } else { 229 geckoargs::sPluginPath.Put(mGMPPath.c_str(), args); 230 } 231 #endif 232 233 // We need to wait until OnChannelConnected to clear the pref serializer, but 234 // SyncLaunch will block until that is called, so we don't actually need to do 235 // any overriding, and it only lives on the stack. 236 bool launched = SyncLaunch(std::move(args), aTimeoutMs); 237 if (launched) { 238 nsFmtString name{FMT_STRING(u"GMPProcessParent {}"), 239 static_cast<void*>(this)}; 240 mShutdownBlocker = media::ShutdownBlockingTicket::Create( 241 name, NS_LITERAL_STRING_FROM_CSTRING(__FILE__), __LINE__); 242 } 243 return launched; 244 } 245 246 void GMPProcessParent::Delete(nsCOMPtr<nsIRunnable> aCallback) { 247 mDeletedCallback = aCallback; 248 XRE_GetAsyncIOEventTarget()->Dispatch(NewNonOwningRunnableMethod( 249 "gmp::GMPProcessParent::DoDelete", this, &GMPProcessParent::DoDelete)); 250 } 251 252 void GMPProcessParent::DoDelete() { 253 MOZ_ASSERT(XRE_GetAsyncIOEventTarget()->IsOnCurrentThread()); 254 255 if (mDeletedCallback) { 256 mDeletedCallback->Run(); 257 } 258 259 Destroy(); 260 } 261 262 #if defined(XP_MACOSX) && defined(MOZ_SANDBOX) 263 bool GMPProcessParent::IsMacSandboxLaunchEnabled() { 264 return sLaunchWithMacSandbox; 265 } 266 267 void GMPProcessParent::SetRequiresWindowServer(bool aRequiresWindowServer) { 268 mRequiresWindowServer = aRequiresWindowServer; 269 } 270 271 bool GMPProcessParent::FillMacSandboxInfo(MacSandboxInfo& aInfo) { 272 aInfo.type = MacSandboxType_GMP; 273 aInfo.hasWindowServer = mRequiresWindowServer; 274 aInfo.shouldLog = (aInfo.shouldLog || sMacSandboxGMPLogging); 275 nsAutoCString appPath; 276 if (!nsMacUtilsImpl::GetAppPath(appPath)) { 277 GMP_LOG_DEBUG( 278 "GMPProcessParent::FillMacSandboxInfo: failed to get app path"); 279 return false; 280 } 281 aInfo.appPath.assign(appPath.get()); 282 283 GMP_LOG_DEBUG( 284 "GMPProcessParent::FillMacSandboxInfo: " 285 "plugin dir path: %s", 286 mGMPPath.c_str()); 287 nsCOMPtr<nsIFile> pluginDir; 288 nsresult rv = NS_NewLocalFile(NS_ConvertUTF8toUTF16(mGMPPath.c_str()), 289 getter_AddRefs(pluginDir)); 290 if (NS_FAILED(rv)) { 291 GMP_LOG_DEBUG( 292 "GMPProcessParent::FillMacSandboxInfo: " 293 "NS_NewLocalFile failed for plugin dir, rv=%d", 294 uint32_t(rv)); 295 return false; 296 } 297 298 rv = pluginDir->Normalize(); 299 if (NS_FAILED(rv)) { 300 GMP_LOG_DEBUG( 301 "GMPProcessParent::FillMacSandboxInfo: " 302 "failed to normalize plugin dir path, rv=%d", 303 uint32_t(rv)); 304 return false; 305 } 306 307 nsAutoCString resolvedPluginPath; 308 pluginDir->GetNativePath(resolvedPluginPath); 309 aInfo.pluginPath.assign(resolvedPluginPath.get()); 310 GMP_LOG_DEBUG( 311 "GMPProcessParent::FillMacSandboxInfo: " 312 "resolved plugin dir path: %s", 313 resolvedPluginPath.get()); 314 315 if (!mozilla::IsPackagedBuild()) { 316 GMP_LOG_DEBUG( 317 "GMPProcessParent::FillMacSandboxInfo: IsPackagedBuild()=false"); 318 319 // Repo dir 320 nsCOMPtr<nsIFile> repoDir; 321 rv = nsMacUtilsImpl::GetRepoDir(getter_AddRefs(repoDir)); 322 if (NS_FAILED(rv)) { 323 GMP_LOG_DEBUG( 324 "GMPProcessParent::FillMacSandboxInfo: failed to get repo dir"); 325 return false; 326 } 327 nsCString repoDirPath; 328 (void)repoDir->GetNativePath(repoDirPath); 329 aInfo.testingReadPath1 = repoDirPath.get(); 330 GMP_LOG_DEBUG( 331 "GMPProcessParent::FillMacSandboxInfo: " 332 "repo dir path: %s", 333 repoDirPath.get()); 334 335 // Object dir 336 nsCOMPtr<nsIFile> objDir; 337 rv = nsMacUtilsImpl::GetObjDir(getter_AddRefs(objDir)); 338 if (NS_FAILED(rv)) { 339 GMP_LOG_DEBUG( 340 "GMPProcessParent::FillMacSandboxInfo: failed to get object dir"); 341 return false; 342 } 343 nsCString objDirPath; 344 (void)objDir->GetNativePath(objDirPath); 345 aInfo.testingReadPath2 = objDirPath.get(); 346 GMP_LOG_DEBUG( 347 "GMPProcessParent::FillMacSandboxInfo: " 348 "object dir path: %s", 349 objDirPath.get()); 350 } 351 return true; 352 } 353 #endif 354 355 nsresult GMPProcessParent::NormalizePath(const char* aPath, 356 PathString& aNormalizedPath) { 357 nsCOMPtr<nsIFile> fileOrDir; 358 nsresult rv = 359 NS_NewLocalFile(NS_ConvertUTF8toUTF16(aPath), getter_AddRefs(fileOrDir)); 360 NS_ENSURE_SUCCESS(rv, rv); 361 362 rv = fileOrDir->Normalize(); 363 NS_ENSURE_SUCCESS(rv, rv); 364 365 #ifdef XP_WIN 366 return fileOrDir->GetTarget(aNormalizedPath); 367 #else 368 bool isLink = false; 369 rv = fileOrDir->IsSymlink(&isLink); 370 NS_ENSURE_SUCCESS(rv, rv); 371 if (isLink) { 372 return fileOrDir->GetNativeTarget(aNormalizedPath); 373 } 374 return fileOrDir->GetNativePath(aNormalizedPath); 375 #endif 376 } 377 378 } // namespace mozilla::gmp