gecko-dev/dom/media/gmp/GMPLoader.cpp
Gabriele Svelto aa43fa218e Bug 1831092 - Use the new pull-based API for all crash annotations and remove the global annotations table r=jgilbert,necko-reviewers,media-playback-reviewers,profiler-reviewers,win-reviewers,padenot,handyman,afranchuk,valentin,alwu,sotaro
This changes comes with several different refactorings all rolled into one,
unfotunately I couldn't find a way to pull them apart:
- First of all annotations now can either recorded (that is, we copy the value
  and have the crash reporting code own the copy) or registered. Several
  annotations are changed to use this functionality so that we don't need to
  update them as their value change.
- The code in the exception handler is modified to read the annotations from
  the mozannotation_client crate. This has the unfortunate side-effect that
  we need three different bits of code to serialize them: one for annotations
  read from a child process, one for reading annotations from the main process
  outside of the exception handler and one for reading annotations from the
  main process within the exception handler. As we move to fully
  out-of-process crash reporting the last two methods will go away.
- The mozannotation_client crate now doesn't record annotation types anymore.
  I realized as I was working on this that storing types at runtime has two
  issues: the first one is that buggy code might change the type of an
  annotation (that is record it under two different types at two different
  moments), the second issue is that types might become corrupt during a
  crash, so better enforce them at annotation-writing time. The end result is
  that the mozannotation_* crates now only store byte buffers, track the
  format the data is stored in (null-terminated string, fixed size buffer,
  etc...) but not the type of data each annotation is supposed to contain.
- Which brings us to the next change: concrete types for annotations are now
  enforced when they're written out. If an annotation doesn't match the
  expected type it's skipped. Storing an annotation with the wrong type will
  also trigger an assertion in debug builds.

Differential Revision: https://phabricator.services.mozilla.com/D195248
2024-03-04 10:24:43 +00:00

203 lines
5.8 KiB
C++

/* -*- 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::AutoRecordAnnotation 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