Bug 1506291 - Add Linux sandboxing for the RDD (media decoder) process. r=gcp,mjf,flod
authorJed Davis <jld@mozilla.com>
Wed, 27 Feb 2019 20:14:54 +0000
changeset 519405 493b443954fe15f7b542ba14671f25e5f8531dff
parent 519404 bf58d8320f5a1de358b930d996615c73ff22cce9
child 519406 d4dd2195766e051b57d93e799fbdc1a029c99c2b
push id10862
push userffxbld-merge
push dateMon, 11 Mar 2019 13:01:11 +0000
treeherdermozilla-beta@a2e7f5c935da [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgcp, mjf, flod
bugs1506291, 1511560
milestone67.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1506291 - Add Linux sandboxing for the RDD (media decoder) process. r=gcp,mjf,flod The seccomp-bpf policy is currently just the "common" policy with no additions (but with the fixes in bug 1511560 to enable shared memory creation). The file broker policy allows shared memory creation and nothing else. The namespace setup is the same as for GMP (i.e., as restrictive as we currently can be). The sandbox can be turned off for troubleshooting by setting the environment variable MOZ_DISABLE_RDD_SANDBOX, similarly to the other process types. Tested against https://demo.bitmovin.com/public/firefox/av1/ with the necessary prefs set. Depends on D20895 Differential Revision: https://phabricator.services.mozilla.com/D14525
dom/media/ipc/PRDD.ipdl
dom/media/ipc/RDDChild.cpp
dom/media/ipc/RDDChild.h
dom/media/ipc/RDDParent.cpp
dom/media/ipc/RDDParent.h
dom/media/ipc/RDDProcessHost.cpp
security/sandbox/linux/Sandbox.cpp
security/sandbox/linux/Sandbox.h
security/sandbox/linux/SandboxFilter.cpp
security/sandbox/linux/SandboxFilter.h
security/sandbox/linux/broker/SandboxBroker.h
security/sandbox/linux/broker/SandboxBrokerPolicyFactory.cpp
security/sandbox/linux/broker/SandboxBrokerPolicyFactory.h
security/sandbox/linux/launch/SandboxLaunch.cpp
security/sandbox/linux/reporter/SandboxReporter.cpp
security/sandbox/linux/reporter/SandboxReporterCommon.h
security/sandbox/linux/reporter/SandboxReporterWrappers.cpp
toolkit/locales/en-US/toolkit/about/aboutSupport.ftl
--- a/dom/media/ipc/PRDD.ipdl
+++ b/dom/media/ipc/PRDD.ipdl
@@ -16,17 +16,17 @@ namespace mozilla {
 // (RemoteDataDecoder) process. There is one instance of this protocol,
 // with the RDDParent living on the main thread of the RDD process and
 // the RDDChild living on the main thread of the UI process.
 protocol PRDD
 {
 parent:
 
   // args TBD, sent by UI process to initiate core settings
-  async Init();
+  async Init(MaybeFileDesc sandboxBroker);
 
   async InitProfiler(Endpoint<PProfilerChild> endpoint);
 
   async NewContentRemoteDecoderManager(
             Endpoint<PRemoteDecoderManagerParent> endpoint);
 
   async RequestMemoryReport(uint32_t generation,
                             bool anonymize,
--- a/dom/media/ipc/RDDChild.cpp
+++ b/dom/media/ipc/RDDChild.cpp
@@ -3,37 +3,61 @@
 /* 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 "RDDChild.h"
 
 #include "mozilla/dom/MemoryReportRequest.h"
 #include "mozilla/ipc/CrashReporterHost.h"
 
+#if defined(XP_LINUX) && defined(MOZ_SANDBOX)
+#  include "mozilla/SandboxBroker.h"
+#  include "mozilla/SandboxBrokerPolicyFactory.h"
+#endif
+
 #ifdef MOZ_GECKO_PROFILER
 #  include "ProfilerParent.h"
 #endif
 #include "RDDProcessHost.h"
 
 namespace mozilla {
 
 using namespace layers;
 
 RDDChild::RDDChild(RDDProcessHost* aHost) : mHost(aHost), mRDDReady(false) {
   MOZ_COUNT_CTOR(RDDChild);
 }
 
 RDDChild::~RDDChild() { MOZ_COUNT_DTOR(RDDChild); }
 
-void RDDChild::Init() {
-  SendInit();
+bool RDDChild::Init() {
+  MaybeFileDesc brokerFd = void_t();
+
+#if defined(XP_LINUX) && defined(MOZ_SANDBOX)
+  auto policy = SandboxBrokerPolicyFactory::GetUtilityPolicy(OtherPid());
+  if (policy != nullptr) {
+    brokerFd = FileDescriptor();
+    mSandboxBroker =
+        SandboxBroker::Create(std::move(policy), OtherPid(), brokerFd);
+    // This is unlikely to fail and probably indicates OS resource
+    // exhaustion, but we can at least try to recover.
+    if (NS_WARN_IF(mSandboxBroker == nullptr)) {
+      return false;
+    }
+    MOZ_ASSERT(static_cast<const FileDescriptor&>(brokerFd).IsValid());
+  }
+#endif  // XP_LINUX && MOZ_SANDBOX
+
+  SendInit(brokerFd);
 
 #ifdef MOZ_GECKO_PROFILER
   Unused << SendInitProfiler(ProfilerParent::CreateForProcess(OtherPid()));
 #endif
+
+  return true;
 }
 
 bool RDDChild::EnsureRDDReady() {
   if (mRDDReady) {
     return true;
   }
 
   mRDDReady = true;
--- a/dom/media/ipc/RDDChild.h
+++ b/dom/media/ipc/RDDChild.h
@@ -7,33 +7,37 @@
 #define _include_dom_media_ipc_RDDChild_h_
 #include "mozilla/PRDDChild.h"
 
 #include "mozilla/RefPtr.h"
 #include "mozilla/UniquePtr.h"
 
 namespace mozilla {
 
+#if defined(XP_LINUX) && defined(MOZ_SANDBOX)
+class SandboxBroker;
+#endif
+
 namespace ipc {
 class CrashReporterHost;
 }  // namespace ipc
 namespace dom {
 class MemoryReportRequestHost;
 }  // namespace dom
 
 class RDDProcessHost;
 
 class RDDChild final : public PRDDChild {
   typedef mozilla::dom::MemoryReportRequestHost MemoryReportRequestHost;
 
  public:
   explicit RDDChild(RDDProcessHost* aHost);
   ~RDDChild();
 
-  void Init();
+  bool Init();
 
   bool EnsureRDDReady();
 
   // PRDDChild overrides.
   mozilla::ipc::IPCResult RecvInitComplete();
   mozilla::ipc::IPCResult RecvInitCrashReporter(
       Shmem&& shmem, const NativeThreadId& aThreadId);
 
@@ -48,14 +52,17 @@ class RDDChild final : public PRDDChild 
                                const MaybeFileDesc& aDMDFile);
 
   static void Destroy(UniquePtr<RDDChild>&& aChild);
 
  private:
   RDDProcessHost* mHost;
   UniquePtr<ipc::CrashReporterHost> mCrashReporter;
   UniquePtr<MemoryReportRequestHost> mMemoryReportRequest;
+#if defined(XP_LINUX) && defined(MOZ_SANDBOX)
+  UniquePtr<SandboxBroker> mSandboxBroker;
+#endif
   bool mRDDReady;
 };
 
 }  // namespace mozilla
 
 #endif  // _include_dom_media_ipc_RDDChild_h_
--- a/dom/media/ipc/RDDParent.cpp
+++ b/dom/media/ipc/RDDParent.cpp
@@ -14,16 +14,20 @@
 #include "mozilla/HangDetails.h"
 #include "mozilla/RemoteDecoderManagerChild.h"
 #include "mozilla/RemoteDecoderManagerParent.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/dom/MemoryReportRequest.h"
 #include "mozilla/ipc/CrashReporterClient.h"
 #include "mozilla/ipc/ProcessChild.h"
 
+#if defined(XP_LINUX) && defined(MOZ_SANDBOX)
+#  include "mozilla/Sandbox.h"
+#endif
+
 #ifdef MOZ_GECKO_PROFILER
 #  include "ChildProfilerController.h"
 #endif
 
 #if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
 #  include "mozilla/Sandbox.h"
 #  include "nsMacUtilsImpl.h"
 #  include <Carbon/Carbon.h>  // for CGSSetDenyWindowServerConnections
@@ -118,22 +122,29 @@ static void StartRDDMacSandbox() {
   bool rv = mozilla::StartMacSandbox(info, err);
   if (!rv) {
     NS_WARNING(err.c_str());
     MOZ_CRASH("mozilla::StartMacSandbox failed");
   }
 }
 #endif
 
-mozilla::ipc::IPCResult RDDParent::RecvInit() {
+mozilla::ipc::IPCResult RDDParent::RecvInit(const MaybeFileDesc& aBrokerFd) {
   Unused << SendInitComplete();
-
-#if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
+#if defined(MOZ_SANDBOX)
+#  if defined(XP_MACOSX)
   StartRDDMacSandbox();
-#endif
+#  elif defined(XP_LINUX)
+  int fd = -1;
+  if (aBrokerFd.type() == MaybeFileDesc::TFileDescriptor) {
+    fd = aBrokerFd.get_FileDescriptor().ClonePlatformHandle().release();
+  }
+  SetRemoteDataDecoderSandbox(fd);
+#  endif  // XP_MACOSX/XP_LINUX
+#endif    // MOZ_SANDBOX
 
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult RDDParent::RecvInitProfiler(
     Endpoint<PProfilerChild>&& aEndpoint) {
 #ifdef MOZ_GECKO_PROFILER
   mProfilerController = ChildProfilerController::Create(std::move(aEndpoint));
--- a/dom/media/ipc/RDDParent.h
+++ b/dom/media/ipc/RDDParent.h
@@ -19,17 +19,17 @@ class RDDParent final : public PRDDParen
   RDDParent();
   ~RDDParent();
 
   static RDDParent* GetSingleton();
 
   bool Init(base::ProcessId aParentPid, const char* aParentBuildID,
             MessageLoop* aIOLoop, IPC::Channel* aChannel);
 
-  mozilla::ipc::IPCResult RecvInit();
+  mozilla::ipc::IPCResult RecvInit(const MaybeFileDesc& aBrokerFd);
   mozilla::ipc::IPCResult RecvInitProfiler(
       Endpoint<PProfilerChild>&& aEndpoint);
 
   mozilla::ipc::IPCResult RecvNewContentRemoteDecoderManager(
       Endpoint<PRemoteDecoderManagerParent>&& aEndpoint);
   mozilla::ipc::IPCResult RecvRequestMemoryReport(
       const uint32_t& generation, const bool& anonymize,
       const bool& minimizeMemoryUsage, const MaybeFileDesc& DMDFile);
--- a/dom/media/ipc/RDDProcessHost.cpp
+++ b/dom/media/ipc/RDDProcessHost.cpp
@@ -123,17 +123,19 @@ void RDDProcessHost::InitAfterConnect(bo
 
   if (aSucceeded) {
     mProcessToken = ++sRDDProcessTokenCounter;
     mRDDChild = MakeUnique<RDDChild>(this);
     DebugOnly<bool> rv =
         mRDDChild->Open(GetChannel(), base::GetProcId(GetChildProcessHandle()));
     MOZ_ASSERT(rv);
 
-    mRDDChild->Init();
+    if (!mRDDChild->Init()) {
+      KillHard("ActorInitFailed");
+    }
   }
 
   if (mListener) {
     mListener->OnProcessLaunchComplete(this);
   }
 }
 
 void RDDProcessHost::Shutdown() {
--- a/security/sandbox/linux/Sandbox.cpp
+++ b/security/sandbox/linux/Sandbox.cpp
@@ -648,9 +648,29 @@ void SetMediaPluginSandbox(const char* a
   files->Add("/proc/self/auxv");  // Info also in process's address space.
 #  endif
 
   // Finally, start the sandbox.
   SetCurrentProcessSandbox(GetMediaSandboxPolicy(files));
 }
 #endif  // MOZ_GMP_SANDBOX
 
+void SetRemoteDataDecoderSandbox(int aBroker) {
+  if (PR_GetEnv("MOZ_DISABLE_RDD_SANDBOX") != nullptr) {
+    if (aBroker >= 0) {
+      close(aBroker);
+    }
+    return;
+  }
+
+  gSandboxReporterClient =
+      new SandboxReporterClient(SandboxReport::ProcType::RDD);
+
+  // FIXME(bug 1513773): merge this with the one for content?
+  static SandboxBrokerClient* sBroker;
+  if (aBroker >= 0) {
+    sBroker = new SandboxBrokerClient(aBroker);
+  }
+
+  SetCurrentProcessSandbox(GetDecoderSandboxPolicy(sBroker));
+}
+
 }  // namespace mozilla
--- a/security/sandbox/linux/Sandbox.h
+++ b/security/sandbox/linux/Sandbox.h
@@ -57,11 +57,13 @@ MOZ_EXPORT bool SetContentProcessSandbox
 
 #ifdef MOZ_GMP_SANDBOX
 // Call only if SandboxInfo::CanSandboxMedia() returns true.
 // (No-op if MOZ_DISABLE_GMP_SANDBOX is set.)
 // aFilePath is the path to the plugin file.
 MOZ_EXPORT void SetMediaPluginSandbox(const char* aFilePath);
 #endif
 
+MOZ_EXPORT void SetRemoteDataDecoderSandbox(int aBroker);
+
 }  // namespace mozilla
 
 #endif  // mozilla_Sandbox_h
--- a/security/sandbox/linux/SandboxFilter.cpp
+++ b/security/sandbox/linux/SandboxFilter.cpp
@@ -1395,9 +1395,28 @@ class GMPSandboxPolicy : public SandboxP
 
 UniquePtr<sandbox::bpf_dsl::Policy> GetMediaSandboxPolicy(
     const SandboxOpenedFiles* aFiles) {
   return UniquePtr<sandbox::bpf_dsl::Policy>(new GMPSandboxPolicy(aFiles));
 }
 
 #endif  // MOZ_GMP_SANDBOX
 
+// The policy for the data decoder process is similar to the one for
+// media plugins, but the codec code is all in-tree so it's better
+// behaved and doesn't need special exceptions (or the ability to load
+// a plugin file).  However, it does directly create shared memory
+// segments, so it may need file brokering.
+class RDDSandboxPolicy final : public SandboxPolicyCommon {
+ public:
+  explicit RDDSandboxPolicy(SandboxBrokerClient* aBroker)
+      : SandboxPolicyCommon(aBroker) {}
+
+  // Pass through EvaluateSyscall.
+};
+
+UniquePtr<sandbox::bpf_dsl::Policy> GetDecoderSandboxPolicy(
+    SandboxBrokerClient* aMaybeBroker) {
+  return UniquePtr<sandbox::bpf_dsl::Policy>(
+      new RDDSandboxPolicy(aMaybeBroker));
+}
+
 }  // namespace mozilla
--- a/security/sandbox/linux/SandboxFilter.h
+++ b/security/sandbox/linux/SandboxFilter.h
@@ -14,28 +14,31 @@
 
 namespace sandbox {
 namespace bpf_dsl {
 class Policy;
 }
 }  // namespace sandbox
 
 namespace mozilla {
+class SandboxBrokerClient;
 
 #ifdef MOZ_CONTENT_SANDBOX
-class SandboxBrokerClient;
 struct ContentProcessSandboxParams;
 
 UniquePtr<sandbox::bpf_dsl::Policy> GetContentSandboxPolicy(
     SandboxBrokerClient* aMaybeBroker, ContentProcessSandboxParams&& aParams);
 #endif
 
 #ifdef MOZ_GMP_SANDBOX
 class SandboxOpenedFiles;
 
 // The SandboxOpenedFiles object must live until the process exits.
 UniquePtr<sandbox::bpf_dsl::Policy> GetMediaSandboxPolicy(
     const SandboxOpenedFiles* aFiles);
 #endif
 
+UniquePtr<sandbox::bpf_dsl::Policy> GetDecoderSandboxPolicy(
+    SandboxBrokerClient* aMaybeBroker);
+
 }  // namespace mozilla
 
 #endif
--- a/security/sandbox/linux/broker/SandboxBroker.h
+++ b/security/sandbox/linux/broker/SandboxBroker.h
@@ -106,16 +106,18 @@ class SandboxBroker final : private Sand
       AddPath(aPerms, aPath,
               (aPerms & MAY_CREATE) ? AddAlways : AddIfExistsNow);
     }
     int Lookup(const nsACString& aPath) const;
     int Lookup(const char* aPath) const {
       return Lookup(nsDependentCString(aPath));
     }
 
+    bool IsEmpty() const { return mMap.Count() == 0; }
+
    private:
     // ValidatePath checks |path| and returns true if these conditions are met
     // * Greater than 0 length
     // * Is an absolute path
     // * No trailing slash
     // * No /../ path traversal
     bool ValidatePath(const char* path) const;
     void AddPrefixInternal(int aPerms, const nsACString& aPath);
--- a/security/sandbox/linux/broker/SandboxBrokerPolicyFactory.cpp
+++ b/security/sandbox/linux/broker/SandboxBrokerPolicyFactory.cpp
@@ -188,16 +188,23 @@ static void AddPathsFromFile(SandboxBrok
   } while (more);
 }
 
 static void AddLdconfigPaths(SandboxBroker::Policy* aPolicy) {
   nsAutoCString ldconfigPath(NS_LITERAL_CSTRING("/etc/ld.so.conf"));
   AddPathsFromFile(aPolicy, ldconfigPath);
 }
 
+static void AddSharedMemoryPaths(SandboxBroker::Policy* aPolicy, pid_t aPid) {
+  std::string shmPath("/dev/shm");
+  if (base::SharedMemory::AppendPosixShmPrefix(&shmPath, aPid)) {
+    aPolicy->AddPrefix(rdwrcr, shmPath.c_str());
+  }
+}
+
 SandboxBrokerPolicyFactory::SandboxBrokerPolicyFactory() {
   // Policy entries that are the same in every process go here, and
   // are cached over the lifetime of the factory.
 #if defined(MOZ_CONTENT_SANDBOX)
   SandboxBroker::Policy* policy = new SandboxBroker::Policy;
   // Write permssions
   //
   // Bug 1308851: NVIDIA proprietary driver when using WebGL
@@ -519,20 +526,17 @@ UniquePtr<SandboxBroker::Policy> Sandbox
   if (allowAlsa) {
     // Bug 1309098: ALSA support
     policy->AddDir(rdwr, "/dev/snd");
   }
 
   if (allowPulse) {
     policy->AddDir(rdwrcr, "/dev/shm");
   } else {
-    std::string shmPath("/dev/shm");
-    if (base::SharedMemory::AppendPosixShmPrefix(&shmPath, aPid)) {
-      policy->AddPrefix(rdwrcr, shmPath.c_str());
-    }
+    AddSharedMemoryPaths(policy.get(), aPid);
   }
 
 #  ifdef MOZ_WIDGET_GTK
   if (const auto userDir = g_get_user_runtime_dir()) {
     // Bug 1321134: DConf's single bit of shared memory
     // The leaf filename is "user" by default, but is configurable.
     nsPrintfCString shmPath("%s/dconf/", userDir);
     policy->AddPrefix(rdwrcr, shmPath.get());
@@ -576,10 +580,22 @@ void SandboxBrokerPolicyFactory::AddDyna
     for (const nsACString& path : pathList.Split(',')) {
       nsCString trimPath(path);
       trimPath.Trim(" ", true, true);
       policy->AddDynamic(perms, trimPath.get());
     }
   }
 }
 
+/* static */ UniquePtr<SandboxBroker::Policy>
+SandboxBrokerPolicyFactory::GetUtilityPolicy(int aPid) {
+  auto policy = MakeUnique<SandboxBroker::Policy>();
+
+  AddSharedMemoryPaths(policy.get(), aPid);
+
+  if (policy->IsEmpty()) {
+    policy = nullptr;
+  }
+  return policy;
+}
+
 #endif  // MOZ_CONTENT_SANDBOX
 }  // namespace mozilla
--- a/security/sandbox/linux/broker/SandboxBrokerPolicyFactory.h
+++ b/security/sandbox/linux/broker/SandboxBrokerPolicyFactory.h
@@ -15,16 +15,18 @@ class SandboxBrokerPolicyFactory {
  public:
   SandboxBrokerPolicyFactory();
 
 #ifdef MOZ_CONTENT_SANDBOX
   UniquePtr<SandboxBroker::Policy> GetContentPolicy(int aPid,
                                                     bool aFileProcess);
 #endif
 
+  static UniquePtr<SandboxBroker::Policy> GetUtilityPolicy(int aPid);
+
  private:
   UniquePtr<const SandboxBroker::Policy> mCommonContentPolicy;
   static void AddDynamicPathList(SandboxBroker::Policy* policy,
                                  const char* aPathListPref, int perms);
 };
 
 }  // namespace mozilla
 
--- a/security/sandbox/linux/launch/SandboxLaunch.cpp
+++ b/security/sandbox/linux/launch/SandboxLaunch.cpp
@@ -223,16 +223,18 @@ static int GetEffectiveSandboxLevel(Geck
     case GeckoProcessType_Content:
       // GetEffectiveContentSandboxLevel is main-thread-only due to prefs.
       MOZ_ASSERT(NS_IsMainThread());
       if (info.Test(SandboxInfo::kEnabledForContent)) {
         return GetEffectiveContentSandboxLevel();
       }
       return 0;
 #endif
+    case GeckoProcessType_RDD:
+      return PR_GetEnv("MOZ_DISABLE_RDD_SANDBOX") == nullptr ? 1 : 0;
     default:
       return 0;
   }
 }
 
 void SandboxLaunchPrepare(GeckoProcessType aType,
                           base::LaunchOptions* aOptions) {
   auto info = SandboxInfo::Get();
@@ -272,22 +274,23 @@ void SandboxLaunchPrepare(GeckoProcessTy
   // Anything below this requires unprivileged user namespaces.
   if (!info.Test(SandboxInfo::kHasUserNamespaces)) {
     return;
   }
 
   switch (aType) {
 #ifdef MOZ_GMP_SANDBOX
     case GeckoProcessType_GMPlugin:
+#endif
+    case GeckoProcessType_RDD:
       if (level >= 1) {
         canChroot = true;
         flags |= CLONE_NEWNET | CLONE_NEWIPC;
       }
       break;
-#endif
 #ifdef MOZ_CONTENT_SANDBOX
     case GeckoProcessType_Content:
       if (level >= 4) {
         canChroot = true;
         // Unshare network namespace if allowed by graphics; see
         // function definition above for details.  (The display
         // local-ness is cached because it won't change.)
         static const bool canCloneNet =
--- a/security/sandbox/linux/reporter/SandboxReporter.cpp
+++ b/security/sandbox/linux/reporter/SandboxReporter.cpp
@@ -121,21 +121,24 @@ static void SubmitToTelemetry(const Sand
   // * "content:x86_64/110"       (bug 1285768)
   // * "gmp:madvise:8"            (bug 1303813)
   // * "content:clock_gettime:4"  (bug 1334687)
 
   switch (aReport.mProcType) {
     case SandboxReport::ProcType::CONTENT:
       key.AppendLiteral("content");
       break;
+    case SandboxReport::ProcType::FILE:
+      key.AppendLiteral("file");
+      break;
     case SandboxReport::ProcType::MEDIA_PLUGIN:
       key.AppendLiteral("gmp");
       break;
-    case SandboxReport::ProcType::FILE:
-      key.AppendLiteral("file");
+    case SandboxReport::ProcType::RDD:
+      key.AppendLiteral("rdd");
       break;
     default:
       MOZ_ASSERT(false);
   }
   key.Append(':');
 
   switch (aReport.mSyscall) {
     // Syscalls that are filtered by arguments in one or more of the
--- a/security/sandbox/linux/reporter/SandboxReporterCommon.h
+++ b/security/sandbox/linux/reporter/SandboxReporterCommon.h
@@ -28,16 +28,17 @@ struct SandboxReport {
   // In the future this may include finer distinctions than
   // GeckoProcessType -- e.g., whether a content process can load
   // file:/// URLs, or if it's reserved for content with certain
   // user-granted permissions.
   enum class ProcType : uint8_t {
     CONTENT,
     FILE,
     MEDIA_PLUGIN,
+    RDD,
   };
 
   // The syscall number and arguments are usually `unsigned long`, but
   // that causes ambiguous overload errors with nsACString::AppendInt.
   using ULong = UnsignedStdintTypeForSize<sizeof(unsigned long)>::Type;
 
   // This time uses CLOCK_MONOTONIC_COARSE.  Displaying or reporting
   // it should usually be done relative to the current value of that
--- a/security/sandbox/linux/reporter/SandboxReporterWrappers.cpp
+++ b/security/sandbox/linux/reporter/SandboxReporterWrappers.cpp
@@ -71,16 +71,19 @@ NS_IMETHODIMP SandboxReportWrapper::GetP
       aProcType.AssignLiteral("content");
       return NS_OK;
     case SandboxReport::ProcType::FILE:
       aProcType.AssignLiteral("file");
       return NS_OK;
     case SandboxReport::ProcType::MEDIA_PLUGIN:
       aProcType.AssignLiteral("mediaPlugin");
       return NS_OK;
+    case SandboxReport::ProcType::RDD:
+      aProcType.AssignLiteral("dataDecoder");
+      return NS_OK;
     default:
       MOZ_ASSERT(false);
       return NS_ERROR_UNEXPECTED;
   }
 }
 
 /* readonly attribute uint32_t syscall; */
 NS_IMETHODIMP SandboxReportWrapper::GetSyscall(uint32_t* aSyscall) {
--- a/toolkit/locales/en-US/toolkit/about/aboutSupport.ftl
+++ b/toolkit/locales/en-US/toolkit/about/aboutSupport.ftl
@@ -255,16 +255,17 @@ has-user-namespaces = User Namespaces
 has-privileged-user-namespaces = User Namespaces for privileged processes
 can-sandbox-content = Content Process Sandboxing
 can-sandbox-media = Media Plugin Sandboxing
 content-sandbox-level = Content Process Sandbox Level
 effective-content-sandbox-level = Effective Content Process Sandbox Level
 sandbox-proc-type-content = content
 sandbox-proc-type-file = file content
 sandbox-proc-type-media-plugin = media plugin
+sandbox-proc-type-data-decoder = data decoder
 
 launcher-process-status-0 = Enabled
 launcher-process-status-1 = Disabled due to failure
 launcher-process-status-2 = Disabled forcibly
 launcher-process-status-unknown = Unknown status
 
 # Variables
 # $remoteWindows (integer) - Number of remote windows