blob: 5a5a01f0fa1b1a57e5d1e9c57368ae1c5cee0ced [file]
// Copyright 2012 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/runtime/runtime.h"
#include "src/base/hashmap.h"
#include "src/execution/isolate.h"
#include "src/runtime/runtime-utils.h"
#include "src/strings/string-hasher-inl.h"
namespace v8 {
namespace internal {
// Header of runtime functions.
#define F(name, number_of_args, result_size, ...) \
Address Runtime_##name(int args_length, Address* args_object, \
Isolate* isolate);
FOR_EACH_INTRINSIC_RETURN_OBJECT(F)
#undef F
#define P(name, number_of_args, result_size, ...) \
ObjectPair Runtime_##name(int args_length, Address* args_object, \
Isolate* isolate);
FOR_EACH_INTRINSIC_RETURN_PAIR(P)
#undef P
// clang-format off
#define F(name, number_of_args, result_size, ...) \
{ \
Runtime::k##name, Runtime::RUNTIME, \
#name, FUNCTION_ADDR(Runtime_##name), \
number_of_args, result_size},
#define I(name, number_of_args, result_size, ...) \
{ \
Runtime::kInline##name, Runtime::INLINE, \
"_" #name, FUNCTION_ADDR(Runtime_##name), \
number_of_args, result_size},
// clang-format on
static const Runtime::Function kIntrinsicFunctions[] = {
FOR_EACH_INTRINSIC(F) FOR_EACH_INLINE_INTRINSIC(I)};
#undef I
#undef F
namespace {
V8_DECLARE_ONCE(initialize_function_name_map_once);
static const base::CustomMatcherHashMap* kRuntimeFunctionNameMap;
struct IntrinsicFunctionIdentifier {
IntrinsicFunctionIdentifier(const unsigned char* data, const int length)
: data_(data), length_(length) {}
static bool Match(void* key1, void* key2) {
const IntrinsicFunctionIdentifier* lhs =
static_cast<IntrinsicFunctionIdentifier*>(key1);
const IntrinsicFunctionIdentifier* rhs =
static_cast<IntrinsicFunctionIdentifier*>(key2);
if (lhs->length_ != rhs->length_) return false;
return CompareCharsEqual(lhs->data_, rhs->data_, rhs->length_);
}
uint32_t Hash() {
return StringHasher::HashSequentialString<uint8_t>(data_, length_,
HashSeed::Default());
}
const unsigned char* data_;
const int length_;
};
void InitializeIntrinsicFunctionNames() {
base::CustomMatcherHashMap* function_name_map =
new base::CustomMatcherHashMap(IntrinsicFunctionIdentifier::Match);
for (size_t i = 0; i < arraysize(kIntrinsicFunctions); ++i) {
const Runtime::Function* function = &kIntrinsicFunctions[i];
IntrinsicFunctionIdentifier* identifier = new IntrinsicFunctionIdentifier(
reinterpret_cast<const unsigned char*>(function->name),
static_cast<int>(strlen(function->name)));
base::HashMap::Entry* entry =
function_name_map->InsertNew(identifier, identifier->Hash());
entry->value = const_cast<Runtime::Function*>(function);
}
kRuntimeFunctionNameMap = function_name_map;
}
} // namespace
bool Runtime::NeedsExactContext(FunctionId id) {
switch (id) {
case Runtime::kInlineAsyncFunctionReject:
case Runtime::kInlineAsyncFunctionResolve:
// For %_AsyncFunctionReject and %_AsyncFunctionResolve we don't
// really need the current context, which in particular allows
// us to usually eliminate the catch context for the implicit
// try-catch in async function.
return false;
case Runtime::kCreatePrivateAccessors:
case Runtime::kCopyDataProperties:
case Runtime::kCreateDataProperty:
case Runtime::kCreatePrivateNameSymbol:
case Runtime::kCreatePrivateBrandSymbol:
case Runtime::kLoadPrivateGetter:
case Runtime::kLoadPrivateSetter:
case Runtime::kReThrow:
case Runtime::kReThrowWithMessage:
case Runtime::kThrow:
case Runtime::kThrowTargetNonFunction:
case Runtime::kThrowCalledNonCallable:
case Runtime::kThrowConstAssignError:
case Runtime::kThrowConstructorNonCallableError:
case Runtime::kThrowConstructedNonConstructable:
case Runtime::kThrowConstructorReturnedNonObject:
case Runtime::kThrowInvalidStringLength:
case Runtime::kThrowInvalidTypedArrayAlignment:
case Runtime::kThrowIteratorError:
case Runtime::kThrowIteratorResultNotAnObject:
case Runtime::kThrowNotConstructor:
case Runtime::kThrowRangeError:
case Runtime::kThrowReferenceError:
case Runtime::kThrowAccessedUninitializedVariable:
case Runtime::kThrowStackOverflow:
case Runtime::kThrowStaticPrototypeError:
case Runtime::kThrowSuperAlreadyCalledError:
case Runtime::kThrowSuperNotCalled:
case Runtime::kThrowSymbolAsyncIteratorInvalid:
case Runtime::kThrowSymbolIteratorInvalid:
case Runtime::kThrowThrowMethodMissing:
case Runtime::kThrowTypeError:
case Runtime::kThrowUnsupportedSuperError:
case Runtime::kTerminateExecution:
#if V8_ENABLE_WEBASSEMBLY
case Runtime::kThrowWasmError:
case Runtime::kThrowWasmStackOverflow:
#endif // V8_ENABLE_WEBASSEMBLY
return false;
default:
return true;
}
}
bool Runtime::IsNonReturning(FunctionId id) {
switch (id) {
case Runtime::kThrowUnsupportedSuperError:
case Runtime::kThrowConstructorNonCallableError:
case Runtime::kThrowStaticPrototypeError:
case Runtime::kThrowSuperAlreadyCalledError:
case Runtime::kThrowSuperNotCalled:
case Runtime::kReThrow:
case Runtime::kReThrowWithMessage:
case Runtime::kThrow:
case Runtime::kThrowTargetNonFunction:
case Runtime::kThrowCalledNonCallable:
case Runtime::kThrowConstructedNonConstructable:
case Runtime::kThrowConstructorReturnedNonObject:
case Runtime::kThrowInvalidStringLength:
case Runtime::kThrowInvalidTypedArrayAlignment:
case Runtime::kThrowIteratorError:
case Runtime::kThrowIteratorResultNotAnObject:
case Runtime::kThrowThrowMethodMissing:
case Runtime::kThrowSymbolIteratorInvalid:
case Runtime::kThrowNotConstructor:
case Runtime::kThrowRangeError:
case Runtime::kThrowReferenceError:
case Runtime::kThrowAccessedUninitializedVariable:
case Runtime::kThrowStackOverflow:
case Runtime::kThrowSymbolAsyncIteratorInvalid:
case Runtime::kThrowTypeError:
case Runtime::kThrowConstAssignError:
case Runtime::kTerminateExecution:
#if V8_ENABLE_WEBASSEMBLY
case Runtime::kThrowWasmError:
case Runtime::kThrowWasmStackOverflow:
case Runtime::kThrowWasmJSPISuspendError:
case Runtime::kThrowWasmFXSuspendError:
#endif // V8_ENABLE_WEBASSEMBLY
return true;
default:
return false;
}
}
bool Runtime::MayAllocate(FunctionId id) {
switch (id) {
case Runtime::kCompleteInobjectSlackTracking:
case Runtime::kCompleteInobjectSlackTrackingForMap:
case Runtime::kGlobalPrint:
return false;
default:
return true;
}
}
bool Runtime::IsEnabledForFuzzing(FunctionId id) {
CHECK(v8_flags.fuzzing);
// In general, all runtime functions meant for testing should also be exposed
// to the fuzzers. That way, the fuzzers are able to import and mutate
// regression tests that use those functions. Internal runtime functions
// (which are e.g. only called from other builtins, etc.) should not directly
// be exposed as they are not meant to be called directly from JavaScript.
// However, exceptions exist: some test functions cannot be used for certain
// types of fuzzing (e.g. differential fuzzing), or would cause false
// positive crashes and therefore should not be exposed to fuzzers at all.
// For differential fuzzing, only a handful of functions are allowed,
// everything else is disabled. Many runtime functions are unsuited for
// differential fuzzing as they for example expose internal engine state
// (e.g. functions such as %HasFastProperties). To avoid having to maintain a
// large denylist of such functions, we instead use an allowlist for
// differential fuzzing.
bool is_differential_fuzzing =
v8_flags.allow_natives_for_differential_fuzzing;
if (is_differential_fuzzing) {
switch (id) {
case Runtime::kArrayBufferDetach:
case Runtime::kDeoptimizeFunction:
case Runtime::kDeoptimizeNow:
case Runtime::kDisableOptimizationFinalization:
case Runtime::kEnableCodeLoggingForTesting:
case Runtime::kFinalizeOptimization:
case Runtime::kGetUndetectable:
case Runtime::kNeverOptimizeFunction:
case Runtime::kOptimizeFunctionOnNextCall:
case Runtime::kOptimizeMaglevOnNextCall:
case Runtime::kOptimizeOsr:
case Runtime::kPrepareFunctionForOptimization:
case Runtime::kPretenureAllocationSite:
case Runtime::kSetAllocationTimeout:
case Runtime::kSetForceSlowPath:
case Runtime::kSimulateNewspaceFull:
case Runtime::kWaitForBackgroundOptimization:
case Runtime::kSetBatterySaverMode:
case Runtime::kSetPriorityBestEffort:
case Runtime::kSetPriorityUserVisible:
case Runtime::kSetPriorityUserBlocking:
case Runtime::kIsEfficiencyModeEnabled:
case Runtime::kBaselineOsr:
case Runtime::kCompileBaseline:
#if V8_ENABLE_WEBASSEMBLY && V8_WASM_RANDOM_FUZZERS
case Runtime::kWasmGenerateRandomModule:
#endif // V8_ENABLE_WEBASSEMBLY && V8_WASM_RANDOM_FUZZERS
#if V8_ENABLE_WEBASSEMBLY
case Runtime::kWasmArray:
case Runtime::kWasmStruct:
case Runtime::kWasmTierUpFunction:
case Runtime::kWasmTriggerTierUpForTesting:
#endif // V8_ENABLE_WEBASSEMBLY
return true;
default:
return false;
}
}
// Runtime functions disabled for all/most types of fuzzing.
// These are mainly functions that are not fuzzing-safe and therefore would
// cause false-positive crashes (e.g. %AbortJS).
switch (id) {
case Runtime::kAbort:
case Runtime::kAbortCSADcheck:
case Runtime::kAbortJS:
case Runtime::kSystemBreak:
case Runtime::kBenchMaglev:
case Runtime::kBenchTurbofan:
case Runtime::kDebugPrintPtr:
case Runtime::kDisassembleFunction:
case Runtime::kGetFunctionForCurrentFrame:
case Runtime::kGetCallable:
case Runtime::kGetAbstractModuleSource:
case Runtime::kAssertNotPeeled:
case Runtime::kAssertPeeled:
case Runtime::kTurbofanStaticAssert:
case Runtime::kAssertEscapeAnalysisElided:
case Runtime::kClearFunctionFeedback:
case Runtime::kStringIsFlat:
case Runtime::kGetInitializerFunction:
case Runtime::kArrayBufferDetachForceWasm:
#ifdef V8_ENABLE_WEBASSEMBLY
case Runtime::kWasmTraceEnter:
case Runtime::kWasmTraceExit:
case Runtime::kWasmTraceMemory:
case Runtime::kWasmTraceGlobal:
case Runtime::kCheckIsOnCentralStack:
case Runtime::kSetWasmInstantiateControls:
case Runtime::kFreezeWasmLazyCompilation:
#endif // V8_ENABLE_WEBASSEMBLY
// TODO(353685107): investigate whether these should be exposed to fuzzers.
case Runtime::kConstructDouble:
// TODO(353971258): investigate whether this should be exposed to fuzzers.
case Runtime::kSerializeDeserializeNow:
// TODO(353928347): investigate whether this should be exposed to fuzzers.
case Runtime::kCompleteInobjectSlackTracking:
// TODO(354310130): investigate whether this should be exposed to fuzzers.
case Runtime::kForceFlush:
return false;
case Runtime::kLeakHole:
return v8_flags.hole_fuzzing;
case Runtime::kGetBytecode:
// %GetBytecode is designed for sandbox fuzzing. It is not safe to be
// used during regular fuzzing as it exposes the objects in the
// BytecodeArray's constant pool (which may be internal objects such
// as ScopeInfo) to the caller.
return v8_flags.sandbox_testing || v8_flags.sandbox_fuzzing;
case Runtime::kInstallBytecode:
// %InstallBytecode allows installing manipulated bytecode that has
// only been checked for sandbox safety, not general correctness.
// Therefore, it is restricted to sandbox fuzzing with full bytecode
// verification enabled, preventing trivial sandbox violation alarms.
return (v8_flags.sandbox_testing || v8_flags.sandbox_fuzzing) &&
v8_flags.verify_bytecode_full;
case Runtime::kIsSmi:
return true; // Enabled when not performing differential fuzzing.
default:
break;
}
// The default case: test functions are exposed, everything else is not.
switch (id) {
// Functions used in testing and outside
case Runtime::kArrayBufferDetach:
#define F(name, nargs, ressize, ...) case k##name:
#define I(name, nargs, ressize, ...) case kInline##name:
FOR_EACH_INTRINSIC_TEST(F, I)
IF_WASM(FOR_EACH_INTRINSIC_WASM_TEST, F, I)
#undef I
#undef F
return true;
default:
return false;
}
}
const Runtime::Function* Runtime::FunctionForName(const unsigned char* name,
int length) {
base::CallOnce(&initialize_function_name_map_once,
&InitializeIntrinsicFunctionNames);
IntrinsicFunctionIdentifier identifier(name, length);
base::HashMap::Entry* entry =
kRuntimeFunctionNameMap->Lookup(&identifier, identifier.Hash());
if (entry) {
return reinterpret_cast<Function*>(entry->value);
}
return nullptr;
}
const Runtime::Function* Runtime::FunctionForEntry(Address entry) {
for (size_t i = 0; i < arraysize(kIntrinsicFunctions); ++i) {
if (entry == kIntrinsicFunctions[i].entry) {
return &(kIntrinsicFunctions[i]);
}
}
return nullptr;
}
const Runtime::Function* Runtime::FunctionForId(Runtime::FunctionId id) {
return &(kIntrinsicFunctions[static_cast<int>(id)]);
}
const Runtime::Function* Runtime::RuntimeFunctionTable(Isolate* isolate) {
#ifdef USE_SIMULATOR
// When running with the simulator we need to provide a table which has
// redirected runtime entry addresses.
if (!isolate->runtime_state()->redirected_intrinsic_functions()) {
size_t function_count = arraysize(kIntrinsicFunctions);
Function* redirected_functions = new Function[function_count];
memcpy(redirected_functions, kIntrinsicFunctions,
sizeof(kIntrinsicFunctions));
for (size_t i = 0; i < function_count; i++) {
ExternalReference redirected_entry =
ExternalReference::Create(static_cast<Runtime::FunctionId>(i));
redirected_functions[i].entry = redirected_entry.address();
}
isolate->runtime_state()->set_redirected_intrinsic_functions(
redirected_functions);
}
return isolate->runtime_state()->redirected_intrinsic_functions();
#else
return kIntrinsicFunctions;
#endif
}
std::ostream& operator<<(std::ostream& os, Runtime::FunctionId id) {
return os << Runtime::FunctionForId(id)->name;
}
int g_num_isolates_for_testing = 1;
} // namespace internal
} // namespace v8