Commit 15efe5a6 authored by Clemens Backes's avatar Clemens Backes Committed by Commit Bot

Reland "[wasm] Disallow late enabling of trap handlers"

This is a reland of bcb0a7c5.
Data races detected by TSan are fixed by using (relaxed) atomic
updates.

Original change's description:
> [wasm] Disallow late enabling of trap handlers
>
> It's dangerous if trap handlers are enabled after we already used the
> information whether they are enabled or not.
> This CL checks for such misbehaviour by remembering whether
> {IsTrapHandlerEnabled} was already called, and disallowing
> {EnableTrapHandler} afterwards. Also, calling {EnableTrapHandler}
> multiple times is disallowed now.
>
> The trap handler tests are changed to only enable trap handlers once,
> and to do that before allocating wasm memory or generating code.
>
> R=ahaas@chromium.org
>
> Bug: v8:11017
> Change-Id: Ib2256bb8435efd914c12769cedd4a0051052aeef
> Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2494935
> Reviewed-by: Andreas Haas <ahaas@chromium.org>
> Commit-Queue: Clemens Backes <clemensb@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#70750}

Bug: v8:11017
Cq-Include-Trybots: luci.v8.try:v8_linux64_tsan_rel_ng
Cq-Include-Trybots: luci.v8.try:v8_linux64_tsan_isolates_rel_ng
Change-Id: I24299c433ffa3ce31e2aac12134dc03f30609da2
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2498683
Commit-Queue: Clemens Backes <clemensb@chromium.org>
Reviewed-by: 's avatarAndreas Haas <ahaas@chromium.org>
Cr-Commit-Position: refs/heads/master@{#70761}
parent 4eedff25
...@@ -249,9 +249,19 @@ bool RegisterDefaultTrapHandler() { return false; } ...@@ -249,9 +249,19 @@ bool RegisterDefaultTrapHandler() { return false; }
void RemoveTrapHandler() {} void RemoveTrapHandler() {}
#endif #endif
bool g_is_trap_handler_enabled; bool g_is_trap_handler_enabled{false};
std::atomic<bool> g_can_enable_trap_handler{true};
bool EnableTrapHandler(bool use_v8_handler) { bool EnableTrapHandler(bool use_v8_handler) {
// We should only enable the trap handler once, and before any call to
// {IsTrapHandlerEnabled}. Enabling the trap handler late can lead to problems
// because code or objects might have been generated under the assumption that
// trap handlers are disabled.
bool can_enable =
g_can_enable_trap_handler.exchange(false, std::memory_order_relaxed);
if (!can_enable) {
FATAL("EnableTrapHandler called twice, or after IsTrapHandlerEnabled");
}
if (!V8_TRAP_HANDLER_SUPPORTED) { if (!V8_TRAP_HANDLER_SUPPORTED) {
return false; return false;
} }
......
...@@ -8,6 +8,8 @@ ...@@ -8,6 +8,8 @@
#include <stdint.h> #include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
#include <atomic>
#include "src/base/build_config.h" #include "src/base/build_config.h"
#include "src/common/globals.h" #include "src/common/globals.h"
#include "src/flags/flags.h" #include "src/flags/flags.h"
...@@ -64,7 +66,17 @@ void V8_EXPORT_PRIVATE ReleaseHandlerData(int index); ...@@ -64,7 +66,17 @@ void V8_EXPORT_PRIVATE ReleaseHandlerData(int index);
#define THREAD_LOCAL __thread #define THREAD_LOCAL __thread
#endif #endif
// Initially false, set to true if when trap handlers are enabled. Never goes
// back to false then.
extern bool g_is_trap_handler_enabled; extern bool g_is_trap_handler_enabled;
// Initially true, set to false when either {IsTrapHandlerEnabled} or
// {EnableTrapHandler} is called to prevent calling {EnableTrapHandler}
// repeatedly, or after {IsTrapHandlerEnabled}. Needs to be atomic because
// {IsTrapHandlerEnabled} can be called from any thread. Updated using relaxed
// semantics, since it's not used for synchronization.
extern std::atomic<bool> g_can_enable_trap_handler;
// Enables trap handling for WebAssembly bounds checks. // Enables trap handling for WebAssembly bounds checks.
// //
// use_v8_handler indicates that V8 should install its own handler // use_v8_handler indicates that V8 should install its own handler
...@@ -73,6 +85,13 @@ V8_EXPORT_PRIVATE bool EnableTrapHandler(bool use_v8_handler); ...@@ -73,6 +85,13 @@ V8_EXPORT_PRIVATE bool EnableTrapHandler(bool use_v8_handler);
inline bool IsTrapHandlerEnabled() { inline bool IsTrapHandlerEnabled() {
DCHECK_IMPLIES(g_is_trap_handler_enabled, V8_TRAP_HANDLER_SUPPORTED); DCHECK_IMPLIES(g_is_trap_handler_enabled, V8_TRAP_HANDLER_SUPPORTED);
// Disallow enabling the trap handler after retrieving the current value.
// Re-enabling them late can produce issues because code or objects might have
// been generated under the assumption that trap handlers are disabled.
// Note: We test before setting to avoid contention by an unconditional write.
if (g_can_enable_trap_handler.load(std::memory_order_relaxed)) {
g_can_enable_trap_handler.store(false, std::memory_order_relaxed);
}
return g_is_trap_handler_enabled; return g_is_trap_handler_enabled;
} }
......
...@@ -80,7 +80,8 @@ class TrapHandlerTest : public TestWithIsolate, ...@@ -80,7 +80,8 @@ class TrapHandlerTest : public TestWithIsolate,
public ::testing::WithParamInterface<TrapHandlerStyle> { public ::testing::WithParamInterface<TrapHandlerStyle> {
protected: protected:
void SetUp() override { void SetUp() override {
CHECK(trap_handler::EnableTrapHandler(false)); InstallFallbackHandler();
SetupTrapHandler(GetParam());
backing_store_ = BackingStore::AllocateWasmMemory(i_isolate(), 1, 1, backing_store_ = BackingStore::AllocateWasmMemory(i_isolate(), 1, 1,
SharedFlag::kNotShared); SharedFlag::kNotShared);
CHECK(backing_store_); CHECK(backing_store_);
...@@ -93,7 +94,9 @@ class TrapHandlerTest : public TestWithIsolate, ...@@ -93,7 +94,9 @@ class TrapHandlerTest : public TestWithIsolate,
GetRandomMmapAddr()); GetRandomMmapAddr());
InitRecoveryCode(); InitRecoveryCode();
}
void InstallFallbackHandler() {
#if V8_OS_LINUX || V8_OS_MACOSX || V8_OS_FREEBSD #if V8_OS_LINUX || V8_OS_MACOSX || V8_OS_FREEBSD
// Set up a signal handler to recover from the expected crash. // Set up a signal handler to recover from the expected crash.
struct sigaction action; struct sigaction action;
...@@ -197,13 +200,13 @@ class TrapHandlerTest : public TestWithIsolate, ...@@ -197,13 +200,13 @@ class TrapHandlerTest : public TestWithIsolate,
} }
#endif #endif
public:
void SetupTrapHandler(TrapHandlerStyle style) { void SetupTrapHandler(TrapHandlerStyle style) {
bool use_default_handler = style == kDefault; bool use_default_handler = style == kDefault;
g_use_as_first_chance_handler = !use_default_handler; g_use_as_first_chance_handler = !use_default_handler;
CHECK(v8::V8::EnableWebAssemblyTrapHandler(use_default_handler)); CHECK(v8::V8::EnableWebAssemblyTrapHandler(use_default_handler));
} }
public:
void GenerateSetThreadInWasmFlagCode(MacroAssembler* masm) { void GenerateSetThreadInWasmFlagCode(MacroAssembler* masm) {
masm->Move(scratch, masm->Move(scratch,
i_isolate()->thread_local_top()->thread_in_wasm_flag_address_, i_isolate()->thread_local_top()->thread_in_wasm_flag_address_,
...@@ -282,7 +285,6 @@ TEST_P(TrapHandlerTest, TestTrapHandlerRecovery) { ...@@ -282,7 +285,6 @@ TEST_P(TrapHandlerTest, TestTrapHandlerRecovery) {
CodeDesc desc; CodeDesc desc;
masm.GetCode(nullptr, &desc); masm.GetCode(nullptr, &desc);
SetupTrapHandler(GetParam());
trap_handler::ProtectedInstructionData protected_instruction{crash_offset, trap_handler::ProtectedInstructionData protected_instruction{crash_offset,
recovery_offset}; recovery_offset};
trap_handler::RegisterHandlerData(reinterpret_cast<Address>(desc.buffer), trap_handler::RegisterHandlerData(reinterpret_cast<Address>(desc.buffer),
...@@ -314,8 +316,6 @@ TEST_P(TrapHandlerTest, TestReleaseHandlerData) { ...@@ -314,8 +316,6 @@ TEST_P(TrapHandlerTest, TestReleaseHandlerData) {
reinterpret_cast<Address>(desc.buffer), desc.instr_size, 1, reinterpret_cast<Address>(desc.buffer), desc.instr_size, 1,
&protected_instruction); &protected_instruction);
SetupTrapHandler(GetParam());
ExecuteBuffer(); ExecuteBuffer();
// Deregister from the trap handler. The trap handler should not do the // Deregister from the trap handler. The trap handler should not do the
...@@ -345,8 +345,6 @@ TEST_P(TrapHandlerTest, TestNoThreadInWasmFlag) { ...@@ -345,8 +345,6 @@ TEST_P(TrapHandlerTest, TestNoThreadInWasmFlag) {
trap_handler::RegisterHandlerData(reinterpret_cast<Address>(desc.buffer), trap_handler::RegisterHandlerData(reinterpret_cast<Address>(desc.buffer),
desc.instr_size, 1, &protected_instruction); desc.instr_size, 1, &protected_instruction);
SetupTrapHandler(GetParam());
ExecuteExpectCrash(buffer_.get()); ExecuteExpectCrash(buffer_.get());
} }
...@@ -373,8 +371,6 @@ TEST_P(TrapHandlerTest, TestCrashInWasmNoProtectedInstruction) { ...@@ -373,8 +371,6 @@ TEST_P(TrapHandlerTest, TestCrashInWasmNoProtectedInstruction) {
trap_handler::RegisterHandlerData(reinterpret_cast<Address>(desc.buffer), trap_handler::RegisterHandlerData(reinterpret_cast<Address>(desc.buffer),
desc.instr_size, 1, &protected_instruction); desc.instr_size, 1, &protected_instruction);
SetupTrapHandler(GetParam());
ExecuteExpectCrash(buffer_.get()); ExecuteExpectCrash(buffer_.get());
} }
...@@ -401,8 +397,6 @@ TEST_P(TrapHandlerTest, TestCrashInWasmWrongCrashType) { ...@@ -401,8 +397,6 @@ TEST_P(TrapHandlerTest, TestCrashInWasmWrongCrashType) {
trap_handler::RegisterHandlerData(reinterpret_cast<Address>(desc.buffer), trap_handler::RegisterHandlerData(reinterpret_cast<Address>(desc.buffer),
desc.instr_size, 1, &protected_instruction); desc.instr_size, 1, &protected_instruction);
SetupTrapHandler(GetParam());
#if V8_OS_POSIX #if V8_OS_POSIX
// On Posix, the V8 default trap handler does not register for SIGFPE, // On Posix, the V8 default trap handler does not register for SIGFPE,
// therefore the thread-in-wasm flag is never reset in this test. We // therefore the thread-in-wasm flag is never reset in this test. We
...@@ -462,8 +456,6 @@ TEST_P(TrapHandlerTest, TestCrashInOtherThread) { ...@@ -462,8 +456,6 @@ TEST_P(TrapHandlerTest, TestCrashInOtherThread) {
trap_handler::RegisterHandlerData(reinterpret_cast<Address>(desc.buffer), trap_handler::RegisterHandlerData(reinterpret_cast<Address>(desc.buffer),
desc.instr_size, 1, &protected_instruction); desc.instr_size, 1, &protected_instruction);
SetupTrapHandler(GetParam());
CodeRunner runner(this, buffer_.get()); CodeRunner runner(this, buffer_.get());
CHECK(!GetThreadInWasmFlag()); CHECK(!GetThreadInWasmFlag());
// Set the thread-in-wasm flag manually in this thread. // Set the thread-in-wasm flag manually in this thread.
......
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