Commit 180a8ca8 authored by Clemens Backes's avatar Clemens Backes Committed by V8 LUCI CQ

Reland "[traphandler] Add simulator support"

This is a reland of 431fff66.
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: 's avatarAndreas Haas <ahaas@chromium.org>
Cr-Commit-Position: refs/heads/master@{#75809}
parent 7cebcd0b
......@@ -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
......
// 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");
// 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",
......
// 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_.*',
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment