Bug 1740624 - Add RLBox sandbox pool support r=bholley
authorDeian Stefan <deian@cs.ucsd.edu>
Fri, 12 Nov 2021 05:20:29 +0000
changeset 599012 8fe04ef415915226cf80a1c904477154183c247a
parent 599011 b16763f1da6bcf31f964744c765c4af480ae551c
child 599013 b7e727dbc32044bbbb3dadfee2780dcea7419fa2
push id38973
push usersmolnar@mozilla.com
push dateFri, 12 Nov 2021 21:34:06 +0000
treeherdermozilla-central@d2b5e7cc2dbb [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbholley
bugs1740624
milestone96.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 1740624 - Add RLBox sandbox pool support r=bholley Differential Revision: https://phabricator.services.mozilla.com/D130934
security/rlbox/include/RLBoxSandboxPool.h
security/rlbox/moz.build
security/rlbox/src/RLBoxSandboxPool.cpp
new file mode 100644
--- /dev/null
+++ b/security/rlbox/include/RLBoxSandboxPool.h
@@ -0,0 +1,100 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#ifndef SECURITY_RLBOX_SANDBOX_POOL_H_
+#define SECURITY_RLBOX_SANDBOX_POOL_H_
+
+#include "nsCOMPtr.h"
+#include "nsITimer.h"
+#include "nsTArray.h"
+
+#include "mozilla/Mutex.h"
+#include "mozilla/rlbox/rlbox_types.hpp"
+
+namespace mozilla {
+
+class RLBoxSandboxDataBase;
+class RLBoxSandboxPoolData;
+
+// The RLBoxSandboxPool class is used to manage a pool of sandboxes that are
+// reused -- to save sandbox creation time and memory -- and automatically
+// destroyed when no longer in used. The sandbox pool is threadsafe and can be
+// used to share unused sandboxes across a thread pool.
+//
+// Each sandbox pool manages a particular kind of sandbox (e.g., expat
+// sandboxes, woff2 sandboxes, etc.); this is largely because different
+// sandboxes might have different callbacks and attacker assumptions. Hence,
+// RLBoxSandboxPool is intended to be subclassed for the different kinds of
+// sandbox pools. Each sandbox pool class needs to implement the
+// CreateSandboxData() method, which returns a pointer to a RLBoxSandboxDataBase
+// object.  RLBoxSandboxDataBase itself should be subclassed to implement
+// sandbox-specific details.
+class RLBoxSandboxPool : public nsITimerCallback, public nsINamed {
+ public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSITIMERCALLBACK
+  NS_DECL_NSINAMED
+
+  RLBoxSandboxPool(size_t aDelaySeconds = 10)
+      : mPool(),
+        mDelaySeconds(aDelaySeconds),
+        mMutex("RLBoxSandboxPool::mMutex"){};
+
+  void Push(UniquePtr<RLBoxSandboxDataBase> sbx);
+  // PopOrCreate() returns a sandbox from the pool if the pool is not empty and
+  // tries to mint a new one otherwise. If creating a new sandbox fails, the
+  // function returns a nullptr.
+  UniquePtr<RLBoxSandboxPoolData> PopOrCreate();
+
+ protected:
+  virtual UniquePtr<RLBoxSandboxDataBase> CreateSandboxData() = 0;
+  virtual ~RLBoxSandboxPool() = default;
+
+ private:
+  void StartTimer();
+  void CancelTimer();
+
+  nsTArray<UniquePtr<RLBoxSandboxDataBase>> mPool;
+  const size_t mDelaySeconds;
+  nsCOMPtr<nsITimer> mTimer;
+  mozilla::Mutex mMutex;
+};
+
+// The RLBoxSandboxDataBase class serves as the subclass for all sandbox data
+// classes, which keep track of the RLBox sandbox and any relevant sandbox data
+// (e.g., callbacks).
+class RLBoxSandboxDataBase {
+ public:
+  virtual ~RLBoxSandboxDataBase() = default;
+};
+
+// This class is used wrap sandbox data objects (RLBoxSandboxDataBase) when they
+// are popped from sandbox pools. The wrapper destructor pushes the sandbox back
+// into the pool.
+class RLBoxSandboxPoolData {
+ public:
+  RLBoxSandboxPoolData(UniquePtr<RLBoxSandboxDataBase> aSbxData,
+                       RefPtr<RLBoxSandboxPool> aPool) {
+    mSbxData = std::move(aSbxData);
+    mPool = aPool;
+    MOZ_COUNT_CTOR(RLBoxSandboxPoolData);
+  }
+
+  const RLBoxSandboxDataBase* SandboxData() const { return mSbxData.get(); };
+
+  ~RLBoxSandboxPoolData() {
+    mPool->Push(std::move(mSbxData));
+    MOZ_COUNT_DTOR(RLBoxSandboxPoolData);
+  };
+
+ private:
+  UniquePtr<RLBoxSandboxDataBase> mSbxData;
+  RefPtr<RLBoxSandboxPool> mPool;
+};
+
+}  // namespace mozilla
+
+#endif
--- a/security/rlbox/moz.build
+++ b/security/rlbox/moz.build
@@ -5,20 +5,27 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 WASM_SOURCES += [
     "/third_party/rlbox_wasm2c_sandbox/c_src/wasm2c_sandbox_wrapper.c",
 ]
 
 LOCAL_INCLUDES += ["/third_party/wasm2c/wasm2c/"]
 
-EXPORTS += ["/third_party/wasm2c/wasm2c/wasm-rt.h"]
+EXPORTS += [
+    "/third_party/wasm2c/wasm2c/wasm-rt.h",
+]
+
+EXPORTS.mozilla += [
+    "/security/rlbox/include/RLBoxSandboxPool.h",
+]
 
 SOURCES += [
     "!rlbox.wasm.c",
+    "/security/rlbox/src/RLBoxSandboxPool.cpp",
     "/third_party/wasm2c/wasm2c/wasm-rt-impl.c",
     "/third_party/wasm2c/wasm2c/wasm-rt-os-unix.c",
     "/third_party/wasm2c/wasm2c/wasm-rt-os-win.c",
     "/third_party/wasm2c/wasm2c/wasm-rt-wasi.c",
 ]
 
 # Configure the wasm runtime to use a custom trap handler that calls MOZ_CRASH
 DEFINES["WASM_RT_CUSTOM_TRAP_HANDLER"] = "moz_wasm2c_trap_handler"
new file mode 100644
--- /dev/null
+++ b/security/rlbox/src/RLBoxSandboxPool.cpp
@@ -0,0 +1,71 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 "nsThreadUtils.h"
+#include "mozilla/DebugOnly.h"
+#include "mozilla/RLBoxSandboxPool.h"
+
+using namespace mozilla;
+
+NS_IMPL_ISUPPORTS(RLBoxSandboxPool, nsITimerCallback, nsINamed)
+
+void RLBoxSandboxPool::StartTimer() {
+  mMutex.AssertCurrentThreadOwns();
+  MOZ_ASSERT(!mTimer, "timer already initialized");
+  DebugOnly<nsresult> rv = NS_NewTimerWithCallback(
+      getter_AddRefs(mTimer), this, mDelaySeconds * 1000,
+      nsITimer::TYPE_ONE_SHOT, GetMainThreadEventTarget());
+  MOZ_ASSERT(NS_SUCCEEDED(rv), "failed to create timer");
+}
+
+void RLBoxSandboxPool::CancelTimer() {
+  mMutex.AssertCurrentThreadOwns();
+  if (mTimer) {
+    mTimer->Cancel();
+    mTimer = nullptr;
+  }
+}
+
+NS_IMETHODIMP RLBoxSandboxPool::Notify(nsITimer* aTimer) {
+  MutexAutoLock lock(mMutex);
+
+  mPool.Clear();
+  mTimer = nullptr;
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP RLBoxSandboxPool::GetName(nsACString& aName) {
+  aName.AssignLiteral("RLBoxSandboxPool");
+  return NS_OK;
+}
+
+void RLBoxSandboxPool::Push(UniquePtr<RLBoxSandboxDataBase> sbxData) {
+  MutexAutoLock lock(mMutex);
+
+  mPool.AppendElement(std::move(sbxData));
+  if (!mTimer) {
+    StartTimer();
+  }
+}
+
+UniquePtr<RLBoxSandboxPoolData> RLBoxSandboxPool::PopOrCreate() {
+  MutexAutoLock lock(mMutex);
+
+  UniquePtr<RLBoxSandboxDataBase> sbxData;
+  if (!mPool.IsEmpty()) {
+    sbxData = mPool.PopLastElement();
+    CancelTimer();
+    if (!mPool.IsEmpty()) {
+      StartTimer();
+    }
+  } else {
+    sbxData = CreateSandboxData();
+    NS_ENSURE_TRUE(sbxData, nullptr);
+  }
+
+  return MakeUnique<RLBoxSandboxPoolData>(std::move(sbxData), this);
+}