Revert of [wasm] Initial signal handler (patchset #56 id:1090001 of https://codereview.chromium.org/2371833007/ )
Reason for revert:
Breaks tree, i.e. https://build.chromium.org/p/client.v8/builders/V8%20Linux64%20ASAN/builds/18928/steps/Check/logs/grow-memory
Original issue's description:
> [wasm] Initial signal handler
>
> This is basically the minimum viable signal handler for Wasm bounds checks.
> It includes the TLS check and the fine grained instructions checks. These
> two checks provide most of the safety for the signal handler. Future CLs will
> add code range and data range checks for more robustness.
>
> The trap handling code and data structures are all in src/trap-handler, with
> the code that actually runs in the signal handler confined to
> src/trap-handler/signal-handler.cc.
>
> This changes adds a new V8 API that the embedder should call from a signal
> handler that will give V8 the chance to handle the fault first. For hosts that
> do not want to implement their own signal handler, we include the option to
> install a simple one. This simple handler is also used for the tests.
>
> When a Wasm module is instantiated, information about each function is passed
> to the trap handler, which is used to classify faults. These are removed during
> the instance finalizer.
>
> Several future enhancements are planned before turning this on by default.
> Obviously, the additional checks will be added to MaybeHandleFault. We are
> also planning to add a two-level CodeObjectData table that is grouped by
> isolates to make cleanup easier and also reduce potential for contending on
> a single data structure.
>
> BUG= https://bugs.chromium.org/p/v8/issues/detail?id=5277
>
> Review-Url: https://codereview.chromium.org/2371833007
> Cr-Commit-Position: refs/heads/master@{#43523}
> Committed: a5af7fe9ee
TBR=ahaas@chromium.org,bradnelson@google.com,hpayer@chromium.org,jochen@chromium.org,mark@chromium.org,mseaborn@chromium.org,titzer@chromium.org,eholk@chromium.org
# Skipping CQ checks because original CL landed less than 1 days ago.
NOPRESUBMIT=true
NOTREECHECKS=true
NOTRY=true
BUG= https://bugs.chromium.org/p/v8/issues/detail?id=5277
Review-Url: https://codereview.chromium.org/2723133003
Cr-Commit-Position: refs/heads/master@{#43525}
This commit is contained in:
parent
19f24d6ef5
commit
0b3e554e03
6
BUILD.gn
6
BUILD.gn
@ -1774,9 +1774,6 @@ v8_source_set("v8_base") {
|
||||
"src/transitions-inl.h",
|
||||
"src/transitions.cc",
|
||||
"src/transitions.h",
|
||||
"src/trap-handler/handler-outside.cc",
|
||||
"src/trap-handler/handler-shared.cc",
|
||||
"src/trap-handler/trap-handler-internal.h",
|
||||
"src/trap-handler/trap-handler.h",
|
||||
"src/type-hints.cc",
|
||||
"src/type-hints.h",
|
||||
@ -1932,9 +1929,6 @@ v8_source_set("v8_base") {
|
||||
"src/x64/simulator-x64.h",
|
||||
"src/x64/sse-instr.h",
|
||||
]
|
||||
if (is_linux) {
|
||||
sources += [ "src/trap-handler/handler-inside.cc" ]
|
||||
}
|
||||
} else if (v8_current_cpu == "arm") {
|
||||
sources += [ ### gcmole(arch:arm) ###
|
||||
"src/arm/assembler-arm-inl.h",
|
||||
|
29
include/v8.h
29
include/v8.h
@ -7693,35 +7693,6 @@ class V8_EXPORT V8 {
|
||||
*/
|
||||
static void ShutdownPlatform();
|
||||
|
||||
#if V8_OS_LINUX && V8_TARGET_ARCH_X64
|
||||
/**
|
||||
* Give the V8 signal handler a chance to handle a fault.
|
||||
*
|
||||
* This function determines whether a memory access violation can be recovered
|
||||
* by V8. If so, it will return true and modify context to return to a code
|
||||
* fragment that can recover from the fault. Otherwise, TryHandleSignal will
|
||||
* return false.
|
||||
*
|
||||
* The parameters to this function correspond to those passed to a Linux
|
||||
* signal handler.
|
||||
*
|
||||
* \param signal_number The signal number.
|
||||
*
|
||||
* \param info A pointer to the siginfo_t structure provided to the signal
|
||||
* handler.
|
||||
*
|
||||
* \param context The third argument passed to the Linux signal handler, which
|
||||
* points to a ucontext_t structure.
|
||||
*/
|
||||
static bool TryHandleSignal(int signal_number, void* info, void* context);
|
||||
#endif // V8_OS_LINUX
|
||||
|
||||
/**
|
||||
* Enable the default signal handler rather than using one provided by the
|
||||
* embedder.
|
||||
*/
|
||||
static bool RegisterDefaultSignalHandler();
|
||||
|
||||
private:
|
||||
V8();
|
||||
|
||||
|
2
src/DEPS
2
src/DEPS
@ -18,8 +18,6 @@ include_rules = [
|
||||
"+src/interpreter/bytecode-register.h",
|
||||
"+src/interpreter/bytecodes.h",
|
||||
"+src/interpreter/interpreter.h",
|
||||
"-src/trap-handler",
|
||||
"+src/trap-handler/trap-handler.h",
|
||||
"+testing/gtest/include/gtest/gtest_prod.h",
|
||||
"-src/libplatform",
|
||||
"-include/libplatform"
|
||||
|
11
src/api.cc
11
src/api.cc
@ -69,7 +69,6 @@
|
||||
#include "src/snapshot/snapshot.h"
|
||||
#include "src/startup-data-util.h"
|
||||
#include "src/tracing/trace-event.h"
|
||||
#include "src/trap-handler/trap-handler.h"
|
||||
#include "src/unicode-inl.h"
|
||||
#include "src/v8.h"
|
||||
#include "src/v8threads.h"
|
||||
@ -6152,16 +6151,6 @@ bool v8::V8::Initialize() {
|
||||
return true;
|
||||
}
|
||||
|
||||
#if V8_OS_LINUX && V8_TARGET_ARCH_X64
|
||||
bool V8::TryHandleSignal(int signum, void* info, void* context) {
|
||||
return v8::internal::trap_handler::TryHandleSignal(
|
||||
signum, static_cast<siginfo_t*>(info), static_cast<ucontext_t*>(context));
|
||||
}
|
||||
#endif // V8_OS_LINUX
|
||||
|
||||
bool V8::RegisterDefaultSignalHandler() {
|
||||
return v8::internal::trap_handler::RegisterDefaultSignalHandler();
|
||||
}
|
||||
|
||||
void v8::V8::SetEntropySource(EntropySource entropy_source) {
|
||||
base::RandomNumberGenerator::SetEntropySource(entropy_source);
|
||||
|
@ -111,6 +111,13 @@ Node* BuildCallToRuntime(Runtime::FunctionId f, JSGraph* jsgraph,
|
||||
|
||||
} // namespace
|
||||
|
||||
// TODO(eholk): Support trap handlers on other platforms.
|
||||
#if V8_TARGET_ARCH_X64 && V8_OS_LINUX
|
||||
const bool kTrapHandlerSupported = true;
|
||||
#else
|
||||
const bool kTrapHandlerSupported = false;
|
||||
#endif
|
||||
|
||||
// A helper that handles building graph fragments for trapping.
|
||||
// To avoid generating a ton of redundant code that just calls the runtime
|
||||
// to trap, we generate a per-trap-reason block of code that all trap sites
|
||||
@ -2800,15 +2807,6 @@ void WasmGraphBuilder::BuildJSToWasmWrapper(Handle<Code> wasm_code,
|
||||
args[pos++] = wasm_param;
|
||||
}
|
||||
|
||||
// Set the ThreadInWasm flag before we do the actual call.
|
||||
if (trap_handler::UseTrapHandler()) {
|
||||
// TODO(eholk): Set the flag directly without a runtime call. We should be
|
||||
// able to store directly to a location in the isolate (later TLS) that sets
|
||||
// the g_thread_in_wasm_code flag.
|
||||
BuildCallToRuntime(Runtime::kSetThreadInWasm, jsgraph(), nullptr, 0,
|
||||
effect_, *control_);
|
||||
}
|
||||
|
||||
args[pos++] = *effect_;
|
||||
args[pos++] = *control_;
|
||||
|
||||
@ -2818,16 +2816,6 @@ void WasmGraphBuilder::BuildJSToWasmWrapper(Handle<Code> wasm_code,
|
||||
|
||||
Node* call = graph()->NewNode(jsgraph()->common()->Call(desc), count, args);
|
||||
*effect_ = call;
|
||||
|
||||
// Clear the ThreadInWasmFlag
|
||||
if (trap_handler::UseTrapHandler()) {
|
||||
// TODO(eholk): Set the flag directly without a runtime call. We should be
|
||||
// able to store directly to a location in the isolate (later TLS) that sets
|
||||
// the g_thread_in_wasm_code flag.
|
||||
BuildCallToRuntime(Runtime::kClearThreadInWasm, jsgraph(), nullptr, 0,
|
||||
effect_, *control_);
|
||||
}
|
||||
|
||||
Node* retval = call;
|
||||
Node* jsval = ToJS(
|
||||
retval, sig->return_count() == 0 ? wasm::kWasmStmt : sig->GetReturn());
|
||||
@ -2874,11 +2862,6 @@ void WasmGraphBuilder::BuildWasmToJSWrapper(Handle<JSReceiver> target,
|
||||
Node* call;
|
||||
bool direct_call = false;
|
||||
|
||||
if (trap_handler::UseTrapHandler()) {
|
||||
BuildCallToRuntime(Runtime::kClearThreadInWasm, jsgraph(), nullptr, 0,
|
||||
effect_, *control_);
|
||||
}
|
||||
|
||||
if (target->IsJSFunction()) {
|
||||
Handle<JSFunction> function = Handle<JSFunction>::cast(target);
|
||||
if (function->shared()->internal_formal_parameter_count() == wasm_count) {
|
||||
@ -2943,11 +2926,6 @@ void WasmGraphBuilder::BuildWasmToJSWrapper(Handle<JSReceiver> target,
|
||||
*effect_ = call;
|
||||
SetSourcePosition(call, 0);
|
||||
|
||||
if (trap_handler::UseTrapHandler()) {
|
||||
BuildCallToRuntime(Runtime::kSetThreadInWasm, jsgraph(), nullptr, 0,
|
||||
effect_, *control_);
|
||||
}
|
||||
|
||||
// Convert the return value back.
|
||||
Node* i32_zero = jsgraph()->Int32Constant(0);
|
||||
Node* val = sig->return_count() == 0
|
||||
@ -3221,7 +3199,7 @@ Node* WasmGraphBuilder::LoadMem(wasm::ValueType type, MachineType memtype,
|
||||
Node* load;
|
||||
|
||||
// WASM semantics throw on OOB. Introduce explicit bounds check.
|
||||
if (!FLAG_wasm_trap_handler || !V8_TRAP_HANDLER_SUPPORTED) {
|
||||
if (!FLAG_wasm_trap_handler || !kTrapHandlerSupported) {
|
||||
BoundsCheckMem(memtype, index, offset, position);
|
||||
}
|
||||
bool aligned = static_cast<int>(alignment) >=
|
||||
@ -3229,7 +3207,7 @@ Node* WasmGraphBuilder::LoadMem(wasm::ValueType type, MachineType memtype,
|
||||
|
||||
if (aligned ||
|
||||
jsgraph()->machine()->UnalignedLoadSupported(memtype, alignment)) {
|
||||
if (FLAG_wasm_trap_handler && V8_TRAP_HANDLER_SUPPORTED) {
|
||||
if (FLAG_wasm_trap_handler && kTrapHandlerSupported) {
|
||||
DCHECK(FLAG_wasm_guard_pages);
|
||||
Node* position_node = jsgraph()->Int32Constant(position);
|
||||
load = graph()->NewNode(jsgraph()->machine()->ProtectedLoad(memtype),
|
||||
@ -3241,7 +3219,7 @@ Node* WasmGraphBuilder::LoadMem(wasm::ValueType type, MachineType memtype,
|
||||
}
|
||||
} else {
|
||||
// TODO(eholk): Support unaligned loads with trap handlers.
|
||||
DCHECK(!FLAG_wasm_trap_handler || !V8_TRAP_HANDLER_SUPPORTED);
|
||||
DCHECK(!FLAG_wasm_trap_handler || !kTrapHandlerSupported);
|
||||
load = graph()->NewNode(jsgraph()->machine()->UnalignedLoad(memtype),
|
||||
MemBuffer(offset), index, *effect_, *control_);
|
||||
}
|
||||
@ -3275,7 +3253,7 @@ Node* WasmGraphBuilder::StoreMem(MachineType memtype, Node* index,
|
||||
Node* store;
|
||||
|
||||
// WASM semantics throw on OOB. Introduce explicit bounds check.
|
||||
if (!FLAG_wasm_trap_handler || !V8_TRAP_HANDLER_SUPPORTED) {
|
||||
if (!FLAG_wasm_trap_handler || !kTrapHandlerSupported) {
|
||||
BoundsCheckMem(memtype, index, offset, position);
|
||||
}
|
||||
StoreRepresentation rep(memtype.representation(), kNoWriteBarrier);
|
||||
@ -3289,7 +3267,7 @@ Node* WasmGraphBuilder::StoreMem(MachineType memtype, Node* index,
|
||||
|
||||
if (aligned ||
|
||||
jsgraph()->machine()->UnalignedStoreSupported(memtype, alignment)) {
|
||||
if (FLAG_wasm_trap_handler && V8_TRAP_HANDLER_SUPPORTED) {
|
||||
if (FLAG_wasm_trap_handler && kTrapHandlerSupported) {
|
||||
Node* position_node = jsgraph()->Int32Constant(position);
|
||||
store = graph()->NewNode(
|
||||
jsgraph()->machine()->ProtectedStore(memtype.representation()),
|
||||
@ -3302,7 +3280,7 @@ Node* WasmGraphBuilder::StoreMem(MachineType memtype, Node* index,
|
||||
}
|
||||
} else {
|
||||
// TODO(eholk): Support unaligned stores with trap handlers.
|
||||
DCHECK(!FLAG_wasm_trap_handler || !V8_TRAP_HANDLER_SUPPORTED);
|
||||
DCHECK(!FLAG_wasm_trap_handler || !kTrapHandlerSupported);
|
||||
UnalignedStoreRepresentation rep(memtype.representation());
|
||||
store =
|
||||
graph()->NewNode(jsgraph()->machine()->UnalignedStore(rep),
|
||||
|
@ -270,12 +270,13 @@ class OutOfLineRecordWrite final : public OutOfLineCode {
|
||||
class WasmOutOfLineTrap final : public OutOfLineCode {
|
||||
public:
|
||||
WasmOutOfLineTrap(CodeGenerator* gen, int pc, bool frame_elided,
|
||||
int32_t position)
|
||||
int32_t position, Instruction* instr)
|
||||
: OutOfLineCode(gen),
|
||||
gen_(gen),
|
||||
pc_(pc),
|
||||
frame_elided_(frame_elided),
|
||||
position_(position) {}
|
||||
position_(position),
|
||||
instr_(instr) {}
|
||||
|
||||
// TODO(eholk): Refactor this method to take the code generator as a
|
||||
// parameter.
|
||||
@ -289,17 +290,14 @@ class WasmOutOfLineTrap final : public OutOfLineCode {
|
||||
wasm::TrapReason trap_id = wasm::kTrapMemOutOfBounds;
|
||||
int trap_reason = wasm::WasmOpcodes::TrapReasonToMessageId(trap_id);
|
||||
__ Push(Smi::FromInt(trap_reason));
|
||||
// TODO(eholk): use AssembleSourcePosition instead of passing in position_
|
||||
// as a parameter. See AssembleArchTrap as an example. Consider sharing code
|
||||
// with AssembleArchTrap.
|
||||
__ Push(Smi::FromInt(position_));
|
||||
__ Move(rsi, Smi::kZero);
|
||||
__ Move(rsi, gen_->isolate()->native_context());
|
||||
__ CallRuntime(Runtime::kThrowWasmError);
|
||||
|
||||
ReferenceMap* reference_map =
|
||||
new (gen_->code()->zone()) ReferenceMap(gen_->code()->zone());
|
||||
gen_->RecordSafepoint(reference_map, Safepoint::kSimple, 0,
|
||||
Safepoint::kNoLazyDeopt);
|
||||
if (instr_->reference_map() != nullptr) {
|
||||
gen_->RecordSafepoint(instr_->reference_map(), Safepoint::kSimple, 0,
|
||||
Safepoint::kNoLazyDeopt);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
@ -307,17 +305,18 @@ class WasmOutOfLineTrap final : public OutOfLineCode {
|
||||
int pc_;
|
||||
bool frame_elided_;
|
||||
int32_t position_;
|
||||
Instruction* instr_;
|
||||
};
|
||||
|
||||
void EmitOOLTrapIfNeeded(Zone* zone, CodeGenerator* codegen,
|
||||
InstructionCode opcode, size_t input_count,
|
||||
X64OperandConverter& i, int pc) {
|
||||
X64OperandConverter& i, int pc, Instruction* instr) {
|
||||
const X64MemoryProtection protection =
|
||||
static_cast<X64MemoryProtection>(MiscField::decode(opcode));
|
||||
if (protection == X64MemoryProtection::kProtected) {
|
||||
const bool frame_elided = !codegen->frame_access_state()->has_frame();
|
||||
const int32_t position = i.InputInt32(input_count - 1);
|
||||
new (zone) WasmOutOfLineTrap(codegen, pc, frame_elided, position);
|
||||
new (zone) WasmOutOfLineTrap(codegen, pc, frame_elided, position, instr);
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
@ -1855,30 +1854,30 @@ CodeGenerator::CodeGenResult CodeGenerator::AssembleArchInstruction(
|
||||
break;
|
||||
case kX64Movsxbl:
|
||||
EmitOOLTrapIfNeeded(zone(), this, opcode, instr->InputCount(), i,
|
||||
__ pc_offset());
|
||||
__ pc_offset(), instr);
|
||||
ASSEMBLE_MOVX(movsxbl);
|
||||
__ AssertZeroExtended(i.OutputRegister());
|
||||
break;
|
||||
case kX64Movzxbl:
|
||||
EmitOOLTrapIfNeeded(zone(), this, opcode, instr->InputCount(), i,
|
||||
__ pc_offset());
|
||||
__ pc_offset(), instr);
|
||||
ASSEMBLE_MOVX(movzxbl);
|
||||
__ AssertZeroExtended(i.OutputRegister());
|
||||
break;
|
||||
case kX64Movsxbq:
|
||||
EmitOOLTrapIfNeeded(zone(), this, opcode, instr->InputCount(), i,
|
||||
__ pc_offset());
|
||||
__ pc_offset(), instr);
|
||||
ASSEMBLE_MOVX(movsxbq);
|
||||
break;
|
||||
case kX64Movzxbq:
|
||||
EmitOOLTrapIfNeeded(zone(), this, opcode, instr->InputCount(), i,
|
||||
__ pc_offset());
|
||||
__ pc_offset(), instr);
|
||||
ASSEMBLE_MOVX(movzxbq);
|
||||
__ AssertZeroExtended(i.OutputRegister());
|
||||
break;
|
||||
case kX64Movb: {
|
||||
EmitOOLTrapIfNeeded(zone(), this, opcode, instr->InputCount(), i,
|
||||
__ pc_offset());
|
||||
__ pc_offset(), instr);
|
||||
size_t index = 0;
|
||||
Operand operand = i.MemoryOperand(&index);
|
||||
if (HasImmediateInput(instr, index)) {
|
||||
@ -1890,30 +1889,30 @@ CodeGenerator::CodeGenResult CodeGenerator::AssembleArchInstruction(
|
||||
}
|
||||
case kX64Movsxwl:
|
||||
EmitOOLTrapIfNeeded(zone(), this, opcode, instr->InputCount(), i,
|
||||
__ pc_offset());
|
||||
__ pc_offset(), instr);
|
||||
ASSEMBLE_MOVX(movsxwl);
|
||||
__ AssertZeroExtended(i.OutputRegister());
|
||||
break;
|
||||
case kX64Movzxwl:
|
||||
EmitOOLTrapIfNeeded(zone(), this, opcode, instr->InputCount(), i,
|
||||
__ pc_offset());
|
||||
__ pc_offset(), instr);
|
||||
ASSEMBLE_MOVX(movzxwl);
|
||||
__ AssertZeroExtended(i.OutputRegister());
|
||||
break;
|
||||
case kX64Movsxwq:
|
||||
EmitOOLTrapIfNeeded(zone(), this, opcode, instr->InputCount(), i,
|
||||
__ pc_offset());
|
||||
__ pc_offset(), instr);
|
||||
ASSEMBLE_MOVX(movsxwq);
|
||||
break;
|
||||
case kX64Movzxwq:
|
||||
EmitOOLTrapIfNeeded(zone(), this, opcode, instr->InputCount(), i,
|
||||
__ pc_offset());
|
||||
__ pc_offset(), instr);
|
||||
ASSEMBLE_MOVX(movzxwq);
|
||||
__ AssertZeroExtended(i.OutputRegister());
|
||||
break;
|
||||
case kX64Movw: {
|
||||
EmitOOLTrapIfNeeded(zone(), this, opcode, instr->InputCount(), i,
|
||||
__ pc_offset());
|
||||
__ pc_offset(), instr);
|
||||
size_t index = 0;
|
||||
Operand operand = i.MemoryOperand(&index);
|
||||
if (HasImmediateInput(instr, index)) {
|
||||
@ -1925,7 +1924,7 @@ CodeGenerator::CodeGenResult CodeGenerator::AssembleArchInstruction(
|
||||
}
|
||||
case kX64Movl:
|
||||
EmitOOLTrapIfNeeded(zone(), this, opcode, instr->InputCount(), i,
|
||||
__ pc_offset());
|
||||
__ pc_offset(), instr);
|
||||
if (instr->HasOutput()) {
|
||||
if (instr->addressing_mode() == kMode_None) {
|
||||
if (instr->InputAt(0)->IsRegister()) {
|
||||
@ -1949,12 +1948,12 @@ CodeGenerator::CodeGenResult CodeGenerator::AssembleArchInstruction(
|
||||
break;
|
||||
case kX64Movsxlq:
|
||||
EmitOOLTrapIfNeeded(zone(), this, opcode, instr->InputCount(), i,
|
||||
__ pc_offset());
|
||||
__ pc_offset(), instr);
|
||||
ASSEMBLE_MOVX(movsxlq);
|
||||
break;
|
||||
case kX64Movq:
|
||||
EmitOOLTrapIfNeeded(zone(), this, opcode, instr->InputCount(), i,
|
||||
__ pc_offset());
|
||||
__ pc_offset(), instr);
|
||||
if (instr->HasOutput()) {
|
||||
__ movq(i.OutputRegister(), i.MemoryOperand());
|
||||
} else {
|
||||
@ -1969,7 +1968,7 @@ CodeGenerator::CodeGenResult CodeGenerator::AssembleArchInstruction(
|
||||
break;
|
||||
case kX64Movss:
|
||||
EmitOOLTrapIfNeeded(zone(), this, opcode, instr->InputCount(), i,
|
||||
__ pc_offset());
|
||||
__ pc_offset(), instr);
|
||||
if (instr->HasOutput()) {
|
||||
__ movss(i.OutputDoubleRegister(), i.MemoryOperand());
|
||||
} else {
|
||||
@ -1980,7 +1979,7 @@ CodeGenerator::CodeGenResult CodeGenerator::AssembleArchInstruction(
|
||||
break;
|
||||
case kX64Movsd:
|
||||
EmitOOLTrapIfNeeded(zone(), this, opcode, instr->InputCount(), i,
|
||||
__ pc_offset());
|
||||
__ pc_offset(), instr);
|
||||
if (instr->HasOutput()) {
|
||||
__ Movsd(i.OutputDoubleRegister(), i.MemoryOperand());
|
||||
} else {
|
||||
|
@ -36,7 +36,6 @@
|
||||
#include "src/msan.h"
|
||||
#include "src/objects-inl.h"
|
||||
#include "src/snapshot/natives.h"
|
||||
#include "src/trap-handler/trap-handler.h"
|
||||
#include "src/utils.h"
|
||||
#include "src/v8.h"
|
||||
|
||||
@ -2944,13 +2943,6 @@ int Shell::Main(int argc, char* argv[]) {
|
||||
create_params.add_histogram_sample_callback = AddHistogramSample;
|
||||
}
|
||||
|
||||
if (i::trap_handler::UseTrapHandler()) {
|
||||
if (!v8::V8::RegisterDefaultSignalHandler()) {
|
||||
fprintf(stderr, "Could not register signal handler");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
Isolate* isolate = Isolate::New(create_params);
|
||||
{
|
||||
Isolate::Scope scope(isolate);
|
||||
|
@ -1690,7 +1690,6 @@ Handle<Code> Factory::NewCode(const CodeDesc& desc,
|
||||
code->set_prologue_offset(prologue_offset);
|
||||
code->set_constant_pool_offset(desc.instr_size - desc.constant_pool_size);
|
||||
code->set_builtin_index(-1);
|
||||
code->set_trap_handler_index(Smi::FromInt(-1));
|
||||
|
||||
if (code->kind() == Code::OPTIMIZED_FUNCTION) {
|
||||
code->set_marked_for_deoptimization(false);
|
||||
|
@ -1202,10 +1202,6 @@ Object* Isolate::UnwindAndFindHandler() {
|
||||
for (StackFrameIterator iter(this); !iter.done(); iter.Advance()) {
|
||||
StackFrame* frame = iter.frame();
|
||||
|
||||
if (frame->is_wasm() && trap_handler::IsThreadInWasm()) {
|
||||
trap_handler::ClearThreadInWasm();
|
||||
}
|
||||
|
||||
// For JSEntryStub frames we always have a handler.
|
||||
if (frame->is_entry() || frame->is_entry_construct()) {
|
||||
StackHandler* handler = frame->top_handler();
|
||||
@ -1237,11 +1233,6 @@ Object* Isolate::UnwindAndFindHandler() {
|
||||
|
||||
handler_sp = return_sp;
|
||||
handler_fp = frame->fp();
|
||||
|
||||
// This is going to be handled by Wasm, so we need to set the TLS flag
|
||||
// again.
|
||||
trap_handler::SetThreadInWasm();
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -6652,7 +6652,6 @@ CODE_ACCESSORS(relocation_info, ByteArray, kRelocationInfoOffset)
|
||||
CODE_ACCESSORS(handler_table, FixedArray, kHandlerTableOffset)
|
||||
CODE_ACCESSORS(deoptimization_data, FixedArray, kDeoptimizationDataOffset)
|
||||
CODE_ACCESSORS(source_position_table, ByteArray, kSourcePositionTableOffset)
|
||||
CODE_ACCESSORS(trap_handler_index, Smi, kTrapHandlerIndex)
|
||||
CODE_ACCESSORS(raw_type_feedback_info, Object, kTypeFeedbackInfoOffset)
|
||||
CODE_ACCESSORS(next_code_link, Object, kNextCodeLinkOffset)
|
||||
#undef CODE_ACCESSORS
|
||||
|
@ -13845,12 +13845,6 @@ void Code::InvalidateEmbeddedObjects() {
|
||||
|
||||
|
||||
void Code::Relocate(intptr_t delta) {
|
||||
if (trap_handler::UseTrapHandler() && is_wasm_code()) {
|
||||
const int index = trap_handler_index()->value();
|
||||
if (index >= 0) {
|
||||
trap_handler::UpdateHandlerDataCodePointer(index, instruction_start());
|
||||
}
|
||||
}
|
||||
for (RelocIterator it(this, RelocInfo::kApplyMask); !it.done(); it.next()) {
|
||||
it.rinfo()->apply(delta);
|
||||
}
|
||||
|
@ -4926,10 +4926,6 @@ class Code: public HeapObject {
|
||||
// [source_position_table]: ByteArray for the source positions table.
|
||||
DECL_ACCESSORS(source_position_table, ByteArray)
|
||||
|
||||
// [trap_handler_index]: An index into the trap handler's master list of code
|
||||
// objects.
|
||||
DECL_ACCESSORS(trap_handler_index, Smi)
|
||||
|
||||
// [raw_type_feedback_info]: This field stores various things, depending on
|
||||
// the kind of the code object.
|
||||
// FUNCTION => type feedback information.
|
||||
@ -5318,8 +5314,7 @@ class Code: public HeapObject {
|
||||
static const int kConstantPoolOffset = kPrologueOffset + kIntSize;
|
||||
static const int kBuiltinIndexOffset =
|
||||
kConstantPoolOffset + kConstantPoolSize;
|
||||
static const int kTrapHandlerIndex = kBuiltinIndexOffset + kIntSize;
|
||||
static const int kHeaderPaddingStart = kTrapHandlerIndex + kPointerSize;
|
||||
static const int kHeaderPaddingStart = kBuiltinIndexOffset + kIntSize;
|
||||
|
||||
enum TrapFields { kTrapCodeOffset, kTrapLandingOffset, kTrapDataSize };
|
||||
|
||||
|
@ -12,7 +12,6 @@
|
||||
#include "src/factory.h"
|
||||
#include "src/frames-inl.h"
|
||||
#include "src/objects-inl.h"
|
||||
#include "src/trap-handler/trap-handler.h"
|
||||
#include "src/v8memory.h"
|
||||
#include "src/wasm/wasm-module.h"
|
||||
#include "src/wasm/wasm-objects.h"
|
||||
@ -169,16 +168,6 @@ RUNTIME_FUNCTION(Runtime_WasmGetCaughtExceptionValue) {
|
||||
return exception;
|
||||
}
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_SetThreadInWasm) {
|
||||
trap_handler::SetThreadInWasm();
|
||||
return isolate->heap()->undefined_value();
|
||||
}
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_ClearThreadInWasm) {
|
||||
trap_handler::ClearThreadInWasm();
|
||||
return isolate->heap()->undefined_value();
|
||||
}
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_WasmRunInterpreter) {
|
||||
DCHECK_EQ(3, args.length());
|
||||
HandleScope scope(isolate);
|
||||
|
@ -639,9 +639,7 @@ namespace internal {
|
||||
F(WasmThrow, 2, 1) \
|
||||
F(WasmGetCaughtExceptionValue, 1, 1) \
|
||||
F(WasmRunInterpreter, 3, 1) \
|
||||
F(WasmStackGuard, 0, 1) \
|
||||
F(SetThreadInWasm, 0, 1) \
|
||||
F(ClearThreadInWasm, 0, 1)
|
||||
F(WasmStackGuard, 0, 1)
|
||||
|
||||
#define FOR_EACH_INTRINSIC_RETURN_PAIR(F) \
|
||||
F(LoadLookupSlotForCall, 1, 2)
|
||||
|
@ -1,17 +0,0 @@
|
||||
# In order to make it easier to audit the signal handler code, we use very
|
||||
# restrictive include rules to limit the amount of code that the signal handler
|
||||
# can depend on.
|
||||
|
||||
include_rules = [
|
||||
"-src",
|
||||
"-include",
|
||||
"+src/trap-handler",
|
||||
]
|
||||
|
||||
specific_include_rules = {
|
||||
"trap-handler.h": [
|
||||
"+src/base/build_config.h",
|
||||
"+src/globals.h",
|
||||
"+src/flags.h",
|
||||
]
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
set noparent
|
||||
|
||||
jochen@chromium.org
|
||||
bradnelson@chromium.org
|
||||
|
||||
# Changes to this directory should also be reviewed by:
|
||||
#
|
||||
# eholk@chromium.org
|
||||
# mseaborn@chromium.org
|
||||
# mark@chromium.org
|
@ -1,170 +0,0 @@
|
||||
// Copyright 2017 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.
|
||||
|
||||
// PLEASE READ BEFORE CHANGING THIS FILE!
|
||||
//
|
||||
// This file implements the out of bounds signal handler for
|
||||
// WebAssembly. Signal handlers are notoriously difficult to get
|
||||
// right, and getting it wrong can lead to security
|
||||
// vulnerabilities. In order to minimize this risk, here are some
|
||||
// rules to follow.
|
||||
//
|
||||
// 1. Do not introduce any new external dependencies. This file needs
|
||||
// to be self contained so it is easy to audit everything that a
|
||||
// signal handler might do.
|
||||
//
|
||||
// 2. Any changes must be reviewed by someone from the crash reporting
|
||||
// or security team. See OWNERS for suggested reviewers.
|
||||
//
|
||||
// For more information, see https://goo.gl/yMeyUY.
|
||||
//
|
||||
// This file contains most of the code that actually runs in a signal handler
|
||||
// context. Some additional code is used both inside and outside the signal
|
||||
// handler. This code can be found in handler-shared.cc.
|
||||
|
||||
#include <signal.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "src/trap-handler/trap-handler-internal.h"
|
||||
#include "src/trap-handler/trap-handler.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
namespace trap_handler {
|
||||
|
||||
namespace {
|
||||
|
||||
bool IsKernelGeneratedSignal(siginfo_t* info) {
|
||||
return info->si_code > 0 && info->si_code != SI_USER &&
|
||||
info->si_code != SI_QUEUE && info->si_code != SI_TIMER &&
|
||||
info->si_code != SI_ASYNCIO && info->si_code != SI_MESGQ;
|
||||
}
|
||||
|
||||
#if V8_TRAP_HANDLER_SUPPORTED
|
||||
class SigUnmaskStack {
|
||||
public:
|
||||
explicit SigUnmaskStack(sigset_t sigs) {
|
||||
// TODO(eholk): consider using linux-syscall-support for calling this
|
||||
// syscall.
|
||||
pthread_sigmask(SIG_UNBLOCK, &sigs, &old_mask_);
|
||||
}
|
||||
|
||||
~SigUnmaskStack() { pthread_sigmask(SIG_SETMASK, &old_mask_, nullptr); }
|
||||
|
||||
private:
|
||||
sigset_t old_mask_;
|
||||
|
||||
// We'd normally use DISALLOW_COPY_AND_ASSIGN, but we're avoiding a dependency
|
||||
// on base/macros.h
|
||||
SigUnmaskStack(const SigUnmaskStack&) = delete;
|
||||
void operator=(const SigUnmaskStack&) = delete;
|
||||
};
|
||||
#endif
|
||||
} // namespace
|
||||
|
||||
#if V8_TRAP_HANDLER_SUPPORTED && V8_OS_LINUX
|
||||
bool TryHandleSignal(int signum, siginfo_t* info, ucontext_t* context) {
|
||||
// Bail out early in case we got called for the wrong kind of signal.
|
||||
if (signum != SIGSEGV) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Make sure the signal was generated by the kernel and not some other source.
|
||||
if (!IsKernelGeneratedSignal(info)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Ensure the faulting thread was actually running Wasm code.
|
||||
if (!IsThreadInWasm()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Clear g_thread_in_wasm_code, primarily to protect against nested faults.
|
||||
g_thread_in_wasm_code = false;
|
||||
|
||||
// Begin signal mask scope. We need to be sure to restore the signal mask
|
||||
// before we restore the g_thread_in_wasm_code flag.
|
||||
{
|
||||
// Unmask the signal so that if this signal handler crashes, the crash will
|
||||
// be handled by the crash reporter. Otherwise, the process might be killed
|
||||
// with the crash going unreported.
|
||||
sigset_t sigs;
|
||||
// Fortunately, sigemptyset and sigaddset are async-signal-safe according to
|
||||
// the POSIX standard.
|
||||
sigemptyset(&sigs);
|
||||
sigaddset(&sigs, SIGSEGV);
|
||||
SigUnmaskStack unmask(sigs);
|
||||
|
||||
uintptr_t fault_addr = context->uc_mcontext.gregs[REG_RIP];
|
||||
|
||||
// TODO(eholk): broad code range check
|
||||
|
||||
// Taking locks in a signal handler is risky because a fault in the signal
|
||||
// handler could lead to a deadlock when attempting to acquire the lock
|
||||
// again. We guard against this case with g_thread_in_wasm_code. The lock
|
||||
// may only be taken when not executing Wasm code (an assert in
|
||||
// MetadataLock's constructor ensures this). This signal handler will bail
|
||||
// out before trying to take the lock if g_thread_in_wasm_code is not set.
|
||||
MetadataLock lock_holder;
|
||||
|
||||
for (size_t i = 0; i < gNumCodeObjects; ++i) {
|
||||
const CodeProtectionInfo* data = gCodeObjects[i].code_info;
|
||||
if (data == nullptr) {
|
||||
continue;
|
||||
}
|
||||
const uintptr_t base = reinterpret_cast<uintptr_t>(data->base);
|
||||
|
||||
if (fault_addr >= base && fault_addr < base + data->size) {
|
||||
// Hurray, we found the code object. Check for protected addresses.
|
||||
const ptrdiff_t offset = fault_addr - base;
|
||||
|
||||
for (unsigned i = 0; i < data->num_protected_instructions; ++i) {
|
||||
if (data->instructions[i].instr_offset == offset) {
|
||||
// Hurray again, we found the actual instruction. Tell the caller to
|
||||
// return to the landing pad.
|
||||
context->uc_mcontext.gregs[REG_RIP] =
|
||||
data->instructions[i].landing_offset + base;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} // end signal mask scope
|
||||
|
||||
// If we get here, it's not a recoverable wasm fault, so we go to the next
|
||||
// handler.
|
||||
g_thread_in_wasm_code = true;
|
||||
return false;
|
||||
}
|
||||
#endif // V8_TRAP_HANDLER_SUPPORTED && V8_OS_LINUX
|
||||
|
||||
#if V8_TRAP_HANDLER_SUPPORTED
|
||||
void HandleSignal(int signum, siginfo_t* info, void* context) {
|
||||
ucontext_t* uc = reinterpret_cast<ucontext_t*>(context);
|
||||
|
||||
if (!TryHandleSignal(signum, info, uc)) {
|
||||
// Since V8 didn't handle this signal, we want to re-raise the same signal.
|
||||
// For kernel-generated SEGV signals, we do this by restoring the default
|
||||
// SEGV handler and then returning. The fault will happen again and the
|
||||
// usual SEGV handling will happen.
|
||||
//
|
||||
// We handle user-generated signals by calling raise() instead. This is for
|
||||
// completeness. We should never actually see one of these, but just in
|
||||
// case, we do the right thing.
|
||||
struct sigaction action;
|
||||
action.sa_handler = SIG_DFL;
|
||||
sigemptyset(&action.sa_mask);
|
||||
action.sa_flags = 0;
|
||||
sigaction(signum, &action, nullptr);
|
||||
if (!IsKernelGeneratedSignal(info)) {
|
||||
raise(signum);
|
||||
}
|
||||
}
|
||||
// TryHandleSignal modifies context to change where we return to.
|
||||
}
|
||||
#endif
|
||||
} // namespace trap_handler
|
||||
} // namespace internal
|
||||
} // namespace v8
|
@ -1,191 +0,0 @@
|
||||
// Copyright 2017 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.
|
||||
|
||||
// PLEASE READ BEFORE CHANGING THIS FILE!
|
||||
//
|
||||
// This file implements the support code for the out of bounds signal handler.
|
||||
// Nothing in here actually runs in the signal handler, but the code here
|
||||
// manipulates data structures used by the signal handler so we still need to be
|
||||
// careful. In order to minimize this risk, here are some rules to follow.
|
||||
//
|
||||
// 1. Avoid introducing new external dependencies. The files in src/trap-handler
|
||||
// should be as self-contained as possible to make it easy to audit the code.
|
||||
//
|
||||
// 2. Any changes must be reviewed by someone from the crash reporting
|
||||
// or security team. Se OWNERS for suggested reviewers.
|
||||
//
|
||||
// For more information, see https://goo.gl/yMeyUY.
|
||||
//
|
||||
// For the code that runs in the signal handler itself, see handler-inside.cc.
|
||||
|
||||
#include <signal.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <limits>
|
||||
|
||||
#include "src/trap-handler/trap-handler-internal.h"
|
||||
#include "src/trap-handler/trap-handler.h"
|
||||
|
||||
namespace {
|
||||
size_t gNextCodeObject = 0;
|
||||
}
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
namespace trap_handler {
|
||||
|
||||
const size_t kInitialCodeObjectSize = 1024;
|
||||
const size_t kCodeObjectGrowthFactor = 2;
|
||||
|
||||
constexpr size_t HandlerDataSize(size_t num_protected_instructions) {
|
||||
return offsetof(CodeProtectionInfo, instructions) +
|
||||
num_protected_instructions * sizeof(ProtectedInstructionData);
|
||||
}
|
||||
|
||||
CodeProtectionInfo* CreateHandlerData(
|
||||
void* base, size_t size, size_t num_protected_instructions,
|
||||
ProtectedInstructionData* protected_instructions) {
|
||||
const size_t alloc_size = HandlerDataSize(num_protected_instructions);
|
||||
CodeProtectionInfo* data =
|
||||
reinterpret_cast<CodeProtectionInfo*>(malloc(alloc_size));
|
||||
|
||||
if (data == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
data->base = base;
|
||||
data->size = size;
|
||||
data->num_protected_instructions = num_protected_instructions;
|
||||
|
||||
memcpy(data->instructions, protected_instructions,
|
||||
num_protected_instructions * sizeof(ProtectedInstructionData));
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
void UpdateHandlerDataCodePointer(int index, void* base) {
|
||||
MetadataLock lock;
|
||||
if (static_cast<size_t>(index) >= gNumCodeObjects) {
|
||||
abort();
|
||||
}
|
||||
CodeProtectionInfo* data = gCodeObjects[index].code_info;
|
||||
data->base = base;
|
||||
}
|
||||
|
||||
int RegisterHandlerData(void* base, size_t size,
|
||||
size_t num_protected_instructions,
|
||||
ProtectedInstructionData* protected_instructions) {
|
||||
// TODO(eholk): in debug builds, make sure this data isn't already registered.
|
||||
|
||||
CodeProtectionInfo* data = CreateHandlerData(
|
||||
base, size, num_protected_instructions, protected_instructions);
|
||||
|
||||
if (data == nullptr) {
|
||||
abort();
|
||||
}
|
||||
|
||||
MetadataLock lock;
|
||||
|
||||
size_t i = gNextCodeObject;
|
||||
|
||||
// Explicitly convert std::numeric_limits<int>::max() to unsigned to avoid
|
||||
// compiler warnings about signed/unsigned comparisons. We aren't worried
|
||||
// about sign extension because we know std::numeric_limits<int>::max() is
|
||||
// positive.
|
||||
const size_t int_max = std::numeric_limits<int>::max();
|
||||
|
||||
// We didn't find an opening in the available space, so grow.
|
||||
if (i == gNumCodeObjects) {
|
||||
size_t new_size = gNumCodeObjects > 0
|
||||
? gNumCodeObjects * kCodeObjectGrowthFactor
|
||||
: kInitialCodeObjectSize;
|
||||
|
||||
// Because we must return an int, there is no point in allocating space for
|
||||
// more objects than can fit in an int.
|
||||
if (new_size > int_max) {
|
||||
new_size = int_max;
|
||||
}
|
||||
if (new_size == gNumCodeObjects) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Now that we know our new size is valid, we can go ahead and realloc the
|
||||
// array.
|
||||
gCodeObjects = static_cast<CodeProtectionInfoListEntry*>(
|
||||
realloc(gCodeObjects, sizeof(*gCodeObjects) * new_size));
|
||||
|
||||
if (gCodeObjects == nullptr) {
|
||||
abort();
|
||||
}
|
||||
|
||||
memset(gCodeObjects + gNumCodeObjects, 0,
|
||||
sizeof(*gCodeObjects) * (new_size - gNumCodeObjects));
|
||||
gNumCodeObjects = new_size;
|
||||
}
|
||||
|
||||
DCHECK(gCodeObjects[i].code_info == nullptr);
|
||||
|
||||
// Find out where the next entry should go.
|
||||
if (gCodeObjects[i].next_free == 0) {
|
||||
// if this is a fresh entry, use the next one.
|
||||
gNextCodeObject = i + 1;
|
||||
DCHECK(gNextCodeObject == gNumCodeObjects ||
|
||||
(gCodeObjects[gNextCodeObject].code_info == nullptr &&
|
||||
gCodeObjects[gNextCodeObject].next_free == 0));
|
||||
} else {
|
||||
gNextCodeObject = gCodeObjects[i].next_free - 1;
|
||||
}
|
||||
|
||||
if (i <= int_max) {
|
||||
gCodeObjects[i].code_info = data;
|
||||
return static_cast<int>(i);
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
void ReleaseHandlerData(int index) {
|
||||
// Remove the data from the global list if it's there.
|
||||
CodeProtectionInfo* data = nullptr;
|
||||
{
|
||||
MetadataLock lock;
|
||||
|
||||
data = gCodeObjects[index].code_info;
|
||||
gCodeObjects[index].code_info = nullptr;
|
||||
|
||||
// +1 because we reserve {next_entry == 0} to indicate a fresh list entry.
|
||||
gCodeObjects[index].next_free = gNextCodeObject + 1;
|
||||
gNextCodeObject = index;
|
||||
}
|
||||
// TODO(eholk): on debug builds, ensure there are no more copies in
|
||||
// the list.
|
||||
free(data);
|
||||
}
|
||||
|
||||
bool RegisterDefaultSignalHandler() {
|
||||
#if V8_TRAP_HANDLER_SUPPORTED
|
||||
struct sigaction action;
|
||||
action.sa_sigaction = HandleSignal;
|
||||
action.sa_flags = SA_SIGINFO;
|
||||
sigemptyset(&action.sa_mask);
|
||||
// {sigaction} installs a new custom segfault handler. On success, it returns
|
||||
// 0. If we get a nonzero value, we report an error to the caller by returning
|
||||
// false.
|
||||
if (sigaction(SIGSEGV, &action, nullptr) != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace trap_handler
|
||||
} // namespace internal
|
||||
} // namespace v8
|
@ -1,52 +0,0 @@
|
||||
// Copyright 2017 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.
|
||||
|
||||
// PLEASE READ BEFORE CHANGING THIS FILE!
|
||||
//
|
||||
// This file contains code that is used both inside and outside the out of
|
||||
// bounds signal handler. Because this code runs in a signal handler context,
|
||||
// use extra care when modifying this file. Here are some rules to follow.
|
||||
//
|
||||
// 1. Do not introduce any new external dependencies. This file needs
|
||||
// to be self contained so it is easy to audit everything that a
|
||||
// signal handler might do.
|
||||
//
|
||||
// 2. Any changes must be reviewed by someone from the crash reporting
|
||||
// or security team. See OWNERS for suggested reviewers.
|
||||
//
|
||||
// For more information, see https://goo.gl/yMeyUY.
|
||||
|
||||
#include "src/trap-handler/trap-handler-internal.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
namespace trap_handler {
|
||||
|
||||
THREAD_LOCAL bool g_thread_in_wasm_code = false;
|
||||
|
||||
size_t gNumCodeObjects = 0;
|
||||
CodeProtectionInfoListEntry* gCodeObjects = nullptr;
|
||||
|
||||
std::atomic_flag MetadataLock::spinlock_ = ATOMIC_FLAG_INIT;
|
||||
|
||||
MetadataLock::MetadataLock() {
|
||||
if (g_thread_in_wasm_code) {
|
||||
abort();
|
||||
}
|
||||
|
||||
while (spinlock_.test_and_set(std::memory_order::memory_order_acquire)) {
|
||||
}
|
||||
}
|
||||
|
||||
MetadataLock::~MetadataLock() {
|
||||
if (g_thread_in_wasm_code) {
|
||||
abort();
|
||||
}
|
||||
|
||||
spinlock_.clear(std::memory_order::memory_order_release);
|
||||
}
|
||||
|
||||
} // namespace trap_handler
|
||||
} // namespace internal
|
||||
} // namespace v8
|
@ -1,67 +0,0 @@
|
||||
// Copyright 2016 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 TRAP_HANDLER_INTERNAL_H_
|
||||
#define TRAP_HANDLER_INTERNAL_H_
|
||||
|
||||
// This file should not be included (even transitively) by files outside of
|
||||
// src/trap-handler.
|
||||
|
||||
#include "src/trap-handler/trap-handler.h"
|
||||
|
||||
#include <atomic>
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
namespace trap_handler {
|
||||
|
||||
// This describes a chunk of code that the signal handler will be able to handle
|
||||
// faults in. {base} points to the beginning of the chunk, and {size} is the
|
||||
// number of bytes in the code chunk. The remainder of the struct is a list of
|
||||
// protected memory access instructions and an offset to a landing pad to handle
|
||||
// faults on that instruction.
|
||||
struct CodeProtectionInfo {
|
||||
void* base;
|
||||
size_t size;
|
||||
size_t num_protected_instructions;
|
||||
ProtectedInstructionData instructions[1];
|
||||
};
|
||||
|
||||
class MetadataLock {
|
||||
static std::atomic_flag spinlock_;
|
||||
|
||||
public:
|
||||
MetadataLock();
|
||||
~MetadataLock();
|
||||
|
||||
// We'd normally use DISALLOW_COPY_AND_ASSIGN, but we're avoiding a dependency
|
||||
// on base/macros.h
|
||||
MetadataLock(const MetadataLock&) = delete;
|
||||
void operator=(const MetadataLock&) = delete;
|
||||
};
|
||||
|
||||
#if V8_TRAP_HANDLER_SUPPORTED
|
||||
void HandleSignal(int signum, siginfo_t* info, void* context);
|
||||
#endif
|
||||
|
||||
// To enable constant time registration of handler data, we keep a free list of
|
||||
// entries in the gCodeObjects table. Each entry contains a {next_free} field,
|
||||
// which can be used to figure out where the next entry should be inserted.
|
||||
// In order to avoid having to initialize all the links to start with, we use
|
||||
// 0 to indicate that this is a fresh, never-used list entry and that therefore
|
||||
// the next entry is known to be free. If {next_entry} is greater than zero,
|
||||
// then {next_entry - 1} is the index that we should insert into next.
|
||||
struct CodeProtectionInfoListEntry {
|
||||
CodeProtectionInfo* code_info;
|
||||
size_t next_free;
|
||||
};
|
||||
|
||||
extern size_t gNumCodeObjects;
|
||||
extern CodeProtectionInfoListEntry* gCodeObjects;
|
||||
|
||||
} // namespace trap_handler
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
||||
#endif // TRAP_HANDLER_INTERNAL_H_
|
@ -5,29 +5,10 @@
|
||||
#ifndef V8_TRAP_HANDLER_H_
|
||||
#define V8_TRAP_HANDLER_H_
|
||||
|
||||
#include <signal.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "src/base/build_config.h"
|
||||
#include "src/flags.h"
|
||||
#include "src/globals.h"
|
||||
|
||||
#if V8_OS_LINUX
|
||||
#include <ucontext.h>
|
||||
#endif
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
namespace trap_handler {
|
||||
|
||||
// TODO(eholk): Support trap handlers on other platforms.
|
||||
#if V8_TARGET_ARCH_X64 && V8_OS_LINUX
|
||||
#define V8_TRAP_HANDLER_SUPPORTED 1
|
||||
#else
|
||||
#define V8_TRAP_HANDLER_SUPPORTED 0
|
||||
#endif
|
||||
|
||||
struct ProtectedInstructionData {
|
||||
// The offset of this instruction from the start of its code object.
|
||||
intptr_t instr_offset;
|
||||
@ -38,56 +19,6 @@ struct ProtectedInstructionData {
|
||||
intptr_t landing_offset;
|
||||
};
|
||||
|
||||
/// Adjusts the base code pointer.
|
||||
void UpdateHandlerDataCodePointer(int index, void* base);
|
||||
|
||||
/// Adds the handler data to the place where the signal handler will find it.
|
||||
///
|
||||
/// This returns a number that can be used to identify the handler data to
|
||||
/// UpdateHandlerDataCodePointer and ReleaseHandlerData, or -1 on failure.
|
||||
int RegisterHandlerData(void* base, size_t size,
|
||||
size_t num_protected_instructions,
|
||||
ProtectedInstructionData* protected_instructions);
|
||||
|
||||
/// Removes the data from the master list and frees any memory, if necessary.
|
||||
void ReleaseHandlerData(int index);
|
||||
|
||||
#if V8_OS_WIN
|
||||
#define THREAD_LOCAL __declspec(thread)
|
||||
#elif V8_OS_ANDROID
|
||||
// TODO(eholk): fix this before enabling for trap handlers for Android.
|
||||
#define THREAD_LOCAL
|
||||
#else
|
||||
#define THREAD_LOCAL __thread
|
||||
#endif
|
||||
|
||||
inline bool UseTrapHandler() {
|
||||
return FLAG_wasm_trap_handler && V8_TRAP_HANDLER_SUPPORTED;
|
||||
}
|
||||
|
||||
extern THREAD_LOCAL bool g_thread_in_wasm_code;
|
||||
|
||||
inline bool IsThreadInWasm() { return g_thread_in_wasm_code; }
|
||||
|
||||
inline void SetThreadInWasm() {
|
||||
if (UseTrapHandler()) {
|
||||
DCHECK(!IsThreadInWasm());
|
||||
g_thread_in_wasm_code = true;
|
||||
}
|
||||
}
|
||||
inline void ClearThreadInWasm() {
|
||||
if (UseTrapHandler()) {
|
||||
DCHECK(IsThreadInWasm());
|
||||
g_thread_in_wasm_code = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool RegisterDefaultSignalHandler();
|
||||
|
||||
#if V8_OS_LINUX
|
||||
bool TryHandleSignal(int signum, siginfo_t* info, ucontext_t* context);
|
||||
#endif // V8_OS_LINUX
|
||||
|
||||
} // namespace trap_handler
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
@ -1276,10 +1276,7 @@
|
||||
'transitions-inl.h',
|
||||
'transitions.cc',
|
||||
'transitions.h',
|
||||
'trap-handler/handler-outside.cc',
|
||||
'trap-handler/handler-shared.cc',
|
||||
'trap-handler/trap-handler.h',
|
||||
'trap-handler/trap-handler-internal.h',
|
||||
'type-hints.cc',
|
||||
'type-hints.h',
|
||||
'type-info.cc',
|
||||
@ -1671,9 +1668,6 @@
|
||||
'third_party/valgrind/valgrind.h',
|
||||
],
|
||||
}],
|
||||
['v8_target_arch=="x64" and OS=="linux"', {
|
||||
'sources': ['trap-handler/handler-inside.cc']
|
||||
}],
|
||||
['v8_target_arch=="ppc" or v8_target_arch=="ppc64"', {
|
||||
'sources': [ ### gcmole(arch:ppc) ###
|
||||
'builtins/ppc/builtins-ppc.cc',
|
||||
|
@ -14,7 +14,6 @@
|
||||
#include "src/property-descriptor.h"
|
||||
#include "src/simulator.h"
|
||||
#include "src/snapshot/snapshot.h"
|
||||
#include "src/trap-handler/trap-handler.h"
|
||||
#include "src/v8.h"
|
||||
|
||||
#include "src/asmjs/asm-wasm-builder.h"
|
||||
@ -671,18 +670,6 @@ static void InstanceFinalizer(const v8::WeakCallbackInfo<void>& data) {
|
||||
DCHECK(compiled_module->has_weak_wasm_module());
|
||||
WeakCell* weak_wasm_module = compiled_module->ptr_to_weak_wasm_module();
|
||||
|
||||
if (trap_handler::UseTrapHandler()) {
|
||||
Handle<FixedArray> code_table = compiled_module->code_table();
|
||||
for (int i = 0; i < code_table->length(); ++i) {
|
||||
Handle<Code> code = code_table->GetValueChecked<Code>(isolate, i);
|
||||
int index = code->trap_handler_index()->value();
|
||||
if (index >= 0) {
|
||||
trap_handler::ReleaseHandlerData(index);
|
||||
code->set_trap_handler_index(Smi::FromInt(-1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// weak_wasm_module may have been cleared, meaning the module object
|
||||
// was GC-ed. In that case, there won't be any new instances created,
|
||||
// and we don't need to maintain the links between instances.
|
||||
@ -1237,7 +1224,7 @@ class InstantiationHelper {
|
||||
//--------------------------------------------------------------------------
|
||||
// Unpack and notify signal handler of protected instructions.
|
||||
//--------------------------------------------------------------------------
|
||||
if (trap_handler::UseTrapHandler()) {
|
||||
if (FLAG_wasm_trap_handler) {
|
||||
for (int i = 0; i < code_table->length(); ++i) {
|
||||
Handle<Code> code = code_table->GetValueChecked<Code>(isolate_, i);
|
||||
|
||||
@ -1258,15 +1245,8 @@ class InstantiationHelper {
|
||||
reinterpret_cast<intptr_t>(it.rinfo()->pc()) - base;
|
||||
unpacked.emplace_back(data);
|
||||
}
|
||||
if (unpacked.size() > 0) {
|
||||
int size = code->CodeSize();
|
||||
const int index =
|
||||
RegisterHandlerData(reinterpret_cast<void*>(base), size,
|
||||
unpacked.size(), &unpacked[0]);
|
||||
// TODO(eholk): if index is negative, fail.
|
||||
DCHECK(index >= 0);
|
||||
code->set_trap_handler_index(Smi::FromInt(index));
|
||||
}
|
||||
// TODO(eholk): Register the protected instruction information once the
|
||||
// trap handler is in place.
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -31,7 +31,6 @@
|
||||
#include "include/libplatform/libplatform.h"
|
||||
#include "src/debug/debug.h"
|
||||
#include "src/objects-inl.h"
|
||||
#include "src/trap-handler/trap-handler.h"
|
||||
#include "test/cctest/print-extension.h"
|
||||
#include "test/cctest/profiler-extension.h"
|
||||
#include "test/cctest/trace-extension.h"
|
||||
@ -269,10 +268,6 @@ int main(int argc, char* argv[]) {
|
||||
v8::V8::Initialize();
|
||||
v8::V8::InitializeExternalStartupData(argv[0]);
|
||||
|
||||
if (i::trap_handler::UseTrapHandler()) {
|
||||
v8::V8::RegisterDefaultSignalHandler();
|
||||
}
|
||||
|
||||
CcTestArrayBufferAllocator array_buffer_allocator;
|
||||
CcTest::set_array_buffer_allocator(&array_buffer_allocator);
|
||||
|
||||
|
@ -3,7 +3,6 @@
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "src/assembler-inl.h"
|
||||
#include "src/trap-handler/trap-handler.h"
|
||||
#include "src/wasm/wasm-macro-gen.h"
|
||||
#include "test/cctest/cctest.h"
|
||||
#include "test/cctest/compiler/value-helper.h"
|
||||
@ -99,14 +98,6 @@ TEST(Unreachable) {
|
||||
|
||||
// Trigger a trap for loading from out-of-bounds.
|
||||
TEST(IllegalLoad) {
|
||||
if (trap_handler::UseTrapHandler()) {
|
||||
// r.module().AddMemory() does not allocate guard pages, so we skip this
|
||||
// test for now when using trap handlers. The simple out of bounds access
|
||||
// case is covered by mjsunit tests, so we are still getting test coverage.
|
||||
//
|
||||
// TODO(eholk): make this test work with trap handlers.
|
||||
return;
|
||||
}
|
||||
WasmRunner<void> r(kExecuteCompiled);
|
||||
TestSignatures sigs;
|
||||
// Set the execution context, such that a runtime error can be thrown.
|
||||
|
@ -23,7 +23,6 @@
|
||||
#include "src/compiler/pipeline.h"
|
||||
#include "src/compiler/wasm-compiler.h"
|
||||
#include "src/compiler/zone-stats.h"
|
||||
#include "src/trap-handler/trap-handler.h"
|
||||
#include "src/wasm/function-body-decoder.h"
|
||||
#include "src/wasm/wasm-external-refs.h"
|
||||
#include "src/wasm/wasm-interpreter.h"
|
||||
@ -823,29 +822,17 @@ bool WasmRunnerBase::trap_happened;
|
||||
TEST(RunWasmInterpreted_##name) { RunWasm_##name(kExecuteInterpreted); } \
|
||||
void RunWasm_##name(WasmExecutionMode execution_mode)
|
||||
|
||||
#define WASM_EXEC_TEST_WITH_TRAP(name) \
|
||||
void RunWasm_##name(WasmExecutionMode execution_mode); \
|
||||
TEST(RunWasmCompiled_##name) { \
|
||||
if (trap_handler::UseTrapHandler()) { \
|
||||
return; \
|
||||
} \
|
||||
RunWasm_##name(kExecuteCompiled); \
|
||||
} \
|
||||
TEST(RunWasmCompiledWithoutTrapIf_##name) { \
|
||||
if (trap_handler::UseTrapHandler()) { \
|
||||
return; \
|
||||
} \
|
||||
bool trap_if = FLAG_wasm_trap_if; \
|
||||
FLAG_wasm_trap_if = true; \
|
||||
RunWasm_##name(kExecuteCompiled); \
|
||||
FLAG_wasm_trap_if = trap_if; \
|
||||
} \
|
||||
TEST(RunWasmInterpreted_##name) { \
|
||||
if (trap_handler::UseTrapHandler()) { \
|
||||
return; \
|
||||
} \
|
||||
RunWasm_##name(kExecuteInterpreted); \
|
||||
} \
|
||||
#define WASM_EXEC_TEST_WITH_TRAP(name) \
|
||||
void RunWasm_##name(WasmExecutionMode execution_mode); \
|
||||
TEST(RunWasmCompiled_##name) { RunWasm_##name(kExecuteCompiled); } \
|
||||
void RunWasm_##name(WasmExecutionMode execution_mode); \
|
||||
TEST(RunWasmCompiledWithoutTrapIf_##name) { \
|
||||
bool trap_if = FLAG_wasm_trap_if; \
|
||||
FLAG_wasm_trap_if = false; \
|
||||
RunWasm_##name(kExecuteCompiled); \
|
||||
FLAG_wasm_trap_if = trap_if; \
|
||||
} \
|
||||
TEST(RunWasmInterpreted_##name) { RunWasm_##name(kExecuteInterpreted); } \
|
||||
void RunWasm_##name(WasmExecutionMode execution_mode)
|
||||
|
||||
#define WASM_EXEC_COMPILED_TEST(name) \
|
||||
|
@ -13,7 +13,7 @@ ALL_VARIANT_FLAGS = {
|
||||
"ignition_staging": [["--ignition-staging"]],
|
||||
"ignition_turbofan": [["--ignition-staging", "--turbo"]],
|
||||
"asm_wasm": [["--validate-asm"]],
|
||||
"wasm_traps": [["--wasm_guard_pages", "--wasm_trap_handler", "--invoke-weak-callbacks"]],
|
||||
"wasm_traps": [["--wasm_guard_pages", "--invoke-weak-callbacks"]],
|
||||
}
|
||||
|
||||
# FAST_VARIANTS implies no --always-opt.
|
||||
@ -26,7 +26,7 @@ FAST_VARIANT_FLAGS = {
|
||||
"ignition_staging": [["--ignition-staging"]],
|
||||
"ignition_turbofan": [["--ignition-staging", "--turbo"]],
|
||||
"asm_wasm": [["--validate-asm"]],
|
||||
"wasm_traps": [["--wasm_guard_pages", "--wasm_trap_handler", "--invoke-weak-callbacks"]],
|
||||
"wasm_traps": [["--wasm_guard_pages", "--invoke-weak-callbacks"]],
|
||||
}
|
||||
|
||||
ALL_VARIANTS = set(["default", "stress", "turbofan", "turbofan_opt",
|
||||
|
Loading…
Reference in New Issue
Block a user