#include "src/sandbox/testing.h"
#include "src/api/api-inl.h"
#include "src/api/api-natives.h"
#include "src/base/virtual-address-space.h"
#include "src/builtins/builtins.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/instance-type.h"
#include "src/objects/js-objects.h"
#include "src/objects/templates.h"
#include "src/sandbox/sandbox.h"
#ifdef V8_OS_LINUX
#include <signal.h>
#include <sys/mman.h>
#include <sys/ucontext.h>
#include <unistd.h>
#endif
#if defined(V8_USE_ADDRESS_SANITIZER)
#include <sanitizer/asan_interface.h>
#endif
#if defined(V8_USE_ADDRESS_SANITIZER) || defined(V8_USE_MEMORY_SANITIZER) || \
defined(V8_USE_UNDEFINED_BEHAVIOR_SANITIZER)
#define V8_USE_ANY_SANITIZER 1
#include <sanitizer/common_interface_defs.h>
#endif
namespace v8 {
namespace internal {
#ifdef V8_ENABLE_SANDBOX
SandboxTesting::Mode SandboxTesting::mode_ = SandboxTesting::Mode::kDisabled;
#ifdef V8_ENABLE_MEMORY_CORRUPTION_API
namespace {
void SandboxGetBase(const v8::FunctionCallbackInfo<v8::Value>& info) {
DCHECK(ValidateCallbackInfo(info));
v8::Isolate* isolate = info.GetIsolate();
double sandbox_base = Sandbox::current()->base();
info.GetReturnValue().Set(v8::Number::New(isolate, sandbox_base));
}
void SandboxGetByteLength(const v8::FunctionCallbackInfo<v8::Value>& info) {
DCHECK(ValidateCallbackInfo(info));
v8::Isolate* isolate = info.GetIsolate();
double sandbox_size = Sandbox::current()->size();
info.GetReturnValue().Set(v8::Number::New(isolate, sandbox_size));
}
void SandboxMemoryView(const v8::FunctionCallbackInfo<v8::Value>& info) {
DCHECK(ValidateCallbackInfo(info));
v8::Isolate* isolate = info.GetIsolate();
Local<v8::Context> context = isolate->GetCurrentContext();
if (!info.IsConstructCall()) {
isolate->ThrowError("Sandbox.MemoryView must be invoked with 'new'");
return;
}
Local<v8::Integer> arg1, arg2;
if (!info[0]->ToInteger(context).ToLocal(&arg1) ||
!info[1]->ToInteger(context).ToLocal(&arg2)) {
isolate->ThrowError("Expects two number arguments (start offset and size)");
return;
}
Sandbox* sandbox = Sandbox::current();
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));
info.GetReturnValue().Set(Utils::ToLocal(buffer));
}
using ArgumentObjectExtractorFunction = std::function<bool(
const v8::FunctionCallbackInfo<v8::Value>&, Tagged<HeapObject>* out)>;
static bool GetArgumentObjectPassedAsReference(
const v8::FunctionCallbackInfo<v8::Value>& info, Tagged<HeapObject>* out) {
v8::Isolate* isolate = info.GetIsolate();
if (info.Length() == 0) {
isolate->ThrowError("First argument must be provided");
return false;
}
Handle<Object> arg = Utils::OpenHandle(*info[0]);
if (!IsHeapObject(*arg)) {
isolate->ThrowError("First argument must be a HeapObject");
return false;
}
*out = Cast<HeapObject>(*arg);
return true;
}
static bool GetArgumentObjectPassedAsAddress(
const v8::FunctionCallbackInfo<v8::Value>& info, Tagged<HeapObject>* out) {
Sandbox* sandbox = Sandbox::current();
v8::Isolate* isolate = info.GetIsolate();
Local<v8::Context> context = isolate->GetCurrentContext();
if (info.Length() == 0) {
isolate->ThrowError("First argument must be provided");
return false;
}
Local<v8::Uint32> arg1;
if (!info[0]->ToUint32(context).ToLocal(&arg1)) {
isolate->ThrowError("First argument must be the address of a HeapObject");
return false;
}
uint32_t address = arg1->Value();
address &= ~kHeapObjectTagMask;
*out = HeapObject::FromAddress(sandbox->base() + address);
return true;
}
void SandboxGetAddressOf(const v8::FunctionCallbackInfo<v8::Value>& info) {
DCHECK(ValidateCallbackInfo(info));
v8::Isolate* isolate = info.GetIsolate();
Tagged<HeapObject> obj;
if (!GetArgumentObjectPassedAsReference(info, &obj)) {
return;
}
uint32_t address = static_cast<uint32_t>(obj->address());
info.GetReturnValue().Set(v8::Integer::NewFromUnsigned(isolate, address));
}
void SandboxGetObjectAt(const v8::FunctionCallbackInfo<v8::Value>& info) {
DCHECK(ValidateCallbackInfo(info));
v8::Isolate* isolate = info.GetIsolate();
Tagged<HeapObject> obj;
if (!GetArgumentObjectPassedAsAddress(info, &obj)) {
return;
}
Isolate* i_isolate = reinterpret_cast<Isolate*>(isolate);
Handle<Object> handle(obj, i_isolate);
info.GetReturnValue().Set(ToApiHandle<v8::Value>(handle));
}
void SandboxIsValidObjectAt(const v8::FunctionCallbackInfo<v8::Value>& info) {
DCHECK(ValidateCallbackInfo(info));
v8::Isolate* isolate = info.GetIsolate();
Sandbox* sandbox = Sandbox::current();
Heap* heap = reinterpret_cast<Isolate*>(isolate)->heap();
auto IsLocatedInMappedMemory = [&](Address address) {
if (heap->memory_allocator()->LookupChunkContainingAddress(address) !=
nullptr) {
return true;
}
return heap->read_only_space()->ContainsSlow(address);
};
Tagged<HeapObject> obj;
if (!GetArgumentObjectPassedAsAddress(info, &obj)) {
return;
}
info.GetReturnValue().Set(false);
Address current = obj.address();
for (int i = 0; i < 3; i++) {
if (!IsLocatedInMappedMemory(current)) {
return;
}
uint32_t map_word = *reinterpret_cast<uint32_t*>(current);
if ((map_word & kHeapObjectTag) != kHeapObjectTag) {
return;
}
Address map_address = sandbox->base() + map_word - kHeapObjectTag;
if (map_address == current) {
info.GetReturnValue().Set(true);
return;
}
current = map_address;
}
}
static void SandboxIsWritableImpl(
const v8::FunctionCallbackInfo<v8::Value>& info,
ArgumentObjectExtractorFunction getArgumentObject) {
DCHECK(ValidateCallbackInfo(info));
Tagged<HeapObject> obj;
if (!getArgumentObject(info, &obj)) {
return;
}
MemoryChunkMetadata* chunk = MemoryChunkMetadata::FromHeapObject(
reinterpret_cast<Isolate*>(info.GetIsolate()), obj);
bool is_writable = chunk->IsWritable();
info.GetReturnValue().Set(is_writable);
}
void SandboxIsWritable(const v8::FunctionCallbackInfo<v8::Value>& info) {
SandboxIsWritableImpl(info, &GetArgumentObjectPassedAsReference);
}
void SandboxIsWritableObjectAt(
const v8::FunctionCallbackInfo<v8::Value>& info) {
SandboxIsWritableImpl(info, &GetArgumentObjectPassedAsAddress);
}
static void SandboxGetSizeOfImpl(
const v8::FunctionCallbackInfo<v8::Value>& info,
ArgumentObjectExtractorFunction getArgumentObject) {
DCHECK(ValidateCallbackInfo(info));
Tagged<HeapObject> obj;
if (!getArgumentObject(info, &obj)) {
return;
}
int size = obj->Size();
info.GetReturnValue().Set(size);
}
void SandboxGetSizeOf(const v8::FunctionCallbackInfo<v8::Value>& info) {
SandboxGetSizeOfImpl(info, &GetArgumentObjectPassedAsReference);
}
void SandboxGetSizeOfObjectAt(const v8::FunctionCallbackInfo<v8::Value>& info) {
SandboxGetSizeOfImpl(info, &GetArgumentObjectPassedAsAddress);
}
static void SandboxGetInstanceTypeOfImpl(
const v8::FunctionCallbackInfo<v8::Value>& info,
ArgumentObjectExtractorFunction getArgumentObject) {
DCHECK(ValidateCallbackInfo(info));
v8::Isolate* isolate = info.GetIsolate();
Tagged<HeapObject> obj;
if (!getArgumentObject(info, &obj)) {
return;
}
InstanceType type = obj->map()->instance_type();
std::stringstream out;
out << type;
MaybeLocal<v8::String> result =
v8::String::NewFromUtf8(isolate, out.str().c_str());
info.GetReturnValue().Set(result.ToLocalChecked());
}
void SandboxGetInstanceTypeOf(const v8::FunctionCallbackInfo<v8::Value>& info) {
SandboxGetInstanceTypeOfImpl(info, &GetArgumentObjectPassedAsReference);
}
void SandboxGetInstanceTypeOfObjectAt(
const v8::FunctionCallbackInfo<v8::Value>& info) {
SandboxGetInstanceTypeOfImpl(info, &GetArgumentObjectPassedAsAddress);
}
static void SandboxGetInstanceTypeIdOfImpl(
const v8::FunctionCallbackInfo<v8::Value>& info,
ArgumentObjectExtractorFunction getArgumentObject) {
DCHECK(ValidateCallbackInfo(info));
Tagged<HeapObject> obj;
if (!getArgumentObject(info, &obj)) {
return;
}
InstanceType type = obj->map()->instance_type();
static_assert(std::is_same_v<std::underlying_type_t<InstanceType>, uint16_t>);
if (type > LAST_TYPE) {
const uint16_t kUnknownInstanceType = std::numeric_limits<uint16_t>::max();
type = static_cast<InstanceType>(kUnknownInstanceType);
}
info.GetReturnValue().Set(type);
}
void SandboxGetInstanceTypeIdOf(
const v8::FunctionCallbackInfo<v8::Value>& info) {
SandboxGetInstanceTypeIdOfImpl(info, &GetArgumentObjectPassedAsReference);
}
void SandboxGetInstanceTypeIdOfObjectAt(
const v8::FunctionCallbackInfo<v8::Value>& info) {
SandboxGetInstanceTypeIdOfImpl(info, &GetArgumentObjectPassedAsAddress);
}
void SandboxGetInstanceTypeIdFor(
const v8::FunctionCallbackInfo<v8::Value>& info) {
DCHECK(ValidateCallbackInfo(info));
v8::Isolate* isolate = info.GetIsolate();
v8::String::Utf8Value type_name(isolate, info[0]);
if (!*type_name) {
isolate->ThrowError("First argument must be a string");
return;
}
auto& all_types = SandboxTesting::GetInstanceTypeMap();
if (all_types.find(*type_name) == all_types.end()) {
isolate->ThrowError(
"Unknown type name. If needed, add it in "
"SandboxTesting::GetInstanceTypeMap");
return;
}
InstanceType type_id = all_types[*type_name];
info.GetReturnValue().Set(type_id);
}
void SandboxGetFieldOffset(const v8::FunctionCallbackInfo<v8::Value>& info) {
DCHECK(ValidateCallbackInfo(info));
v8::Isolate* isolate = info.GetIsolate();
Local<v8::Context> context = isolate->GetCurrentContext();
if (!info[0]->IsInt32()) {
isolate->ThrowError("First argument must be an integer");
return;
}
int raw_type = info[0]->Int32Value(context).FromMaybe(-1);
if (raw_type < FIRST_TYPE || raw_type > LAST_TYPE) {
isolate->ThrowError("Invalid instance type");
return;
}
InstanceType instance_type = static_cast<InstanceType>(raw_type);
v8::String::Utf8Value field_name(isolate, info[1]);
if (!*field_name) {
isolate->ThrowError("Second argument must be a string");
return;
}
auto& all_fields = SandboxTesting::GetFieldOffsetMap();
if (all_fields.find(instance_type) == all_fields.end()) {
isolate->ThrowError(
"Unknown object type. If needed, add it in "
"SandboxTesting::GetFieldOffsetMap");
return;
}
auto& obj_fields = all_fields[instance_type];
if (obj_fields.find(*field_name) == obj_fields.end()) {
isolate->ThrowError(
"Unknown field. If needed, add it in "
"SandboxTesting::GetFieldOffsetMap");
return;
}
int offset = obj_fields[*field_name];
info.GetReturnValue().Set(offset);
}
void SandboxGetBuiltinNames(const v8::FunctionCallbackInfo<v8::Value>& info) {
DCHECK(ValidateCallbackInfo(info));
v8::Isolate* isolate = info.GetIsolate();
Local<v8::Context> context = isolate->GetCurrentContext();
Local<v8::Array> result = v8::Array::New(isolate, Builtins::kBuiltinCount);
for (Builtin i = Builtins::kFirst; i <= Builtins::kLast; ++i) {
Local<v8::String> name =
v8::String::NewFromUtf8(isolate, Builtins::name(i)).ToLocalChecked();
CHECK(result->Set(context, static_cast<uint32_t>(i), name).FromJust());
}
info.GetReturnValue().Set(result);
}
void SandboxSetFunctionCodeToBuiltin(
const v8::FunctionCallbackInfo<v8::Value>& info) {
DCHECK(ValidateCallbackInfo(info));
v8::Isolate* isolate = info.GetIsolate();
Local<v8::Context> context = isolate->GetCurrentContext();
if (!IsJSFunction(*v8::Utils::OpenHandle(*info[0]))) {
isolate->ThrowError("First argument must be an function");
return;
}
if (!info[1]->IsInt32()) {
isolate->ThrowError("Second argument must be an integer");
return;
}
int raw_builtin_id = info[1]->Int32Value(context).FromMaybe(-1);
if (!Builtins::IsBuiltinId(raw_builtin_id)) {
isolate->ThrowError("Invalid builtin id");
return;
}
Builtin builtin = static_cast<Builtin>(raw_builtin_id);
auto function = Cast<JSFunction>(v8::Utils::OpenDirectHandle(*info[0]));
Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
function->UpdateCode(i_isolate, i_isolate->builtins()->code(builtin));
info.GetReturnValue().Set(true);
}
Handle<FunctionTemplateInfo> NewFunctionTemplate(
Isolate* isolate, FunctionCallback func,
ConstructorBehavior constructor_behavior) {
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(isolate, 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::DefineOwnAccessorIgnoreAttributes(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);
}
}
void SandboxTesting::InstallMemoryCorruptionApi(Isolate* isolate) {
#ifndef V8_ENABLE_MEMORY_CORRUPTION_API
#error "This function should not be available in any shipping build " \
"where it could potentially be abused to facilitate exploitation."
#endif
CHECK(Sandbox::current()->is_initialized());
Handle<JSObject> sandbox = isolate->factory()->NewJSObject(
isolate->object_function(), AllocationType::kOld);
InstallGetter(isolate, sandbox, SandboxGetBase, "base");
InstallGetter(isolate, sandbox, SandboxGetByteLength, "byteLength");
InstallConstructor(isolate, sandbox, SandboxMemoryView, "MemoryView", 2);
InstallFunction(isolate, sandbox, SandboxGetAddressOf, "getAddressOf", 1);
InstallFunction(isolate, sandbox, SandboxGetObjectAt, "getObjectAt", 1);
InstallFunction(isolate, sandbox, SandboxIsValidObjectAt, "isValidObjectAt",
1);
InstallFunction(isolate, sandbox, SandboxIsWritable, "isWritable", 1);
InstallFunction(isolate, sandbox, SandboxIsWritableObjectAt,
"isWritableObjectAt", 1);
InstallFunction(isolate, sandbox, SandboxGetSizeOf, "getSizeOf", 1);
InstallFunction(isolate, sandbox, SandboxGetSizeOfObjectAt,
"getSizeOfObjectAt", 1);
InstallFunction(isolate, sandbox, SandboxGetInstanceTypeOf,
"getInstanceTypeOf", 1);
InstallFunction(isolate, sandbox, SandboxGetInstanceTypeOfObjectAt,
"getInstanceTypeOfObjectAt", 1);
InstallFunction(isolate, sandbox, SandboxGetInstanceTypeIdOf,
"getInstanceTypeIdOf", 1);
InstallFunction(isolate, sandbox, SandboxGetInstanceTypeIdOfObjectAt,
"getInstanceTypeIdOfObjectAt", 1);
InstallFunction(isolate, sandbox, SandboxGetInstanceTypeIdFor,
"getInstanceTypeIdFor", 1);
InstallFunction(isolate, sandbox, SandboxGetFieldOffset, "getFieldOffset", 2);
InstallFunction(isolate, sandbox, SandboxGetBuiltinNames, "getBuiltinNames",
0);
InstallFunction(isolate, sandbox, SandboxSetFunctionCodeToBuiltin,
"setFunctionCodeToBuiltin", 2);
Handle<JSGlobalObject> global = isolate->global_object();
Handle<String> name =
isolate->factory()->NewStringFromAsciiChecked("Sandbox");
JSObject::AddProperty(isolate, global, name, sandbox, DONT_ENUM);
}
#endif
namespace {
#ifdef V8_OS_LINUX
void PrintToStderr(const char* output) {
ssize_t return_val = write(STDERR_FILENO, output, strlen(output));
USE(return_val);
}
[[noreturn]] void FilterCrash(const char* reason) {
PrintToStderr(reason);
int status =
SandboxTesting::mode() == SandboxTesting::Mode::kForFuzzing ? -1 : 0;
_exit(status);
}
struct sigaction g_old_sigabrt_handler, g_old_sigtrap_handler,
g_old_sigbus_handler, g_old_sigsegv_handler;
void UninstallCrashFilter() {
sigaction(SIGABRT, &g_old_sigabrt_handler, nullptr);
sigaction(SIGTRAP, &g_old_sigtrap_handler, nullptr);
sigaction(SIGBUS, &g_old_sigbus_handler, nullptr);
sigaction(SIGSEGV, &g_old_sigsegv_handler, nullptr);
#ifdef V8_USE_ANY_SANITIZER
__sanitizer_set_death_callback(nullptr);
#endif
}
void CrashFilter(int signal, siginfo_t* info, void* context) {
if (signal == SIGABRT) {
FilterCrash("Caught harmless signal (SIGABRT). Exiting process...\n");
}
if (signal == SIGTRAP) {
FilterCrash("Caught harmless signal (SIGTRAP). Exiting process...\n");
}
Address faultaddr = reinterpret_cast<Address>(info->si_addr);
if (Sandbox::current()->Contains(faultaddr)) {
FilterCrash(
"Caught harmless memory access violation (inside sandbox address "
"space). Exiting process...\n");
}
if (info->si_code == SI_KERNEL && faultaddr == 0) {
FilterCrash(
"Caught harmless memory access violation (non-canonical address). "
"Exiting process...\n");
}
if (faultaddr >= 0x8000'0000'0000'0000ULL) {
FilterCrash(
"Caught harmless memory access violation (kernel space address). "
"Exiting process...\n");
}
if (faultaddr < 0x1000) {
FilterCrash(
"Caught harmless memory access violation (nullptr dereference). "
"Exiting process...\n");
}
if (faultaddr < 4ULL * GB) {
FilterCrash(
"Caught harmless memory access violation (first 4GB of virtual address "
"space). Exiting process...\n");
}
pthread_attr_t attr;
int pthread_error = pthread_getattr_np(pthread_self(), &attr);
if (!pthread_error) {
uintptr_t stack_base;
size_t stack_size;
pthread_error = pthread_attr_getstack(
&attr, reinterpret_cast<void**>(&stack_base), &stack_size);
const size_t kMinStackGuardRegionSize = sysconf(_SC_PAGESIZE);
uintptr_t stack_guard_region_start = stack_base - kMinStackGuardRegionSize;
uintptr_t stack_guard_region_end = stack_base;
if (!pthread_error && stack_guard_region_start <= faultaddr &&
faultaddr < stack_guard_region_end) {
FilterCrash("Caught harmless stack overflow. Exiting process...\n");
}
}
if (info->si_code == SEGV_ACCERR) {
FilterCrash(
"Caught harmless memory access violation (memory permission "
"violation). Exiting process...\n");
}
UninstallCrashFilter();
PrintToStderr("\n## V8 sandbox violation detected!\n\n");
#ifdef V8_HOST_ARCH_X64
ucontext_t* ctx = reinterpret_cast<ucontext_t*>(context);
static constexpr greg_t kWriteAccessBit = 1;
const bool write_access =
ctx->uc_mcontext.gregs[REG_ERR] & (1 << kWriteAccessBit);
if (!write_access) {
PrintToStderr(
"The sandbox violation was a *read* access which is technically not a "
"sandbox violation. This requires manual investigation.\n");
}
#endif
}
#ifdef V8_USE_ANY_SANITIZER
void SanitizerFaultHandler() {
#ifdef V8_USE_ADDRESS_SANITIZER
if (__asan_report_present()) {
Address faultaddr = reinterpret_cast<Address>(__asan_get_report_address());
if (faultaddr == kNullAddress) {
FilterCrash(
"Caught ASan fault without a fault address. Ignoring it as we cannot "
"check if it is a sandbox violation. Exiting process...\n");
}
if (Sandbox::current()->Contains(faultaddr)) {
FilterCrash(
"Caught harmless ASan fault (inside sandbox address space). Exiting "
"process...\n");
}
}
#endif
UninstallCrashFilter();
PrintToStderr("\n## V8 sandbox violation detected!\n\n");
}
#endif
void InstallCrashFilter() {
base::OS::EnsureAlternativeSignalStackIsAvailableForCurrentThread();
struct sigaction action;
memset(&action, 0, sizeof(action));
action.sa_flags = SA_SIGINFO | SA_ONSTACK;
action.sa_sigaction = &CrashFilter;
sigemptyset(&action.sa_mask);
bool success = true;
success &= (sigaction(SIGABRT, &action, &g_old_sigabrt_handler) == 0);
success &= (sigaction(SIGTRAP, &action, &g_old_sigtrap_handler) == 0);
success &= (sigaction(SIGBUS, &action, &g_old_sigbus_handler) == 0);
success &= (sigaction(SIGSEGV, &action, &g_old_sigsegv_handler) == 0);
CHECK(success);
#ifdef V8_USE_ANY_SANITIZER
__sanitizer_set_death_callback(&SanitizerFaultHandler);
#endif
}
#endif
}
void SandboxTesting::Enable(Mode mode) {
CHECK_EQ(mode_, Mode::kDisabled);
CHECK_NE(mode, Mode::kDisabled);
CHECK(Sandbox::current()->is_initialized());
mode_ = mode;
fprintf(stderr,
"Sandbox testing mode is enabled. Only sandbox violations will be "
"reported, all other crashes will be ignored.\n");
fprintf(stderr, "Sandbox bounds: [%p,%p)\n",
reinterpret_cast<void*>(Sandbox::current()->base()),
reinterpret_cast<void*>(Sandbox::current()->end()));
#ifdef V8_OS_LINUX
InstallCrashFilter();
#else
FATAL("The sandbox crash filter is currently only available on Linux");
#endif
}
void SandboxTesting::Disable() {
if (mode_ == Mode::kDisabled) return;
mode_ = Mode::kDisabled;
#ifdef V8_OS_LINUX
UninstallCrashFilter();
#else
FATAL("The sandbox crash filter is currently only available on Linux");
#endif
}
SandboxTesting::InstanceTypeMap& SandboxTesting::GetInstanceTypeMap() {
static base::LeakyObject<InstanceTypeMap> g_known_instance_types;
auto& types = *g_known_instance_types.get();
bool is_initialized = types.size() != 0;
if (!is_initialized) {
types["JS_OBJECT_TYPE"] = JS_OBJECT_TYPE;
types["JS_FUNCTION_TYPE"] = JS_FUNCTION_TYPE;
types["JS_BOUND_FUNCTION_TYPE"] = JS_BOUND_FUNCTION_TYPE;
types["JS_ARRAY_TYPE"] = JS_ARRAY_TYPE;
types["JS_ARRAY_BUFFER_TYPE"] = JS_ARRAY_BUFFER_TYPE;
types["JS_TYPED_ARRAY_TYPE"] = JS_TYPED_ARRAY_TYPE;
types["SEQ_ONE_BYTE_STRING_TYPE"] = SEQ_ONE_BYTE_STRING_TYPE;
types["SEQ_TWO_BYTE_STRING_TYPE"] = SEQ_TWO_BYTE_STRING_TYPE;
types["INTERNALIZED_ONE_BYTE_STRING_TYPE"] =
INTERNALIZED_ONE_BYTE_STRING_TYPE;
types["SLICED_ONE_BYTE_STRING_TYPE"] = SLICED_ONE_BYTE_STRING_TYPE;
types["CONS_ONE_BYTE_STRING_TYPE"] = CONS_ONE_BYTE_STRING_TYPE;
types["SHARED_FUNCTION_INFO_TYPE"] = SHARED_FUNCTION_INFO_TYPE;
types["SCRIPT_TYPE"] = SCRIPT_TYPE;
types["JS_PROMISE_TYPE"] = JS_PROMISE_TYPE;
types["PROMISE_REACTION"] = PROMISE_REACTION_TYPE;
types["JS_FUNCTION"] = JS_FUNCTION_TYPE;
types["SHARED_FUNCTION_INFO"] = SHARED_FUNCTION_INFO_TYPE;
#ifdef V8_ENABLE_WEBASSEMBLY
types["WASM_MODULE_OBJECT_TYPE"] = WASM_MODULE_OBJECT_TYPE;
types["WASM_INSTANCE_OBJECT_TYPE"] = WASM_INSTANCE_OBJECT_TYPE;
types["WASM_FUNC_REF_TYPE"] = WASM_FUNC_REF_TYPE;
types["WASM_TABLE_OBJECT_TYPE"] = WASM_TABLE_OBJECT_TYPE;
types["WASM_RESUME_DATA"] = WASM_RESUME_DATA_TYPE;
#endif
}
return types;
}
SandboxTesting::FieldOffsetMap& SandboxTesting::GetFieldOffsetMap() {
static base::LeakyObject<FieldOffsetMap> g_known_fields;
auto& fields = *g_known_fields.get();
bool is_initialized = fields.size() != 0;
if (!is_initialized) {
fields[JS_FUNCTION_TYPE]["dispatch_handle"] =
JSFunction::kDispatchHandleOffset;
fields[JS_FUNCTION_TYPE]["shared_function_info"] =
JSFunction::kSharedFunctionInfoOffset;
fields[JS_BOUND_FUNCTION_TYPE]["bound_arguments"] =
JSBoundFunction::kBoundArgumentsOffset;
fields[JS_ARRAY_TYPE]["elements"] = JSArray::kElementsOffset;
fields[JS_ARRAY_TYPE]["length"] = JSArray::kLengthOffset;
fields[JS_TYPED_ARRAY_TYPE]["byte_length"] =
JSTypedArray::kRawByteLengthOffset;
fields[JS_TYPED_ARRAY_TYPE]["byte_offset"] =
JSTypedArray::kRawByteOffsetOffset;
fields[JS_TYPED_ARRAY_TYPE]["external_pointer"] =
JSTypedArray::kExternalPointerOffset;
fields[JS_TYPED_ARRAY_TYPE]["base_pointer"] =
JSTypedArray::kBasePointerOffset;
fields[SEQ_ONE_BYTE_STRING_TYPE]["length"] =
offsetof(SeqOneByteString, length_);
fields[SEQ_TWO_BYTE_STRING_TYPE]["hash"] =
offsetof(SeqTwoByteString, raw_hash_field_);
fields[SEQ_TWO_BYTE_STRING_TYPE]["length"] =
offsetof(SeqTwoByteString, length_);
fields[INTERNALIZED_ONE_BYTE_STRING_TYPE]["length"] =
offsetof(InternalizedString, length_);
fields[SLICED_ONE_BYTE_STRING_TYPE]["parent"] =
offsetof(SlicedString, parent_);
fields[CONS_ONE_BYTE_STRING_TYPE]["length"] = offsetof(ConsString, length_);
fields[CONS_ONE_BYTE_STRING_TYPE]["first"] = offsetof(ConsString, first_);
fields[CONS_ONE_BYTE_STRING_TYPE]["second"] = offsetof(ConsString, second_);
fields[SHARED_FUNCTION_INFO_TYPE]["trusted_function_data"] =
SharedFunctionInfo::kTrustedFunctionDataOffset;
fields[SHARED_FUNCTION_INFO_TYPE]["length"] =
SharedFunctionInfo::kLengthOffset;
fields[SHARED_FUNCTION_INFO_TYPE]["formal_parameter_count"] =
SharedFunctionInfo::kFormalParameterCountOffset;
fields[SHARED_FUNCTION_INFO_TYPE]["function_data"] =
SharedFunctionInfo::kUntrustedFunctionDataOffset;
fields[SHARED_FUNCTION_INFO_TYPE]["script"] =
SharedFunctionInfo::kScriptOffset;
fields[SCRIPT_TYPE]["wasm_managed_native_module"] =
Script::kEvalFromPositionOffset;
fields[JS_PROMISE_TYPE]["reactions_or_result"] =
JSPromise::kReactionsOrResultOffset;
fields[PROMISE_REACTION_TYPE]["fulfill_handler"] =
offsetof(PromiseReaction, fulfill_handler_);
fields[JS_FUNCTION_TYPE]["shared_function_info"] =
JSFunction::kSharedFunctionInfoOffset;
#ifdef V8_ENABLE_WEBASSEMBLY
fields[WASM_MODULE_OBJECT_TYPE]["managed_native_module"] =
WasmModuleObject::kManagedNativeModuleOffset;
fields[WASM_MODULE_OBJECT_TYPE]["script"] = WasmModuleObject::kScriptOffset;
fields[WASM_INSTANCE_OBJECT_TYPE]["module_object"] =
WasmInstanceObject::kModuleObjectOffset;
fields[WASM_FUNC_REF_TYPE]["trusted_internal"] =
WasmFuncRef::kTrustedInternalOffset;
fields[WASM_TABLE_OBJECT_TYPE]["entries"] = WasmTableObject::kEntriesOffset;
fields[WASM_TABLE_OBJECT_TYPE]["current_length"] =
WasmTableObject::kCurrentLengthOffset;
fields[WASM_TABLE_OBJECT_TYPE]["maximum_length"] =
WasmTableObject::kMaximumLengthOffset;
fields[WASM_TABLE_OBJECT_TYPE]["raw_type"] =
WasmTableObject::kRawTypeOffset;
fields[WASM_RESUME_DATA_TYPE]["trusted_suspender"] =
WasmResumeData::kTrustedSuspenderOffset;
#endif
}
return fields;
}
#endif
}
}