Commit 059903de authored by Samuel Groß's avatar Samuel Groß Committed by V8 LUCI CQ

[sandbox] Implement sandbox crash filter

If enabled, a signal handler is installed which intercepts memory access
violations (e.g. SIGSEGV) and checks whether they occurred inside the
sandbox address space, in which case the process is terminated cleanly
as this does not represent a (security) issue with the sandbox. However,
if the access violation occurred outside the sandbox, the access
violation is forwarded to the original signal handler.

The filter can be enabled in d8 by specifying
--enable-sandbox-crash-filter.

Bug: v8:12878
Change-Id: If9d76267e90ee79ee81ab793d7774afed6226b7c
Cq-Include-Trybots: luci.v8.try:v8_linux64_heap_sandbox_dbg_ng,v8_linux_arm64_sim_heap_sandbox_dbg_ng
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3688408Reviewed-by: 's avatarCamillo Bruni <cbruni@chromium.org>
Commit-Queue: Samuel Groß <saelo@chromium.org>
Reviewed-by: 's avatarIgor Sheludko <ishell@chromium.org>
Cr-Commit-Position: refs/heads/main@{#80999}
parent abcb6bb8
......@@ -62,6 +62,7 @@
#include "src/parsing/parsing.h"
#include "src/parsing/scanner-character-streams.h"
#include "src/profiler/profile-generator.h"
#include "src/sandbox/testing.h"
#include "src/snapshot/snapshot.h"
#include "src/tasks/cancelable-task.h"
#include "src/utils/ostreams.h"
......@@ -4765,6 +4766,11 @@ bool Shell::SetOptions(int argc, char* argv[]) {
} else if (strcmp(argv[i], "--expose-fast-api") == 0) {
options.expose_fast_api = true;
argv[i] = nullptr;
#if V8_ENABLE_SANDBOX
} else if (strcmp(argv[i], "--enable-sandbox-crash-filter") == 0) {
options.enable_sandbox_crash_filter = true;
argv[i] = nullptr;
#endif // V8_ENABLE_SANDBOX
} else {
#ifdef V8_TARGET_OS_WIN
PreProcessUnicodeFilenameArg(argv, i);
......@@ -5496,6 +5502,12 @@ int Shell::Main(int argc, char* argv[]) {
if (!v8::V8::InitializeSandbox()) {
FATAL("Could not initialize the sandbox");
}
if (options.enable_sandbox_crash_filter) {
// Note: this must happen before the Wasm trap handler is installed, so
// that the Wasm trap handler is invoked first (and can handle Wasm OOB
// accesses), then forwards all "real" crashes to the sandbox crash filter.
i::SandboxTesting::InstallSandboxCrashFilter();
}
#endif
v8::V8::Initialize();
if (options.snapshot_blob) {
......
......@@ -479,6 +479,10 @@ class ShellOptions {
DisallowReassignment<bool> wasm_trap_handler = {"wasm-trap-handler", true};
#endif // V8_ENABLE_WEBASSEMBLY
DisallowReassignment<bool> expose_fast_api = {"expose-fast-api", false};
#if V8_ENABLE_SANDBOX
DisallowReassignment<bool> enable_sandbox_crash_filter = {
"enable-sandbox-crash-filter", false};
#endif // V8_ENABLE_SANDBOX
};
class Shell : public i::AllStatic {
......
......@@ -5961,7 +5961,7 @@ bool Genesis::InstallSpecialObjects(Isolate* isolate,
#ifdef V8_EXPOSE_MEMORY_CORRUPTION_API
if (GetProcessWideSandbox()->is_initialized()) {
MemoryCorruptionApi::Install(isolate);
SandboxTesting::InstallMemoryCorruptionApi(isolate);
}
#endif // V8_EXPOSE_MEMORY_CORRUPTION_API
......
......@@ -14,9 +14,16 @@
#include "src/objects/templates.h"
#include "src/sandbox/sandbox.h"
#ifdef V8_OS_LINUX
#include <signal.h>
#include <unistd.h>
#endif // V8_OS_LINUX
namespace v8 {
namespace internal {
#ifdef V8_ENABLE_SANDBOX
#ifdef V8_EXPOSE_MEMORY_CORRUPTION_API
namespace {
......@@ -166,10 +173,14 @@ void InstallConstructor(Isolate* isolate, Handle<JSObject> holder,
} // namespace
// static
void MemoryCorruptionApi::Install(Isolate* isolate) {
void SandboxTesting::InstallMemoryCorruptionApi(Isolate* isolate) {
CHECK(GetProcessWideSandbox()->is_initialized());
#ifndef V8_EXPOSE_MEMORY_CORRUPTION_API
#error "This function should not be available in any shipping build " \
"where it could potentially be abused to facilitate exploitation."
#endif
Factory* factory = isolate->factory();
// Create the special Sandbox object that provides read/write access to the
......@@ -190,5 +201,58 @@ void MemoryCorruptionApi::Install(Isolate* isolate) {
#endif // V8_EXPOSE_MEMORY_CORRUPTION_API
namespace {
// Signal handler checking whether a memory access violation happened inside or
// outside of the sandbox address space. If inside, the signal is ignored and
// the process terminated normally, in the latter case the original signal
// handler is restored and the signal delivered again.
#ifdef V8_OS_LINUX
struct sigaction g_old_sigbus_handler, g_old_sigsegv_handler;
void SandboxSignalHandler(int signal, siginfo_t* info, void* void_context) {
// NOTE: This code MUST be async-signal safe.
// NO malloc or stdio is allowed here.
Address faultaddr = reinterpret_cast<Address>(info->si_addr);
if (GetProcessWideSandbox()->Contains(faultaddr)) {
// Access violation happened inside the sandbox, so ignore it and just exit.
_exit(1);
}
// Otherwise it's a sandbox violation, so restore the original signal
// handler, then return from this handler. The faulting instruction will be
// re-executed and will again trigger the access violation, but now the
// signal will be handled by the original signal handler.
//
// Should any of the sigaction calls below ever fail, the default signal
// handler will be invoked (due to SA_RESETHAND) and will terminate the
// process, so there's no need to attempt to handle that condition.
sigaction(SIGBUS, &g_old_sigbus_handler, nullptr);
sigaction(SIGSEGV, &g_old_sigsegv_handler, nullptr);
}
#endif // V8_OS_LINUX
} // namespace
void SandboxTesting::InstallSandboxCrashFilter() {
CHECK(GetProcessWideSandbox()->is_initialized());
#ifdef V8_OS_LINUX
struct sigaction action;
memset(&action, 0, sizeof(action));
action.sa_flags = SA_RESETHAND | SA_SIGINFO;
action.sa_sigaction = &SandboxSignalHandler;
sigemptyset(&action.sa_mask);
bool success = true;
success &= (sigaction(SIGBUS, &action, &g_old_sigbus_handler) == 0);
success &= (sigaction(SIGSEGV, &action, &g_old_sigsegv_handler) == 0);
CHECK(success);
#else
FATAL("The sandbox crash filter is currently only available on Linux");
#endif // V8_OS_LINUX
}
#endif // V8_ENABLE_SANDBOX
} // namespace internal
} // namespace v8
......@@ -10,17 +10,31 @@
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 {
#ifdef V8_ENABLE_SANDBOX
class SandboxTesting {
public:
V8_EXPORT_PRIVATE static void Install(Isolate* isolate);
#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.
V8_EXPORT_PRIVATE static void InstallMemoryCorruptionApi(Isolate* isolate);
#endif // V8_EXPOSE_MEMORY_CORRUPTION_API
// A signal handler that filters out "harmless" (for the sandbox) crashes.
//
// If an access violation (i.e. a SIGSEGV or SIGBUS) happens inside the
// sandbox address space it is considered harmless for the sandbox and so the
// process is simply terminated through _exit. On the other hand, an access
// violation outside the sandbox address space indicates a security issue with
// the sandbox. In that case, the signal is forwarded to the original signal
// handler which will report the violation appropriately.
// Currently supported on Linux only.
V8_EXPORT_PRIVATE static void InstallSandboxCrashFilter();
};
#endif // V8_EXPOSE_MEMORY_CORRUPTION_API
#endif // V8_ENABLE_SANDBOX
} // namespace internal
} // namespace v8
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment