Commit 08f16d44 authored by Clemens Backes's avatar Clemens Backes Committed by V8 LUCI CQ

[wasm][pku] Assert write protection in strategic places

This adds a few DCHECKs to ensure that the process-wide memory
protection key is not writable (per thread) in a few strategic places:
- Before switching it to writable (which implicitly checks the initial
    state),
- when entering compiled code, and
- in the explicit unit test.

R=jkummerow@chromium.org
CC=mpdenton@chromium.org

Bug: v8:11974
Change-Id: I6037f599afe9009d5e48794eb382eb1979f3ce9f
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3165060Reviewed-by: 's avatarJakob Kummerow <jkummerow@chromium.org>
Commit-Queue: Clemens Backes <clemensb@chromium.org>
Cr-Commit-Position: refs/heads/main@{#76953}
parent eb746422
......@@ -13,7 +13,8 @@
#if V8_ENABLE_WEBASSEMBLY
#include "src/compiler/wasm-compiler.h" // Only for static asserts.
#endif // V8_ENABLE_WEBASSEMBLY
#include "src/wasm/wasm-engine.h"
#endif // V8_ENABLE_WEBASSEMBLY
namespace v8 {
namespace internal {
......@@ -252,6 +253,11 @@ V8_WARN_UNUSED_RESULT MaybeHandle<Object> Invoke(Isolate* isolate,
DCHECK(!params.receiver->IsJSGlobalObject());
DCHECK_LE(params.argc, FixedArray::kMaxLength);
// If we have PKU support for Wasm, ensure that code is currently write
// protected for this thread.
DCHECK_IMPLIES(wasm::GetWasmCodeManager()->HasMemoryProtectionKeySupport(),
wasm::GetWasmCodeManager()->MemoryProtectionKeyWritable());
#ifdef USE_SIMULATOR
// Simulators use separate stacks for C++ and JS. JS stack overflow checks
// are performed whenever a JS function is called. However, it can be the case
......
......@@ -166,7 +166,7 @@ bool SetPermissionsAndMemoryProtectionKey(
DISABLE_CFI_ICALL
void SetPermissionsForMemoryProtectionKey(
int key, MemoryProtectionKeyPermission permissions) {
CHECK_NE(kNoMemoryProtectionKey, key);
DCHECK_NE(kNoMemoryProtectionKey, key);
#if defined(V8_OS_LINUX) && defined(V8_HOST_ARCH_X64)
typedef int (*pkey_set_t)(int, unsigned int);
......@@ -177,8 +177,27 @@ void SetPermissionsForMemoryProtectionKey(
int ret = pkey_set(key, permissions);
CHECK_EQ(0 /* success */, ret);
#else
// On platforms without PKU support, we should have failed the CHECK above
// because the key must be {kNoMemoryProtectionKey}.
// On platforms without PKU support, this method cannot be called because
// no protection key can have been allocated.
UNREACHABLE();
#endif
}
DISABLE_CFI_ICALL
bool MemoryProtectionKeyWritable(int key) {
DCHECK_NE(kNoMemoryProtectionKey, key);
#if defined(V8_OS_LINUX) && defined(V8_HOST_ARCH_X64)
typedef int (*pkey_get_t)(int);
static auto* pkey_get = bit_cast<pkey_get_t>(dlsym(RTLD_DEFAULT, "pkey_get"));
// If a valid key was allocated, {pkey_get()} must also be available.
DCHECK_NOT_NULL(pkey_get);
int permissions = pkey_get(key);
return permissions == kNoRestrictions;
#else
// On platforms without PKU support, this method cannot be called because
// no protection key can have been allocated.
UNREACHABLE();
#endif
}
......
......@@ -82,6 +82,10 @@ bool SetPermissionsAndMemoryProtectionKey(
void SetPermissionsForMemoryProtectionKey(
int key, MemoryProtectionKeyPermission permissions);
// Returns {true} if the protection key {key} is write-enabled for the current
// thread.
bool MemoryProtectionKeyWritable(int key);
} // namespace wasm
} // namespace internal
} // namespace v8
......
......@@ -2128,6 +2128,11 @@ void WasmCodeManager::SetThreadWritable(bool writable) {
MemoryProtectionKeyPermission permissions =
writable ? kNoRestrictions : kDisableWrite;
// When switching to writable we should not already be writable. Otherwise
// this points at a problem with counting writers, or with wrong
// initialization (globally or per thread).
DCHECK_IMPLIES(writable, !MemoryProtectionKeyWritable());
TRACE_HEAP("Setting memory protection key %d to writable: %d.\n",
memory_protection_key_, writable);
SetPermissionsForMemoryProtectionKey(memory_protection_key_, permissions);
......@@ -2137,6 +2142,10 @@ bool WasmCodeManager::HasMemoryProtectionKeySupport() const {
return memory_protection_key_ != kNoMemoryProtectionKey;
}
bool WasmCodeManager::MemoryProtectionKeyWritable() const {
return wasm::MemoryProtectionKeyWritable(memory_protection_key_);
}
void WasmCodeManager::InitializeMemoryProtectionKeyForTesting() {
if (memory_protection_key_ == kNoMemoryProtectionKey) {
memory_protection_key_ = AllocateMemoryProtectionKey();
......
......@@ -1042,6 +1042,11 @@ class V8_EXPORT_PRIVATE WasmCodeManager final {
// Returns true if there is PKU support, false otherwise.
bool HasMemoryProtectionKeySupport() const;
// Returns {true} if the memory protection key is write-enabled for the
// current thread.
// Can only be called if {HasMemoryProtectionKeySupport()} is {true}.
bool MemoryProtectionKeyWritable() const;
// This allocates a memory protection key (if none was allocated before),
// independent of the --wasm-memory-protection-keys flag.
void InitializeMemoryProtectionKeyForTesting();
......
......@@ -53,6 +53,9 @@ class MemoryProtectionTest : public TestWithNativeContext {
FLAG_wasm_memory_protection_keys = enable_pku;
if (enable_pku) {
GetWasmCodeManager()->InitializeMemoryProtectionKeyForTesting();
// The key is initially write-protected.
CHECK_IMPLIES(GetWasmCodeManager()->HasMemoryProtectionKeySupport(),
!GetWasmCodeManager()->MemoryProtectionKeyWritable());
}
bool enable_mprotect =
......
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