Reland "[traphandler] Add simulator support"
This is a reland of 431fff66f5
.
The fix is in BUILD.gn: We need to also include chromeos, which is a
linux target which is not covered by "is_linux" in gn.
R=ahaas@chromium.org
Original change's description:
> [traphandler] Add simulator support
>
> This prepares the trap handler to support being used from simulators.
> Modifications to the arm64 simulator will be done in a follow-up CL. For
> now, the trap handler will be registered but not used in Wasm (we emit
> explicit bounds checks instead, as before).
>
> The implementation uses inline assembly, so it is only available on x64
> POSIX systems for now. This is the main platform we use for testing and
> for fuzzing, so it should give us the test coverage we need. If needed,
> inline assembly for other platforms can be added later.
> The new code will be executed by the existing arm64 simulator bots, e.g.
> "V8 Linux - arm64 - sim".
>
> R=ahaas@chromium.org, mseaborn@chromium.org
>
> Bug: v8:11955
> Change-Id: Idc50291c704d9dea902ae0098e5309f19055816c
> Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3011160
> Commit-Queue: Clemens Backes <clemensb@chromium.org>
> Reviewed-by: Andreas Haas <ahaas@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#75780}
Bug: v8:11955
Change-Id: I8af39dea5b2cd3fa5418170a458832b3d6075107
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3040844
Commit-Queue: Clemens Backes <clemensb@chromium.org>
Commit-Queue: Andreas Haas <ahaas@chromium.org>
Auto-Submit: Clemens Backes <clemensb@chromium.org>
Reviewed-by: Andreas Haas <ahaas@chromium.org>
Cr-Commit-Position: refs/heads/master@{#75809}
This commit is contained in:
parent
7cebcd0b0d
commit
180a8ca840
28
BUILD.gn
28
BUILD.gn
@ -3377,8 +3377,15 @@ v8_header_set("v8_internal_headers") {
|
||||
if (v8_control_flow_integrity) {
|
||||
sources += [ "src/execution/arm64/pointer-authentication-arm64.h" ]
|
||||
}
|
||||
if (v8_enable_webassembly && current_cpu == "arm64" && is_mac) {
|
||||
sources += [ "src/trap-handler/handler-inside-posix.h" ]
|
||||
if (v8_enable_webassembly) {
|
||||
# Trap handling is enabled on arm64 Mac and in simulators on x64 on Linux.
|
||||
if ((current_cpu == "arm64" && is_mac) ||
|
||||
(current_cpu == "x64" && (is_linux || is_chromeos))) {
|
||||
sources += [ "src/trap-handler/handler-inside-posix.h" ]
|
||||
}
|
||||
if (current_cpu == "x64" && (is_linux || is_chromeos)) {
|
||||
sources += [ "src/trap-handler/trap-handler-simulator.h" ]
|
||||
}
|
||||
}
|
||||
if (is_win) {
|
||||
sources += [ "src/diagnostics/unwinding-info-win64.h" ]
|
||||
@ -4278,11 +4285,18 @@ v8_source_set("v8_base_without_compiler") {
|
||||
"src/execution/arm64/simulator-logic-arm64.cc",
|
||||
"src/regexp/arm64/regexp-macro-assembler-arm64.cc",
|
||||
]
|
||||
if (v8_enable_webassembly && current_cpu == "arm64" && is_mac) {
|
||||
sources += [
|
||||
"src/trap-handler/handler-inside-posix.cc",
|
||||
"src/trap-handler/handler-outside-posix.cc",
|
||||
]
|
||||
if (v8_enable_webassembly) {
|
||||
# Trap handling is enabled on arm64 Mac and in simulators on x64 on Linux.
|
||||
if ((current_cpu == "arm64" && is_mac) ||
|
||||
(current_cpu == "x64" && (is_linux || is_chromeos))) {
|
||||
sources += [
|
||||
"src/trap-handler/handler-inside-posix.cc",
|
||||
"src/trap-handler/handler-outside-posix.cc",
|
||||
]
|
||||
}
|
||||
if (current_cpu == "x64" && (is_linux || is_chromeos)) {
|
||||
sources += [ "src/trap-handler/handler-outside-simulator.cc" ]
|
||||
}
|
||||
}
|
||||
if (is_win) {
|
||||
sources += [ "src/diagnostics/unwinding-info-win64.cc" ]
|
||||
|
@ -277,7 +277,13 @@ RUNTIME_FUNCTION(Runtime_IsWasmCode) {
|
||||
RUNTIME_FUNCTION(Runtime_IsWasmTrapHandlerEnabled) {
|
||||
DisallowGarbageCollection no_gc;
|
||||
DCHECK_EQ(0, args.length());
|
||||
// We currently lie to tests about enabled trap handling in simulator builds.
|
||||
// TODO(clemensb): Remove this once the arm64 simulator support trap handling.
|
||||
#ifdef USE_SIMULATOR
|
||||
return isolate->heap()->ToBoolean(false);
|
||||
#else
|
||||
return isolate->heap()->ToBoolean(trap_handler::IsTrapHandlerEnabled());
|
||||
#endif
|
||||
}
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_IsThreadInWasm) {
|
||||
|
@ -43,6 +43,16 @@ namespace v8 {
|
||||
namespace internal {
|
||||
namespace trap_handler {
|
||||
|
||||
#if V8_OS_LINUX
|
||||
#define CONTEXT_REG(reg, REG) &uc->uc_mcontext.gregs[REG_##REG]
|
||||
#elif V8_OS_MACOSX
|
||||
#define CONTEXT_REG(reg, REG) &uc->uc_mcontext->__ss.__##reg
|
||||
#elif V8_OS_FREEBSD
|
||||
#define CONTEXT_REG(reg, REG) &uc->uc_mcontext.mc_##reg
|
||||
#else
|
||||
#error "Unsupported platform."
|
||||
#endif
|
||||
|
||||
bool IsKernelGeneratedSignal(siginfo_t* info) {
|
||||
// On macOS, only `info->si_code > 0` is relevant, because macOS leaves
|
||||
// si_code at its default of 0 for signals that don’t originate in hardware.
|
||||
@ -72,11 +82,18 @@ class UnmaskOobSignalScope {
|
||||
sigset_t old_mask_;
|
||||
};
|
||||
|
||||
#ifdef V8_TRAP_HANDLER_VIA_SIMULATOR
|
||||
// These are addresses inside the "ProbeMemory" function, defined in
|
||||
// "handler-outside-simulators.cc".
|
||||
extern "C" char v8_probe_memory_address[];
|
||||
extern "C" char v8_probe_memory_continuation[];
|
||||
#endif // V8_TRAP_HANDLER_VIA_SIMULATOR
|
||||
|
||||
bool TryHandleSignal(int signum, siginfo_t* info, void* context) {
|
||||
// Ensure the faulting thread was actually running Wasm code. This should be
|
||||
// the first check in the trap handler to guarantee that the IsThreadInWasm
|
||||
// flag is only set in wasm code. Otherwise a later signal handler is executed
|
||||
// with the flag set.
|
||||
// the first check in the trap handler to guarantee that the
|
||||
// g_thread_in_wasm_code flag is only set in wasm code. Otherwise a later
|
||||
// signal handler is executed with the flag set.
|
||||
if (!g_thread_in_wasm_code) return false;
|
||||
|
||||
// Clear g_thread_in_wasm_code, primarily to protect against nested faults.
|
||||
@ -102,23 +119,38 @@ bool TryHandleSignal(int signum, siginfo_t* info, void* context) {
|
||||
UnmaskOobSignalScope unmask_oob_signal;
|
||||
|
||||
ucontext_t* uc = reinterpret_cast<ucontext_t*>(context);
|
||||
#if V8_OS_LINUX && V8_TARGET_ARCH_X64
|
||||
auto* context_ip = &uc->uc_mcontext.gregs[REG_RIP];
|
||||
#elif V8_OS_MACOSX && V8_TARGET_ARCH_ARM64
|
||||
auto* context_ip = &uc->uc_mcontext->__ss.__pc;
|
||||
#elif V8_OS_MACOSX && V8_TARGET_ARCH_X64
|
||||
auto* context_ip = &uc->uc_mcontext->__ss.__rip;
|
||||
#elif V8_OS_FREEBSD && V8_TARGET_ARCH_X64
|
||||
auto* context_ip = &uc->uc_mcontext.mc_rip;
|
||||
#if V8_HOST_ARCH_X64
|
||||
auto* context_ip = CONTEXT_REG(rip, RIP);
|
||||
#elif V8_HOST_ARCH_ARM64
|
||||
auto* context_ip = CONTEXT_REG(pc, PC);
|
||||
#else
|
||||
#error Unsupported platform
|
||||
#error "Unsupported architecture."
|
||||
#endif
|
||||
|
||||
uintptr_t fault_addr = *context_ip;
|
||||
uintptr_t landing_pad = 0;
|
||||
|
||||
#ifdef V8_TRAP_HANDLER_VIA_SIMULATOR
|
||||
// Only handle signals triggered by the load in {ProbeMemory}.
|
||||
if (fault_addr != reinterpret_cast<uintptr_t>(&v8_probe_memory_address)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// The simulated ip will be in the second parameter register (%rsi).
|
||||
auto* simulated_ip_reg = CONTEXT_REG(rsi, RSI);
|
||||
if (!TryFindLandingPad(*simulated_ip_reg, &landing_pad)) return false;
|
||||
TH_DCHECK(landing_pad != 0);
|
||||
|
||||
auto* return_reg = CONTEXT_REG(rax, RAX);
|
||||
*return_reg = landing_pad;
|
||||
// Continue at the memory probing continuation.
|
||||
*context_ip = reinterpret_cast<uintptr_t>(&v8_probe_memory_continuation);
|
||||
#else
|
||||
if (!TryFindLandingPad(fault_addr, &landing_pad)) return false;
|
||||
|
||||
// Tell the caller to return to the landing pad.
|
||||
*context_ip = landing_pad;
|
||||
#endif
|
||||
}
|
||||
// We will return to wasm code, so restore the g_thread_in_wasm_code flag.
|
||||
// This should only be done once the signal is blocked again (outside the
|
||||
|
30
src/trap-handler/handler-outside-simulator.cc
Normal file
30
src/trap-handler/handler-outside-simulator.cc
Normal file
@ -0,0 +1,30 @@
|
||||
// Copyright 2021 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 "include/v8config.h"
|
||||
#include "src/trap-handler/trap-handler-simulator.h"
|
||||
|
||||
#if !V8_OS_LINUX
|
||||
#error "The inline assembly only works on Linux so far."
|
||||
#endif
|
||||
|
||||
asm(
|
||||
// Define the ProbeMemory function declared in trap-handler-simulators.h.
|
||||
".pushsection .text \n"
|
||||
".globl ProbeMemory \n"
|
||||
".type ProbeMemory, %function \n"
|
||||
".globl v8_probe_memory_address \n"
|
||||
".globl v8_probe_memory_continuation \n"
|
||||
"ProbeMemory: \n"
|
||||
// First parameter (address) passed in %rdi.
|
||||
// The second parameter (pc) is unused here. It is read by the trap handler
|
||||
// instead.
|
||||
"v8_probe_memory_address: \n"
|
||||
" movb (%rdi), %al \n"
|
||||
// Return 0 on success.
|
||||
" xorl %eax, %eax \n"
|
||||
"v8_probe_memory_continuation: \n"
|
||||
// If the trap handler continues here, it wrote the landing pad in %rax.
|
||||
" ret \n"
|
||||
".popsection \n");
|
37
src/trap-handler/trap-handler-simulator.h
Normal file
37
src/trap-handler/trap-handler-simulator.h
Normal file
@ -0,0 +1,37 @@
|
||||
// Copyright 2021 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef V8_TRAP_HANDLER_TRAP_HANDLER_SIMULATOR_H_
|
||||
#define V8_TRAP_HANDLER_TRAP_HANDLER_SIMULATOR_H_
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
// This header defines the ProbeMemory function to be used by simulators to
|
||||
// trigger a signal at a defined location, before doing an actual memory access.
|
||||
|
||||
// This implementation is only usable on an x64 host with non-x64 target (i.e. a
|
||||
// simulator build on x64).
|
||||
#if (!defined(_M_X64) && !defined(__x86_64__)) || defined(V8_TARGET_ARCH_X64)
|
||||
#error "Do only include this file on simulator builds on x64."
|
||||
#endif
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
namespace trap_handler {
|
||||
|
||||
// Probe a memory address by doing a 1-byte read from the given address. If the
|
||||
// address is not readable, this will cause a trap as usual, but the trap
|
||||
// handler will recognise the address of the instruction doing the access and
|
||||
// treat it specially. It will use the given {pc} to look up the respective
|
||||
// landing pad and return to this function to return that landing pad. If {pc}
|
||||
// is not registered as a protected instruction, the signal will be propagated
|
||||
// as usual.
|
||||
// If the read at {address} succeeds, this function returns {0} instead.
|
||||
extern "C" uintptr_t ProbeMemory(uintptr_t address, uintptr_t pc);
|
||||
|
||||
} // namespace trap_handler
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
||||
#endif // V8_TRAP_HANDLER_TRAP_HANDLER_SIMULATOR_H_
|
@ -17,16 +17,19 @@ namespace v8 {
|
||||
namespace internal {
|
||||
namespace trap_handler {
|
||||
|
||||
#if V8_TARGET_ARCH_X64 && V8_OS_LINUX && !V8_OS_ANDROID
|
||||
// X64 on Linux, Windows, MacOS, FreeBSD.
|
||||
#if V8_HOST_ARCH_X64 && V8_TARGET_ARCH_X64 && \
|
||||
((V8_OS_LINUX && !V8_OS_ANDROID) || V8_OS_WIN || V8_OS_MACOSX || \
|
||||
V8_OS_FREEBSD)
|
||||
#define V8_TRAP_HANDLER_SUPPORTED true
|
||||
#elif V8_TARGET_ARCH_X64 && V8_OS_WIN
|
||||
// Arm64 (non-simulator) on Mac.
|
||||
#elif V8_TARGET_ARCH_ARM64 && V8_HOST_ARCH_ARM64 && V8_OS_MACOSX
|
||||
#define V8_TRAP_HANDLER_SUPPORTED true
|
||||
#elif V8_TARGET_ARCH_X64 && V8_OS_MACOSX
|
||||
#define V8_TRAP_HANDLER_SUPPORTED true
|
||||
#elif V8_TARGET_ARCH_X64 && V8_OS_FREEBSD
|
||||
#define V8_TRAP_HANDLER_SUPPORTED true
|
||||
#elif V8_HOST_ARCH_ARM64 && V8_TARGET_ARCH_ARM64 && V8_OS_MACOSX
|
||||
// Arm64 simulator on x64 on Linux.
|
||||
#elif V8_TARGET_ARCH_ARM64 && V8_HOST_ARCH_X64 && V8_OS_LINUX
|
||||
#define V8_TRAP_HANDLER_VIA_SIMULATOR
|
||||
#define V8_TRAP_HANDLER_SUPPORTED true
|
||||
// Everything else is unsupported.
|
||||
#else
|
||||
#define V8_TRAP_HANDLER_SUPPORTED false
|
||||
#endif
|
||||
|
@ -823,7 +823,10 @@ BoundsCheckStrategy GetBoundsChecks(const WasmModule* module) {
|
||||
if (FLAG_wasm_enforce_bounds_checks) return kExplicitBoundsChecks;
|
||||
// We do not have trap handler support for memory64 yet.
|
||||
if (module->is_memory64) return kExplicitBoundsChecks;
|
||||
// TODO(clemensb): Enable trap handling in the arm64 simulator.
|
||||
#ifndef USE_SIMULATOR
|
||||
if (trap_handler::IsTrapHandlerEnabled()) return kTrapHandler;
|
||||
#endif // USE_SIMULATOR
|
||||
return kExplicitBoundsChecks;
|
||||
}
|
||||
} // namespace
|
||||
|
@ -488,6 +488,12 @@ v8_source_set("unittests_sources") {
|
||||
sources += [ "wasm/trap-handler-win-unittest.cc" ]
|
||||
}
|
||||
|
||||
# Include this test only on arm64 simulator builds on x64 on Linux.
|
||||
if (current_cpu == "x64" && v8_current_cpu == "arm64" && is_linux &&
|
||||
v8_enable_webassembly) {
|
||||
sources += [ "wasm/trap-handler-simulator-unittest.cc" ]
|
||||
}
|
||||
|
||||
configs = [
|
||||
"../..:cppgc_base_config",
|
||||
"../..:external_config",
|
||||
|
126
test/unittests/wasm/trap-handler-simulator-unittest.cc
Normal file
126
test/unittests/wasm/trap-handler-simulator-unittest.cc
Normal file
@ -0,0 +1,126 @@
|
||||
// Copyright 2021 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/trap-handler/trap-handler-simulator.h"
|
||||
|
||||
#include "include/v8.h"
|
||||
#include "src/codegen/macro-assembler-inl.h"
|
||||
#include "src/execution/simulator.h"
|
||||
#include "src/trap-handler/trap-handler.h"
|
||||
#include "test/common/assembler-tester.h"
|
||||
#include "test/unittests/test-utils.h"
|
||||
|
||||
#if !V8_HOST_ARCH_X64 || !V8_TARGET_ARCH_ARM64
|
||||
#error "Only include this file on arm64 simulator builds on x64."
|
||||
#endif
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
namespace trap_handler {
|
||||
|
||||
constexpr uintptr_t kFakePc = 11;
|
||||
|
||||
class SimulatorTrapHandlerTest : public TestWithIsolate {
|
||||
public:
|
||||
static void SetThreadInWasm() {
|
||||
EXPECT_EQ(0, g_thread_in_wasm_code);
|
||||
g_thread_in_wasm_code = 1;
|
||||
}
|
||||
|
||||
static void ResetThreadInWasm() {
|
||||
EXPECT_EQ(1, g_thread_in_wasm_code);
|
||||
g_thread_in_wasm_code = 0;
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(SimulatorTrapHandlerTest, ProbeMemorySuccess) {
|
||||
int x = 47;
|
||||
EXPECT_EQ(0u, ProbeMemory(reinterpret_cast<uintptr_t>(&x), kFakePc));
|
||||
}
|
||||
|
||||
TEST_F(SimulatorTrapHandlerTest, ProbeMemoryFail) {
|
||||
constexpr uintptr_t kNullAddress = 0;
|
||||
EXPECT_DEATH_IF_SUPPORTED(ProbeMemory(kNullAddress, kFakePc), "");
|
||||
}
|
||||
|
||||
TEST_F(SimulatorTrapHandlerTest, ProbeMemoryFailWhileInWasm) {
|
||||
// Test that we still crash if the trap handler is set up and the "thread in
|
||||
// wasm" flag is set, but the PC is not registered as a protected instruction.
|
||||
constexpr bool kUseDefaultHandler = true;
|
||||
CHECK(v8::V8::EnableWebAssemblyTrapHandler(kUseDefaultHandler));
|
||||
|
||||
constexpr uintptr_t kNullAddress = 0;
|
||||
SetThreadInWasm();
|
||||
EXPECT_DEATH_IF_SUPPORTED(ProbeMemory(kNullAddress, kFakePc), "");
|
||||
}
|
||||
|
||||
TEST_F(SimulatorTrapHandlerTest, ProbeMemoryWithTrapHandled) {
|
||||
constexpr uintptr_t kNullAddress = 0;
|
||||
constexpr uintptr_t kFakeLandingPad = 19;
|
||||
|
||||
constexpr bool kUseDefaultHandler = true;
|
||||
CHECK(v8::V8::EnableWebAssemblyTrapHandler(kUseDefaultHandler));
|
||||
|
||||
ProtectedInstructionData fake_protected_instruction{kFakePc, kFakeLandingPad};
|
||||
int handler_data_index =
|
||||
RegisterHandlerData(0, 128, 1, &fake_protected_instruction);
|
||||
|
||||
SetThreadInWasm();
|
||||
EXPECT_EQ(kFakeLandingPad, ProbeMemory(kNullAddress, kFakePc));
|
||||
|
||||
// Reset everything.
|
||||
ResetThreadInWasm();
|
||||
ReleaseHandlerData(handler_data_index);
|
||||
RemoveTrapHandler();
|
||||
}
|
||||
|
||||
TEST_F(SimulatorTrapHandlerTest, ProbeMemoryWithLandingPad) {
|
||||
EXPECT_EQ(0u, GetRecoveredTrapCount());
|
||||
|
||||
// Test that the trap handler can recover a memory access violation in
|
||||
// wasm code (we fake the wasm code and the access violation).
|
||||
std::unique_ptr<TestingAssemblerBuffer> buffer = AllocateAssemblerBuffer();
|
||||
constexpr Register scratch = x0;
|
||||
MacroAssembler masm(nullptr, AssemblerOptions{}, CodeObjectRequired::kNo,
|
||||
buffer->CreateView());
|
||||
// Generate an illegal memory access.
|
||||
masm.Mov(scratch, 0);
|
||||
uint32_t crash_offset = masm.pc_offset();
|
||||
masm.Str(scratch, MemOperand(scratch, 0)); // nullptr access
|
||||
uint32_t recovery_offset = masm.pc_offset();
|
||||
// Return.
|
||||
masm.Ret();
|
||||
|
||||
CodeDesc desc;
|
||||
masm.GetCode(nullptr, &desc);
|
||||
|
||||
constexpr bool kUseDefaultHandler = true;
|
||||
CHECK(v8::V8::EnableWebAssemblyTrapHandler(kUseDefaultHandler));
|
||||
|
||||
ProtectedInstructionData protected_instruction{crash_offset, recovery_offset};
|
||||
int handler_data_index =
|
||||
RegisterHandlerData(reinterpret_cast<Address>(desc.buffer),
|
||||
desc.instr_size, 1, &protected_instruction);
|
||||
|
||||
// Now execute the code.
|
||||
buffer->MakeExecutable();
|
||||
GeneratedCode<void> code = GeneratedCode<void>::FromAddress(
|
||||
i_isolate(), reinterpret_cast<Address>(desc.buffer));
|
||||
|
||||
SetThreadInWasm();
|
||||
// TODO(clemensb): This should pass after adding memory probing in the
|
||||
// simulator.
|
||||
// code.Call();
|
||||
EXPECT_DEATH_IF_SUPPORTED(code.Call(), "");
|
||||
ResetThreadInWasm();
|
||||
|
||||
ReleaseHandlerData(handler_data_index);
|
||||
RemoveTrapHandler();
|
||||
|
||||
// EXPECT_EQ(1u, GetRecoveredTrapCount());
|
||||
}
|
||||
|
||||
} // namespace trap_handler
|
||||
} // namespace internal
|
||||
} // namespace v8
|
@ -33,6 +33,8 @@ AUTO_EXCLUDE = [
|
||||
'src/flags/flag-definitions.h',
|
||||
# recorder.h should only be included conditionally.
|
||||
'src/libplatform/tracing/recorder.h',
|
||||
# trap-handler-simulator.h can only be included in simulator builds.
|
||||
'src/trap-handler/trap-handler-simulator.h',
|
||||
]
|
||||
AUTO_EXCLUDE_PATTERNS = [
|
||||
'src/base/atomicops_internals_.*',
|
||||
|
Loading…
Reference in New Issue
Block a user