diff options
Diffstat (limited to 'dom/media/gmp/GMPLoader.cpp')
-rw-r--r-- | dom/media/gmp/GMPLoader.cpp | 203 |
1 files changed, 203 insertions, 0 deletions
diff --git a/dom/media/gmp/GMPLoader.cpp b/dom/media/gmp/GMPLoader.cpp new file mode 100644 index 0000000000..9fa7756165 --- /dev/null +++ b/dom/media/gmp/GMPLoader.cpp @@ -0,0 +1,203 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: sw=2 ts=4 et : + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "GMPLoader.h" +#include <stdio.h> +#include "mozilla/Attributes.h" +#include "nsExceptionHandler.h" +#include "gmp-entrypoints.h" +#include "prlink.h" +#include "prenv.h" +#include "prerror.h" +#if defined(XP_WIN) && defined(MOZ_SANDBOX) +# include "mozilla/sandboxTarget.h" +# include "mozilla/sandboxing/SandboxInitialization.h" +# include "mozilla/sandboxing/sandboxLogging.h" +#endif +#if defined(XP_LINUX) && defined(MOZ_SANDBOX) +# include "mozilla/Sandbox.h" +# include "mozilla/SandboxInfo.h" +#endif + +#include <string> + +#ifdef XP_WIN +# include <windows.h> +#endif + +namespace mozilla::gmp { +class PassThroughGMPAdapter : public GMPAdapter { + public: + ~PassThroughGMPAdapter() override { + // Ensure we're always shutdown, even if caller forgets to call + // GMPShutdown(). + GMPShutdown(); + } + + void SetAdaptee(PRLibrary* aLib) override { mLib = aLib; } + + GMPErr GMPInit(const GMPPlatformAPI* aPlatformAPI) override { + if (NS_WARN_IF(!mLib)) { + MOZ_CRASH("Missing library!"); + return GMPGenericErr; + } + GMPInitFunc initFunc = + reinterpret_cast<GMPInitFunc>(PR_FindFunctionSymbol(mLib, "GMPInit")); + if (!initFunc) { + MOZ_CRASH("Missing init method!"); + return GMPNotImplementedErr; + } + return initFunc(aPlatformAPI); + } + + GMPErr GMPGetAPI(const char* aAPIName, void* aHostAPI, void** aPluginAPI, + const nsACString& /* aKeySystem */) override { + if (!mLib) { + return GMPGenericErr; + } + GMPGetAPIFunc getapiFunc = reinterpret_cast<GMPGetAPIFunc>( + PR_FindFunctionSymbol(mLib, "GMPGetAPI")); + if (!getapiFunc) { + return GMPNotImplementedErr; + } + return getapiFunc(aAPIName, aHostAPI, aPluginAPI); + } + + void GMPShutdown() override { + if (mLib) { + GMPShutdownFunc shutdownFunc = reinterpret_cast<GMPShutdownFunc>( + PR_FindFunctionSymbol(mLib, "GMPShutdown")); + if (shutdownFunc) { + shutdownFunc(); + } + PR_UnloadLibrary(mLib); + mLib = nullptr; + } + } + + private: + PRLibrary* mLib = nullptr; +}; + +bool GMPLoader::Load(const char* aUTF8LibPath, uint32_t aUTF8LibPathLen, + const GMPPlatformAPI* aPlatformAPI, GMPAdapter* aAdapter) { + CrashReporter::AutoAnnotateCrashReport autoLibPath( + CrashReporter::Annotation::GMPLibraryPath, + nsDependentCString(aUTF8LibPath)); + + if (!getenv("MOZ_DISABLE_GMP_SANDBOX") && mSandboxStarter && + !mSandboxStarter->Start(aUTF8LibPath)) { + MOZ_CRASH("Cannot start sandbox!"); + return false; + } + + // Load the GMP. + PRLibSpec libSpec; +#ifdef XP_WIN + int pathLen = MultiByteToWideChar(CP_UTF8, 0, aUTF8LibPath, -1, nullptr, 0); + if (pathLen == 0) { + MOZ_CRASH("Cannot get path length as wide char!"); + return false; + } + + auto widePath = MakeUnique<wchar_t[]>(pathLen); + if (MultiByteToWideChar(CP_UTF8, 0, aUTF8LibPath, -1, widePath.get(), + pathLen) == 0) { + MOZ_CRASH("Cannot convert path to wide char!"); + return false; + } + + libSpec.value.pathname_u = widePath.get(); + libSpec.type = PR_LibSpec_PathnameU; +#else + libSpec.value.pathname = aUTF8LibPath; + libSpec.type = PR_LibSpec_Pathname; +#endif + PRLibrary* lib = PR_LoadLibraryWithFlags(libSpec, 0); + if (!lib) { + MOZ_CRASH_UNSAFE_PRINTF("Cannot load plugin as library %d %d", + PR_GetError(), PR_GetOSError()); + return false; + } + + mAdapter.reset((!aAdapter) ? new PassThroughGMPAdapter() : aAdapter); + mAdapter->SetAdaptee(lib); + + if (mAdapter->GMPInit(aPlatformAPI) != GMPNoErr) { + MOZ_CRASH("Cannot initialize plugin adapter!"); + return false; + } + + return true; +} + +GMPErr GMPLoader::GetAPI(const char* aAPIName, void* aHostAPI, + void** aPluginAPI, const nsACString& aKeySystem) { + return mAdapter->GMPGetAPI(aAPIName, aHostAPI, aPluginAPI, aKeySystem); +} + +void GMPLoader::Shutdown() { + if (mAdapter) { + mAdapter->GMPShutdown(); + } +} + +#if defined(XP_WIN) && defined(MOZ_SANDBOX) +class WinSandboxStarter : public mozilla::gmp::SandboxStarter { + public: + bool Start(const char* aLibPath) override { + // Cause advapi32 to load before the sandbox is turned on, as + // Widevine version 970 and later require it and the sandbox + // blocks it on Win7. + unsigned int dummy_rand; + rand_s(&dummy_rand); + + mozilla::SandboxTarget::Instance()->StartSandbox(); + return true; + } +}; +#endif + +#if defined(XP_LINUX) && defined(MOZ_SANDBOX) +namespace { +class LinuxSandboxStarter : public mozilla::gmp::SandboxStarter { + private: + LinuxSandboxStarter() = default; + friend mozilla::detail::UniqueSelector<LinuxSandboxStarter>::SingleObject + mozilla::MakeUnique<LinuxSandboxStarter>(); + + public: + static UniquePtr<SandboxStarter> Make() { + if (mozilla::SandboxInfo::Get().CanSandboxMedia()) { + return MakeUnique<LinuxSandboxStarter>(); + } + // Sandboxing isn't possible, but the parent has already + // checked that this plugin doesn't require it. (Bug 1074561) + return nullptr; + } + bool Start(const char* aLibPath) override { + mozilla::SetMediaPluginSandbox(aLibPath); + return true; + } +}; +} // anonymous namespace +#endif // XP_LINUX && MOZ_SANDBOX + +static UniquePtr<SandboxStarter> MakeSandboxStarter() { +#if defined(XP_WIN) && defined(MOZ_SANDBOX) + return mozilla::MakeUnique<WinSandboxStarter>(); +#elif defined(XP_LINUX) && defined(MOZ_SANDBOX) + return LinuxSandboxStarter::Make(); +#else + return nullptr; +#endif +} + +GMPLoader::GMPLoader() : mSandboxStarter(MakeSandboxStarter()) {} + +bool GMPLoader::CanSandbox() const { return !!mSandboxStarter; } + +} // namespace mozilla::gmp |