diff --git a/BUILD.bazel b/BUILD.bazel
index 8d1184f0925..c58bf397ef8 100644
--- a/BUILD.bazel
+++ b/BUILD.bazel
@@ -1926,6 +1926,8 @@ filegroup(
         "src/sandbox/external-pointer-table.cc",
         "src/sandbox/external-pointer-table-inl.h",
         "src/sandbox/external-pointer-table.h",
+        "src/sandbox/testing.cc",
+        "src/sandbox/testing.h",
         "src/sandbox/sandbox.cc",
         "src/sandbox/sandbox.h",
         "src/sandbox/sandboxed-pointer-inl.h",
diff --git a/BUILD.gn b/BUILD.gn
index 6adac93038c..23137954f3f 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -311,6 +311,11 @@ declare_args() {
   # Enable all available sandbox features. Implies v8_enable_sandbox.
   v8_enable_sandbox_future = false
 
+  # Expose the memory corruption API to JavaScript. Useful for testing the sandbox.
+  # WARNING This will expose builtins that (by design) cause memory corruption.
+  # Sets -DV8_EXPOSE_MEMORY_CORRUPTION_API
+  v8_expose_memory_corruption_api = false
+
   # Experimental feature for collecting per-class zone memory stats.
   # Requires use_rtti = true
   v8_enable_precise_zone_stats = false
@@ -531,6 +536,9 @@ assert(!v8_enable_sandboxed_pointers || v8_enable_sandbox,
 assert(!v8_enable_sandboxed_external_pointers || v8_enable_sandbox,
        "Sandboxed external pointers require the sandbox")
 
+assert(!v8_expose_memory_corruption_api || v8_enable_sandbox,
+       "The Memory Corruption API requires the sandbox")
+
 assert(
     !v8_enable_pointer_compression_shared_cage || v8_enable_pointer_compression,
     "Can't share a pointer compression cage if pointers aren't compressed")
@@ -992,6 +1000,9 @@ config("features") {
   if (v8_fuchsia_use_vmex_resource) {
     defines += [ "V8_USE_VMEX_RESOURCE" ]
   }
+  if (v8_expose_memory_corruption_api) {
+    defines += [ "V8_EXPOSE_MEMORY_CORRUPTION_API" ]
+  }
 }
 
 config("toolchain") {
@@ -3369,6 +3380,7 @@ v8_header_set("v8_internal_headers") {
     "src/sandbox/sandbox.h",
     "src/sandbox/sandboxed-pointer-inl.h",
     "src/sandbox/sandboxed-pointer.h",
+    "src/sandbox/testing.h",
     "src/snapshot/code-serializer.h",
     "src/snapshot/context-deserializer.h",
     "src/snapshot/context-serializer.h",
@@ -4397,6 +4409,7 @@ v8_source_set("v8_base_without_compiler") {
     "src/runtime/runtime.cc",
     "src/sandbox/external-pointer-table.cc",
     "src/sandbox/sandbox.cc",
+    "src/sandbox/testing.cc",
     "src/snapshot/code-serializer.cc",
     "src/snapshot/context-deserializer.cc",
     "src/snapshot/context-serializer.cc",
diff --git a/src/init/bootstrapper.cc b/src/init/bootstrapper.cc
index 318afc569e3..2db82fed905 100644
--- a/src/init/bootstrapper.cc
+++ b/src/init/bootstrapper.cc
@@ -24,6 +24,7 @@
 #include "src/logging/runtime-call-stats-scope.h"
 #include "src/objects/instance-type.h"
 #include "src/objects/objects.h"
+#include "src/sandbox/testing.h"
 #ifdef ENABLE_VTUNE_TRACEMARK
 #include "src/extensions/vtunedomain-support-extension.h"
 #endif  // ENABLE_VTUNE_TRACEMARK
@@ -5769,6 +5770,12 @@ bool Genesis::InstallSpecialObjects(Isolate* isolate,
   }
 #endif  // V8_ENABLE_WEBASSEMBLY
 
+#ifdef V8_EXPOSE_MEMORY_CORRUPTION_API
+  if (GetProcessWideSandbox()->is_initialized()) {
+    MemoryCorruptionApi::Install(isolate);
+  }
+#endif  // V8_EXPOSE_MEMORY_CORRUPTION_API
+
   return true;
 }
 
diff --git a/src/sandbox/sandbox.h b/src/sandbox/sandbox.h
index 20f2343db50..b5300e4d010 100644
--- a/src/sandbox/sandbox.h
+++ b/src/sandbox/sandbox.h
@@ -12,7 +12,6 @@
 #include "testing/gtest/include/gtest/gtest_prod.h"  // nogncheck
 
 namespace v8 {
-
 namespace internal {
 
 #ifdef V8_SANDBOX_IS_AVAILABLE
diff --git a/src/sandbox/testing.cc b/src/sandbox/testing.cc
new file mode 100644
index 00000000000..1599234fd79
--- /dev/null
+++ b/src/sandbox/testing.cc
@@ -0,0 +1,160 @@
+// Copyright 2022 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+#include "src/sandbox/testing.h"
+#include "src/api/api-inl.h"
+#include "src/api/api-natives.h"
+#include "src/common/globals.h"
+#include "src/execution/isolate-inl.h"
+#include "src/heap/factory.h"
+#include "src/objects/backing-store.h"
+#include "src/objects/js-objects.h"
+#include "src/objects/templates.h"
+#include "src/sandbox/sandbox.h"
+namespace v8 {
+namespace internal {
+#ifdef V8_EXPOSE_MEMORY_CORRUPTION_API
+namespace {
+// Sandbox.byteLength
+void SandboxGetByteLength(const v8::FunctionCallbackInfo<v8::Value>& args) {
+  v8::Isolate* isolate = args.GetIsolate();
+  double sandbox_size = GetProcessWideSandbox()->size();
+  args.GetReturnValue().Set(v8::Number::New(isolate, sandbox_size));
+}
+// new Sandbox.MemoryView(args) -> Sandbox.MemoryView
+void SandboxMemoryView(const v8::FunctionCallbackInfo<v8::Value>& args) {
+  v8::Isolate* isolate = args.GetIsolate();
+  Local<v8::Context> context = isolate->GetCurrentContext();
+  if (!args.IsConstructCall()) {
+    isolate->ThrowError("Sandbox.MemoryView must be invoked with 'new'");
+    return;
+  }
+  Local<v8::Integer> arg1, arg2;
+  if (!args[0]->ToInteger(context).ToLocal(&arg1) ||
+      !args[1]->ToInteger(context).ToLocal(&arg2)) {
+    isolate->ThrowError("Expects two number arguments (start offset and size)");
+    return;
+  }
+  Sandbox* sandbox = GetProcessWideSandbox();
+  CHECK_LE(sandbox->size(), kMaxSafeIntegerUint64);
+  uint64_t offset = arg1->Value();
+  uint64_t size = arg2->Value();
+  if (offset > sandbox->size() || size > sandbox->size() ||
+      (offset + size) > sandbox->size()) {
+    isolate->ThrowError(
+        "The MemoryView must be entirely contained within the sandbox");
+    return;
+  }
+  Factory* factory = reinterpret_cast<Isolate*>(isolate)->factory();
+  std::unique_ptr<BackingStore> memory = BackingStore::WrapAllocation(
+      reinterpret_cast<void*>(sandbox->base() + offset), size,
+      v8::BackingStore::EmptyDeleter, nullptr, SharedFlag::kNotShared);
+  if (!memory) {
+    isolate->ThrowError("Out of memory: MemoryView backing store");
+    return;
+  }
+  Handle<JSArrayBuffer> buffer = factory->NewJSArrayBuffer(std::move(memory));
+  args.GetReturnValue().Set(Utils::ToLocal(buffer));
+}
+// Sandbox.getAddressOf(object) -> Number
+void SandboxGetAddressOf(const v8::FunctionCallbackInfo<v8::Value>& args) {
+  v8::Isolate* isolate = args.GetIsolate();
+  if (args.Length() == 0) {
+    isolate->ThrowError("First argument must be provided");
+    return;
+  }
+  Handle<Object> arg = Utils::OpenHandle(*args[0]);
+  if (!arg->IsHeapObject()) {
+    isolate->ThrowError("First argument must be a HeapObject");
+    return;
+  }
+  // HeapObjects must be allocated inside the pointer compression cage so their
+  // address relative to the start of the sandbox can be obtained simply by
+  // taking the lowest 32 bits of the absolute address.
+  uint32_t address = static_cast<uint32_t>(HeapObject::cast(*arg).address());
+  args.GetReturnValue().Set(v8::Integer::NewFromUnsigned(isolate, address));
+}
+// Sandbox.getSizeOf(object) -> Number
+void SandboxGetSizeOf(const v8::FunctionCallbackInfo<v8::Value>& args) {
+  v8::Isolate* isolate = args.GetIsolate();
+  if (args.Length() == 0) {
+    isolate->ThrowError("First argument must be provided");
+    return;
+  }
+  Handle<Object> arg = Utils::OpenHandle(*args[0]);
+  if (!arg->IsHeapObject()) {
+    isolate->ThrowError("First argument must be a HeapObject");
+    return;
+  }
+  int size = HeapObject::cast(*arg).Size();
+  args.GetReturnValue().Set(v8::Integer::New(isolate, size));
+}
+Handle<FunctionTemplateInfo> NewFunctionTemplate(
+    Isolate* isolate, FunctionCallback func,
+    ConstructorBehavior constructor_behavior) {
+  // Use the API functions here as they are more convenient to use.
+  v8::Isolate* api_isolate = reinterpret_cast<v8::Isolate*>(isolate);
+  Local<FunctionTemplate> function_template =
+      FunctionTemplate::New(api_isolate, func, {}, {}, 0, constructor_behavior,
+                            SideEffectType::kHasSideEffect);
+  return v8::Utils::OpenHandle(*function_template);
+}
+Handle<JSFunction> CreateFunc(Isolate* isolate, FunctionCallback func,
+                              Handle<String> name, bool is_constructor) {
+  ConstructorBehavior constructor_behavior = is_constructor
+                                                 ? ConstructorBehavior::kAllow
+                                                 : ConstructorBehavior::kThrow;
+  Handle<FunctionTemplateInfo> function_template =
+      NewFunctionTemplate(isolate, func, constructor_behavior);
+  return ApiNatives::InstantiateFunction(function_template, name)
+      .ToHandleChecked();
+}
+void InstallFunc(Isolate* isolate, Handle<JSObject> holder,
+                 FunctionCallback func, const char* name, int num_parameters,
+                 bool is_constructor) {
+  Factory* factory = isolate->factory();
+  Handle<String> function_name = factory->NewStringFromAsciiChecked(name);
+  Handle<JSFunction> function =
+      CreateFunc(isolate, func, function_name, is_constructor);
+  function->shared().set_length(num_parameters);
+  JSObject::AddProperty(isolate, holder, function_name, function, NONE);
+}
+void InstallGetter(Isolate* isolate, Handle<JSObject> object,
+                   FunctionCallback func, const char* name) {
+  Factory* factory = isolate->factory();
+  Handle<String> property_name = factory->NewStringFromAsciiChecked(name);
+  Handle<JSFunction> getter = CreateFunc(isolate, func, property_name, false);
+  Handle<Object> setter = factory->null_value();
+  JSObject::DefineAccessor(object, property_name, getter, setter, FROZEN);
+}
+void InstallFunction(Isolate* isolate, Handle<JSObject> holder,
+                     FunctionCallback func, const char* name,
+                     int num_parameters) {
+  InstallFunc(isolate, holder, func, name, num_parameters, false);
+}
+void InstallConstructor(Isolate* isolate, Handle<JSObject> holder,
+                        FunctionCallback func, const char* name,
+                        int num_parameters) {
+  InstallFunc(isolate, holder, func, name, num_parameters, true);
+}
+}  // namespace
+// static
+void MemoryCorruptionApi::Install(Isolate* isolate) {
+  CHECK(GetProcessWideSandbox()->is_initialized());
+  Factory* factory = isolate->factory();
+  // Create the special Sandbox object that provides read/write access to the
+  // sandbox address space alongside other miscellaneous functionality.
+  Handle<JSObject> sandbox =
+      factory->NewJSObject(isolate->object_function(), AllocationType::kOld);
+  InstallGetter(isolate, sandbox, SandboxGetByteLength, "byteLength");
+  InstallConstructor(isolate, sandbox, SandboxMemoryView, "MemoryView", 2);
+  InstallFunction(isolate, sandbox, SandboxGetAddressOf, "getAddressOf", 1);
+  InstallFunction(isolate, sandbox, SandboxGetSizeOf, "getSizeOf", 1);
+  // Install the Sandbox object as property on the global object.
+  Handle<JSGlobalObject> global = isolate->global_object();
+  Handle<String> name = factory->NewStringFromAsciiChecked("Sandbox");
+  JSObject::AddProperty(isolate, global, name, sandbox, DONT_ENUM);
+}
+#endif  // V8_EXPOSE_MEMORY_CORRUPTION_API
+}  // namespace internal
+}  // namespace v8
diff --git a/src/sandbox/testing.h b/src/sandbox/testing.h
new file mode 100644
index 00000000000..c3bb749bf02
--- /dev/null
+++ b/src/sandbox/testing.h
@@ -0,0 +1,21 @@
+// Copyright 2022 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+#ifndef V8_SANDBOX_TESTING_H_
+#define V8_SANDBOX_TESTING_H_
+#include "src/common/globals.h"
+namespace v8 {
+namespace internal {
+#ifdef V8_EXPOSE_MEMORY_CORRUPTION_API
+// A JavaScript API that emulates typical exploit primitives.
+//
+// This can be used for testing the sandbox, for example to write regression
+// tests for bugs in the sandbox or to develop fuzzers.
+class MemoryCorruptionApi {
+ public:
+  V8_EXPORT_PRIVATE static void Install(Isolate* isolate);
+};
+#endif  // V8_EXPOSE_MEMORY_CORRUPTION_API
+}  // namespace internal
+}  // namespace v8
+#endif  // V8_SANDBOX_TESTING_H_
