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

[wasm] Add unit tests for code protection

This adds some basic tests for WebAssembly code protection, in four
different configurations:
- no protection
- mprotect-based protection
- PKU-based protection
- PKU with fallback to mprotect

If PKU is not supported by the OS or hardware, then PKU is identical to
no protection, and PKU with fallback is identical to mprotect. We always
execute all four configurations anyway.
If protection is effective, we expect code to be writable within a
{CodeSpaceWriteScope}, and not writable otherwise. When trying to write
to non-writable code, we expect a crash of the process (checked via
{ASSERT_DEATH_IF_SUPPORTED}).

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

Bug: v8:11974
Cq-Include-Trybots: luci.v8.try:v8_mac_arm64_rel_ng
Cq-Include-Trybots: luci.v8.try:v8_mac_arm64_dbg_ng
Change-Id: I4ec0ce9426f70ff41a292b9ea25be1e8956a670e
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3138210
Commit-Queue: Clemens Backes <clemensb@chromium.org>
Reviewed-by: 's avatarJakob Kummerow <jkummerow@chromium.org>
Cr-Commit-Position: refs/heads/main@{#76726}
parent eac21d57
......@@ -48,6 +48,7 @@ class StreamingDecoder;
class WasmCode;
struct WasmModule;
V8_EXPORT_PRIVATE
std::shared_ptr<NativeModule> CompileToNativeModule(
Isolate* isolate, const WasmFeatures& enabled, ErrorThrower* thrower,
std::shared_ptr<const WasmModule> module, const ModuleWireBytes& wire_bytes,
......
......@@ -2124,6 +2124,12 @@ bool WasmCodeManager::HasMemoryProtectionKeySupport() const {
return memory_protection_key_ != kNoMemoryProtectionKey;
}
void WasmCodeManager::InitializeMemoryProtectionKeyForTesting() {
if (memory_protection_key_ == kNoMemoryProtectionKey) {
memory_protection_key_ = AllocateMemoryProtectionKey();
}
}
std::shared_ptr<NativeModule> WasmCodeManager::NewNativeModule(
Isolate* isolate, const WasmFeatures& enabled, size_t code_size_estimate,
std::shared_ptr<const WasmModule> module) {
......
......@@ -549,7 +549,7 @@ class WasmCodeAllocator {
// Make a code region writable. Only allowed if there is at lease one writer
// (see above).
// Hold the {NativeModule}'s {allocation_mutex_} when calling this method.
void MakeWritable(base::AddressRegion);
V8_EXPORT_PRIVATE void MakeWritable(base::AddressRegion);
// Free memory pages of all given code objects. Used for wasm code GC.
// Hold the {NativeModule}'s {allocation_mutex_} when calling this method.
......@@ -1038,6 +1038,10 @@ class V8_EXPORT_PRIVATE WasmCodeManager final {
// Returns true if there is PKU support, false otherwise.
bool HasMemoryProtectionKeySupport() const;
// This allocates a memory protection key (if none was allocated before),
// independent of the --wasm-memory-protection-keys flag.
void InitializeMemoryProtectionKeyForTesting();
private:
friend class WasmCodeAllocator;
friend class WasmEngine;
......@@ -1065,7 +1069,7 @@ class V8_EXPORT_PRIVATE WasmCodeManager final {
// and updated after each GC.
std::atomic<size_t> critical_committed_code_space_;
const int memory_protection_key_;
int memory_protection_key_;
mutable base::Mutex native_modules_mutex_;
......
......@@ -416,6 +416,7 @@ v8_source_set("unittests_sources") {
"wasm/leb-helper-unittest.cc",
"wasm/liftoff-register-unittests.cc",
"wasm/loop-assignment-analysis-unittest.cc",
"wasm/memory-protection-unittest.cc",
"wasm/module-decoder-memory64-unittest.cc",
"wasm/module-decoder-unittest.cc",
"wasm/simd-shuffle-unittest.cc",
......
// 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/flags/flags.h"
#include "src/wasm/code-space-access.h"
#include "src/wasm/module-compiler.h"
#include "src/wasm/module-decoder.h"
#include "src/wasm/wasm-engine.h"
#include "src/wasm/wasm-features.h"
#include "src/wasm/wasm-opcodes.h"
#include "test/common/wasm/wasm-macro-gen.h"
#include "test/unittests/test-utils.h"
namespace v8 {
namespace internal {
namespace wasm {
enum MemoryProtectionMode {
kNoProtection,
kPku,
kMprotect,
kPkuWithMprotectFallback
};
const char* MemoryProtectionModeToString(MemoryProtectionMode mode) {
switch (mode) {
case kNoProtection:
return "NoProtection";
case kPku:
return "Pku";
case kMprotect:
return "Mprotect";
case kPkuWithMprotectFallback:
return "PkuWithMprotectFallback";
}
}
class MemoryProtectionTest : public TestWithNativeContext {
public:
void Initialize(MemoryProtectionMode mode) {
mode_ = mode;
bool enable_pku = mode == kPku || mode == kPkuWithMprotectFallback;
FLAG_wasm_memory_protection_keys = enable_pku;
if (enable_pku) {
GetWasmCodeManager()->InitializeMemoryProtectionKeyForTesting();
}
bool enable_mprotect =
mode == kMprotect || mode == kPkuWithMprotectFallback;
FLAG_wasm_write_protect_code_memory = enable_mprotect;
}
void CompileModule() {
CHECK_NULL(native_module_);
native_module_ = CompileNativeModule();
code_ = native_module_->GetCode(0);
}
NativeModule* native_module() const { return native_module_.get(); }
WasmCode* code() const { return code_; }
bool code_is_protected() {
return V8_HAS_PTHREAD_JIT_WRITE_PROTECT || has_pku() || has_mprotect();
}
void MakeCodeWritable() {
native_module_->MakeWritable(base::AddressRegionOf(code_->instructions()));
}
void WriteToCode() { code_->instructions()[0] = 0; }
private:
bool has_pku() {
bool param_has_pku = mode_ == kPku || mode_ == kPkuWithMprotectFallback;
return param_has_pku &&
GetWasmCodeManager()->HasMemoryProtectionKeySupport();
}
bool has_mprotect() {
return mode_ == kMprotect || mode_ == kPkuWithMprotectFallback;
}
std::shared_ptr<NativeModule> CompileNativeModule() {
// Define the bytes for a module with a single empty function.
static const byte module_bytes[] = {
WASM_MODULE_HEADER, SECTION(Type, ENTRY_COUNT(1), SIG_ENTRY_v_v),
SECTION(Function, ENTRY_COUNT(1), SIG_INDEX(0)),
SECTION(Code, ENTRY_COUNT(1), ADD_COUNT(0 /* locals */, kExprEnd))};
ModuleResult result =
DecodeWasmModule(WasmFeatures::All(), std::begin(module_bytes),
std::end(module_bytes), false, kWasmOrigin,
isolate()->counters(), isolate()->metrics_recorder(),
v8::metrics::Recorder::ContextId::Empty(),
DecodingMethod::kSync, GetWasmEngine()->allocator());
CHECK(result.ok());
Handle<FixedArray> export_wrappers;
ErrorThrower thrower(isolate(), "");
constexpr int kNoCompilationId = 0;
std::shared_ptr<NativeModule> native_module = CompileToNativeModule(
isolate(), WasmFeatures::All(), &thrower, std::move(result).value(),
ModuleWireBytes{base::ArrayVector(module_bytes)}, &export_wrappers,
kNoCompilationId);
CHECK(!thrower.error());
CHECK_NOT_NULL(native_module);
return native_module;
}
MemoryProtectionMode mode_;
std::shared_ptr<NativeModule> native_module_;
WasmCodeRefScope code_refs_;
WasmCode* code_;
};
class ParameterizedMemoryProtectionTest
: public MemoryProtectionTest,
public ::testing::WithParamInterface<MemoryProtectionMode> {
public:
void SetUp() override { Initialize(GetParam()); }
};
#define ASSERT_DEATH_IF_PROTECTED(code) \
if (code_is_protected()) { \
ASSERT_DEATH_IF_SUPPORTED(code, ""); \
} else { \
code; \
}
std::string PrintMemoryProtectionTestParam(
::testing::TestParamInfo<MemoryProtectionMode> info) {
return MemoryProtectionModeToString(info.param);
}
INSTANTIATE_TEST_SUITE_P(MemoryProtection, ParameterizedMemoryProtectionTest,
::testing::Values(kNoProtection, kPku, kMprotect,
kPkuWithMprotectFallback),
PrintMemoryProtectionTestParam);
TEST_P(ParameterizedMemoryProtectionTest, CodeNotWritableAfterCompilation) {
CompileModule();
ASSERT_DEATH_IF_PROTECTED(WriteToCode());
}
TEST_P(ParameterizedMemoryProtectionTest, CodeWritableWithinScope) {
CompileModule();
CodeSpaceWriteScope write_scope(native_module());
MakeCodeWritable();
WriteToCode();
}
TEST_P(ParameterizedMemoryProtectionTest, CodeNotWritableAfterScope) {
CompileModule();
{
CodeSpaceWriteScope write_scope(native_module());
MakeCodeWritable();
WriteToCode();
}
ASSERT_DEATH_IF_PROTECTED(WriteToCode());
}
} // namespace wasm
} // namespace internal
} // namespace v8
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