Commit a3a2284e authored by wenqin.yang's avatar wenqin.yang Committed by V8 LUCI CQ

[pku][heap] Support PKUs for V8 heap

This CL adds PKU support for V8 heap, but we will not enable
PKU by default before adding bots that are able to test the
PKU machinery.

Bug: v8:13023
Change-Id: I0465604d56900536ad63311f119ea0324ebe4f2f
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3793944Reviewed-by: 's avatarOmer Katz <omerkatz@chromium.org>
Reviewed-by: 's avatarIgor Sheludko <ishell@chromium.org>
Commit-Queue: Wenqin Yang <wenqin.yang@intel.com>
Reviewed-by: 's avatarClemens Backes <clemensb@chromium.org>
Reviewed-by: 's avatarCamillo Bruni <cbruni@chromium.org>
Reviewed-by: 's avatarLeszek Swirski <leszeks@chromium.org>
Cr-Commit-Position: refs/heads/main@{#82965}
parent 3f9f1eee
...@@ -54,7 +54,7 @@ pkey_mprotect_t pkey_mprotect = nullptr; ...@@ -54,7 +54,7 @@ pkey_mprotect_t pkey_mprotect = nullptr;
pkey_get_t pkey_get = nullptr; pkey_get_t pkey_get = nullptr;
pkey_set_t pkey_set = nullptr; pkey_set_t pkey_set = nullptr;
#ifdef DEBUG #if DEBUG
bool pkey_api_initialized = false; bool pkey_api_initialized = false;
#endif #endif
...@@ -169,38 +169,31 @@ bool MemoryProtectionKey::SetPermissionsAndKey( ...@@ -169,38 +169,31 @@ bool MemoryProtectionKey::SetPermissionsAndKey(
v8::PageAllocator* page_allocator, base::AddressRegion region, v8::PageAllocator* page_allocator, base::AddressRegion region,
v8::PageAllocator::Permission page_permissions, int key) { v8::PageAllocator::Permission page_permissions, int key) {
DCHECK(pkey_api_initialized); DCHECK(pkey_api_initialized);
DCHECK_NE(key, kNoMemoryProtectionKey);
CHECK_NOT_NULL(pkey_mprotect);
void* address = reinterpret_cast<void*>(region.begin()); void* address = reinterpret_cast<void*>(region.begin());
size_t size = region.size(); size_t size = region.size();
if (pkey_mprotect) { // Copied with slight modifications from base/platform/platform-posix.cc
// Copied with slight modifications from base/platform/platform-posix.cc // {OS::SetPermissions()}.
// {OS::SetPermissions()}. // TODO(dlehmann): Move this block into its own function at the right
// TODO(dlehmann): Move this block into its own function at the right // abstraction boundary (likely some static method in platform.h {OS})
// abstraction boundary (likely some static method in platform.h {OS}) // once the whole PKU code is moved into base/platform/.
// once the whole PKU code is moved into base/platform/. DCHECK_EQ(0, region.begin() % page_allocator->CommitPageSize());
DCHECK_EQ(0, region.begin() % page_allocator->CommitPageSize()); DCHECK_EQ(0, size % page_allocator->CommitPageSize());
DCHECK_EQ(0, size % page_allocator->CommitPageSize());
int protection = GetProtectionFromMemoryPermission(page_permissions); int protection = GetProtectionFromMemoryPermission(page_permissions);
int ret = pkey_mprotect(address, size, protection, key); int ret = pkey_mprotect(address, size, protection, key);
if (ret == 0 && page_permissions == PageAllocator::kNoAccess) { if (ret == 0 && page_permissions == PageAllocator::kNoAccess) {
// Similar to {OS::SetPermissions}, also discard the pages after switching // Similar to {OS::SetPermissions}, also discard the pages after switching
// to no access. This is advisory; ignore errors and continue execution. // to no access. This is advisory; ignore errors and continue execution.
USE(page_allocator->DiscardSystemPages(address, size)); USE(page_allocator->DiscardSystemPages(address, size));
}
return ret == /* success */ 0;
} }
// If there is no runtime support for {pkey_mprotect()}, no key should have return ret == /* success */ 0;
// been allocated in the first place.
DCHECK_EQ(kNoMemoryProtectionKey, key);
// Without PKU support, fallback to regular {mprotect()}.
return page_allocator->SetPermissions(address, size, page_permissions);
} }
// static // static
......
...@@ -161,6 +161,7 @@ class ConcurrentBaselineCompiler { ...@@ -161,6 +161,7 @@ class ConcurrentBaselineCompiler {
outgoing_queue_(outcoming_queue) {} outgoing_queue_(outcoming_queue) {}
void Run(JobDelegate* delegate) override { void Run(JobDelegate* delegate) override {
RwxMemoryWriteScope::SetDefaultPermissionsForNewThread();
LocalIsolate local_isolate(isolate_, ThreadKind::kBackground); LocalIsolate local_isolate(isolate_, ThreadKind::kBackground);
UnparkedScope unparked_scope(&local_isolate); UnparkedScope unparked_scope(&local_isolate);
LocalHandleScope handle_scope(&local_isolate); LocalHandleScope handle_scope(&local_isolate);
......
...@@ -1766,6 +1766,7 @@ class MergeAssumptionChecker final : public ObjectVisitor { ...@@ -1766,6 +1766,7 @@ class MergeAssumptionChecker final : public ObjectVisitor {
} // namespace } // namespace
void BackgroundCompileTask::Run() { void BackgroundCompileTask::Run() {
RwxMemoryWriteScope::SetDefaultPermissionsForNewThread();
DCHECK_NE(ThreadId::Current(), isolate_for_local_isolate_->thread_id()); DCHECK_NE(ThreadId::Current(), isolate_for_local_isolate_->thread_id());
LocalIsolate isolate(isolate_for_local_isolate_, ThreadKind::kBackground); LocalIsolate isolate(isolate_for_local_isolate_, ThreadKind::kBackground);
UnparkedScope unparked_scope(&isolate); UnparkedScope unparked_scope(&isolate);
...@@ -2264,6 +2265,7 @@ BackgroundDeserializeTask::BackgroundDeserializeTask( ...@@ -2264,6 +2265,7 @@ BackgroundDeserializeTask::BackgroundDeserializeTask(
} }
void BackgroundDeserializeTask::Run() { void BackgroundDeserializeTask::Run() {
RwxMemoryWriteScope::SetDefaultPermissionsForNewThread();
LocalIsolate isolate(isolate_for_local_isolate_, ThreadKind::kBackground); LocalIsolate isolate(isolate_for_local_isolate_, ThreadKind::kBackground);
UnparkedScope unparked_scope(&isolate); UnparkedScope unparked_scope(&isolate);
LocalHandleScope handle_scope(&isolate); LocalHandleScope handle_scope(&isolate);
......
...@@ -15,6 +15,7 @@ namespace v8 { ...@@ -15,6 +15,7 @@ namespace v8 {
namespace internal { namespace internal {
RwxMemoryWriteScope::RwxMemoryWriteScope(const char* comment) { RwxMemoryWriteScope::RwxMemoryWriteScope(const char* comment) {
DCHECK(is_key_permissions_initialized_for_current_thread());
if (!FLAG_jitless) { if (!FLAG_jitless) {
SetWritable(); SetWritable();
} }
......
...@@ -16,8 +16,15 @@ int RwxMemoryWriteScope::memory_protection_key_ = ...@@ -16,8 +16,15 @@ int RwxMemoryWriteScope::memory_protection_key_ =
base::MemoryProtectionKey::kNoMemoryProtectionKey; base::MemoryProtectionKey::kNoMemoryProtectionKey;
#if DEBUG #if DEBUG
thread_local bool
RwxMemoryWriteScope::is_key_permissions_initialized_for_current_thread_ =
false;
bool RwxMemoryWriteScope::pkey_initialized = false; bool RwxMemoryWriteScope::pkey_initialized = false;
#endif
bool RwxMemoryWriteScope::is_key_permissions_initialized_for_current_thread() {
return is_key_permissions_initialized_for_current_thread_;
}
#endif // DEBUG
void RwxMemoryWriteScope::InitializeMemoryProtectionKey() { void RwxMemoryWriteScope::InitializeMemoryProtectionKey() {
// Flip {pkey_initialized} (in debug mode) and check the new value. // Flip {pkey_initialized} (in debug mode) and check the new value.
...@@ -36,23 +43,55 @@ bool RwxMemoryWriteScope::IsPKUWritable() { ...@@ -36,23 +43,55 @@ bool RwxMemoryWriteScope::IsPKUWritable() {
ResetPKUPermissionsForThreadSpawning::ResetPKUPermissionsForThreadSpawning() { ResetPKUPermissionsForThreadSpawning::ResetPKUPermissionsForThreadSpawning() {
if (!RwxMemoryWriteScope::IsSupported()) return; if (!RwxMemoryWriteScope::IsSupported()) return;
int pkey = RwxMemoryWriteScope::memory_protection_key(); was_writable_ = RwxMemoryWriteScope::IsPKUWritable();
was_writable_ = base::MemoryProtectionKey::GetKeyPermission(pkey) ==
base::MemoryProtectionKey::kNoRestrictions;
if (was_writable_) { if (was_writable_) {
base::MemoryProtectionKey::SetPermissionsForKey( base::MemoryProtectionKey::SetPermissionsForKey(
pkey, base::MemoryProtectionKey::kDisableWrite); RwxMemoryWriteScope::memory_protection_key(),
base::MemoryProtectionKey::kDisableWrite);
} }
} }
ResetPKUPermissionsForThreadSpawning::~ResetPKUPermissionsForThreadSpawning() { ResetPKUPermissionsForThreadSpawning::~ResetPKUPermissionsForThreadSpawning() {
if (!RwxMemoryWriteScope::IsSupported()) return; if (!RwxMemoryWriteScope::IsSupported()) return;
int pkey = RwxMemoryWriteScope::memory_protection_key();
if (was_writable_) { if (was_writable_) {
base::MemoryProtectionKey::SetPermissionsForKey( base::MemoryProtectionKey::SetPermissionsForKey(
pkey, base::MemoryProtectionKey::kNoRestrictions); RwxMemoryWriteScope::memory_protection_key(),
base::MemoryProtectionKey::kNoRestrictions);
} }
} }
void RwxMemoryWriteScope::SetDefaultPermissionsForNewThread() {
// TODO(v8:13023): consider initializing the permissions only once per thread
// if the SetPermissionsForKey() call is too heavy.
if (RwxMemoryWriteScope::IsSupported() &&
base::MemoryProtectionKey::GetKeyPermission(
RwxMemoryWriteScope::memory_protection_key()) ==
base::MemoryProtectionKey::kDisableAccess) {
base::MemoryProtectionKey::SetPermissionsForKey(
RwxMemoryWriteScope::memory_protection_key(),
base::MemoryProtectionKey::kDisableWrite);
}
#if DEBUG
is_key_permissions_initialized_for_current_thread_ = true;
#endif
}
void RwxMemoryWriteScope::SetDefaultPermissionsForSignalHandler() {
DCHECK(pkey_initialized);
DCHECK(is_key_permissions_initialized_for_current_thread_);
if (!RwxMemoryWriteScope::IsSupported()) return;
base::MemoryProtectionKey::SetPermissionsForKey(
memory_protection_key_, base::MemoryProtectionKey::kDisableWrite);
}
#else // !V8_HAS_PKU_JIT_WRITE_PROTECT
void RwxMemoryWriteScope::SetDefaultPermissionsForNewThread() {}
#if DEBUG
bool RwxMemoryWriteScope::is_key_permissions_initialized_for_current_thread() {
return true;
}
#endif // DEBUG
#endif // V8_HAS_PKU_JIT_WRITE_PROTECT #endif // V8_HAS_PKU_JIT_WRITE_PROTECT
RwxMemoryWriteScopeForTesting::RwxMemoryWriteScopeForTesting() RwxMemoryWriteScopeForTesting::RwxMemoryWriteScopeForTesting()
......
...@@ -57,12 +57,25 @@ class V8_NODISCARD RwxMemoryWriteScope { ...@@ -57,12 +57,25 @@ class V8_NODISCARD RwxMemoryWriteScope {
// executable pages. // executable pages.
V8_INLINE static bool IsSupported(); V8_INLINE static bool IsSupported();
// Sets key's permissions to default state (kDisableWrite) for current thread
// if it wasn't done before. V8 doesn't have control of the worker threads
// used by v8::TaskRunner and thus it's not guaranteed that a thread executing
// a V8 task has the right permissions for the key. V8 tasks that access code
// page bodies must call this function to ensure that they have at least read
// access.
V8_EXPORT_PRIVATE static void SetDefaultPermissionsForNewThread();
#if V8_HAS_PKU_JIT_WRITE_PROTECT #if V8_HAS_PKU_JIT_WRITE_PROTECT
static int memory_protection_key() { return memory_protection_key_; } static int memory_protection_key() { return memory_protection_key_; }
static void InitializeMemoryProtectionKey(); static void InitializeMemoryProtectionKey();
static bool IsPKUWritable(); static bool IsPKUWritable();
// Linux resets key's permissions to kDisableAccess before executing signal
// handlers. If the handler requires access to code page bodies it should take
// care of changing permissions to the default state (kDisableWrite).
static void SetDefaultPermissionsForSignalHandler();
#endif // V8_HAS_PKU_JIT_WRITE_PROTECT #endif // V8_HAS_PKU_JIT_WRITE_PROTECT
private: private:
...@@ -85,10 +98,22 @@ class V8_NODISCARD RwxMemoryWriteScope { ...@@ -85,10 +98,22 @@ class V8_NODISCARD RwxMemoryWriteScope {
#if V8_HAS_PKU_JIT_WRITE_PROTECT #if V8_HAS_PKU_JIT_WRITE_PROTECT
static int memory_protection_key_; static int memory_protection_key_;
#endif // V8_HAS_PKU_JIT_WRITE_PROTECT
#if DEBUG #if DEBUG
V8_EXPORT_PRIVATE static bool
is_key_permissions_initialized_for_current_thread();
#if V8_HAS_PKU_JIT_WRITE_PROTECT
// The state of the PKU key permissions are inherited from the parent
// thread when a new thread is created. Since we don't always control
// the parent thread, we ensure that the new thread resets their key's
// permissions to the default kDisableWrite state.
// This flag is used for checking that threads have initialized the
// permissions.
static thread_local bool is_key_permissions_initialized_for_current_thread_;
static bool pkey_initialized; static bool pkey_initialized;
#endif
#endif // V8_HAS_PKU_JIT_WRITE_PROTECT #endif // V8_HAS_PKU_JIT_WRITE_PROTECT
#endif // DEBUG
}; };
// This class is a no-op version of the RwxMemoryWriteScope class above. // This class is a no-op version of the RwxMemoryWriteScope class above.
...@@ -111,8 +136,8 @@ class V8_NODISCARD NopRwxMemoryWriteScope final { ...@@ -111,8 +136,8 @@ class V8_NODISCARD NopRwxMemoryWriteScope final {
class V8_NODISCARD ResetPKUPermissionsForThreadSpawning { class V8_NODISCARD ResetPKUPermissionsForThreadSpawning {
public: public:
#if V8_HAS_PKU_JIT_WRITE_PROTECT #if V8_HAS_PKU_JIT_WRITE_PROTECT
ResetPKUPermissionsForThreadSpawning(); V8_EXPORT_PRIVATE ResetPKUPermissionsForThreadSpawning();
~ResetPKUPermissionsForThreadSpawning(); V8_EXPORT_PRIVATE ~ResetPKUPermissionsForThreadSpawning();
private: private:
bool was_writable_; bool was_writable_;
......
...@@ -284,6 +284,14 @@ using CodeT = Code; ...@@ -284,6 +284,14 @@ using CodeT = Code;
#define V8_HEAP_USE_PTHREAD_JIT_WRITE_PROTECT false #define V8_HEAP_USE_PTHREAD_JIT_WRITE_PROTECT false
#endif #endif
// TODO(v8:13023): enable PKU support when we have a test coverage
#if V8_HAS_PKU_JIT_WRITE_PROTECT && \
!(defined(V8_COMPRESS_POINTERS) && !defined(V8_EXTERNAL_CODE_SPACE))
#define V8_HEAP_USE_PKU_JIT_WRITE_PROTECT false
#else
#define V8_HEAP_USE_PKU_JIT_WRITE_PROTECT false
#endif
// Determine whether tagged pointers are 8 bytes (used in Torque layouts for // Determine whether tagged pointers are 8 bytes (used in Torque layouts for
// choosing where to insert padding). // choosing where to insert padding).
#if V8_TARGET_ARCH_64_BIT && !defined(V8_COMPRESS_POINTERS) #if V8_TARGET_ARCH_64_BIT && !defined(V8_COMPRESS_POINTERS)
......
...@@ -58,6 +58,10 @@ class OptimizingCompileDispatcher::CompileTask : public CancelableTask { ...@@ -58,6 +58,10 @@ class OptimizingCompileDispatcher::CompileTask : public CancelableTask {
dispatcher_->recompilation_delay_)); dispatcher_->recompilation_delay_));
} }
// This task doesn't modify code objects but it needs a read access to the
// code space in order to be able to get a bytecode array from a baseline
// code. See SharedFunctionInfo::GetActiveBytecodeArray() for details.
RwxMemoryWriteScope::SetDefaultPermissionsForNewThread();
dispatcher_->CompileNext(dispatcher_->NextInput(&local_isolate), dispatcher_->CompileNext(dispatcher_->NextInput(&local_isolate),
&local_isolate); &local_isolate);
} }
......
...@@ -4118,6 +4118,8 @@ bool Isolate::Init(SnapshotData* startup_snapshot_data, ...@@ -4118,6 +4118,8 @@ bool Isolate::Init(SnapshotData* startup_snapshot_data,
DCHECK_EQ(create_heap_objects, startup_snapshot_data == nullptr); DCHECK_EQ(create_heap_objects, startup_snapshot_data == nullptr);
DCHECK_EQ(create_heap_objects, shared_heap_snapshot_data == nullptr); DCHECK_EQ(create_heap_objects, shared_heap_snapshot_data == nullptr);
// Code space setup requires the permissions to be set to default state.
RwxMemoryWriteScope::SetDefaultPermissionsForNewThread();
base::ElapsedTimer timer; base::ElapsedTimer timer;
if (create_heap_objects && FLAG_profile_deserialization) timer.Start(); if (create_heap_objects && FLAG_profile_deserialization) timer.Start();
...@@ -4487,6 +4489,7 @@ void Isolate::Enter() { ...@@ -4487,6 +4489,7 @@ void Isolate::Enter() {
} }
} }
RwxMemoryWriteScope::SetDefaultPermissionsForNewThread();
PerIsolateThreadData* data = FindOrAllocatePerThreadDataForThisThread(); PerIsolateThreadData* data = FindOrAllocatePerThreadDataForThisThread();
DCHECK_NOT_NULL(data); DCHECK_NOT_NULL(data);
DCHECK(data->isolate_ == this); DCHECK(data->isolate_ == this);
......
...@@ -669,6 +669,7 @@ void ConcurrentMarking::RunMajor(JobDelegate* delegate, ...@@ -669,6 +669,7 @@ void ConcurrentMarking::RunMajor(JobDelegate* delegate,
base::EnumSet<CodeFlushMode> code_flush_mode, base::EnumSet<CodeFlushMode> code_flush_mode,
unsigned mark_compact_epoch, unsigned mark_compact_epoch,
bool should_keep_ages_unchanged) { bool should_keep_ages_unchanged) {
RwxMemoryWriteScope::SetDefaultPermissionsForNewThread();
size_t kBytesUntilInterruptCheck = 64 * KB; size_t kBytesUntilInterruptCheck = 64 * KB;
int kObjectsUntilInterruptCheck = 1000; int kObjectsUntilInterruptCheck = 1000;
uint8_t task_id = delegate->GetTaskId() + 1; uint8_t task_id = delegate->GetTaskId() + 1;
......
...@@ -616,7 +616,7 @@ AlwaysAllocateScopeForTesting::AlwaysAllocateScopeForTesting(Heap* heap) ...@@ -616,7 +616,7 @@ AlwaysAllocateScopeForTesting::AlwaysAllocateScopeForTesting(Heap* heap)
CodeSpaceMemoryModificationScope::CodeSpaceMemoryModificationScope(Heap* heap) CodeSpaceMemoryModificationScope::CodeSpaceMemoryModificationScope(Heap* heap)
: :
#if V8_HEAP_USE_PTHREAD_JIT_WRITE_PROTECT #if V8_HEAP_USE_PTHREAD_JIT_WRITE_PROTECT || V8_HEAP_USE_PKU_JIT_WRITE_PROTECT
rwx_write_scope_("A part of CodeSpaceMemoryModificationScope"), rwx_write_scope_("A part of CodeSpaceMemoryModificationScope"),
#endif #endif
heap_(heap) { heap_(heap) {
...@@ -679,7 +679,7 @@ CodeSpaceMemoryModificationScope::~CodeSpaceMemoryModificationScope() { ...@@ -679,7 +679,7 @@ CodeSpaceMemoryModificationScope::~CodeSpaceMemoryModificationScope() {
CodePageCollectionMemoryModificationScope:: CodePageCollectionMemoryModificationScope::
CodePageCollectionMemoryModificationScope(Heap* heap) CodePageCollectionMemoryModificationScope(Heap* heap)
: :
#if V8_HEAP_USE_PTHREAD_JIT_WRITE_PROTECT #if V8_HEAP_USE_PTHREAD_JIT_WRITE_PROTECT || V8_HEAP_USE_PKU_JIT_WRITE_PROTECT
rwx_write_scope_("A part of CodePageCollectionMemoryModificationScope"), rwx_write_scope_("A part of CodePageCollectionMemoryModificationScope"),
#endif #endif
heap_(heap) { heap_(heap) {
...@@ -700,7 +700,7 @@ CodePageCollectionMemoryModificationScope:: ...@@ -700,7 +700,7 @@ CodePageCollectionMemoryModificationScope::
#ifdef V8_ENABLE_THIRD_PARTY_HEAP #ifdef V8_ENABLE_THIRD_PARTY_HEAP
CodePageMemoryModificationScope::CodePageMemoryModificationScope(Code code) CodePageMemoryModificationScope::CodePageMemoryModificationScope(Code code)
: :
#if V8_HEAP_USE_PTHREAD_JIT_WRITE_PROTECT #if V8_HEAP_USE_PTHREAD_JIT_WRITE_PROTECT || V8_HEAP_USE_PKU_JIT_WRITE_PROTECT
rwx_write_scope_("A part of CodePageMemoryModificationScope"), rwx_write_scope_("A part of CodePageMemoryModificationScope"),
#endif #endif
chunk_(nullptr), chunk_(nullptr),
...@@ -714,7 +714,7 @@ CodePageMemoryModificationScope::CodePageMemoryModificationScope(Code code) ...@@ -714,7 +714,7 @@ CodePageMemoryModificationScope::CodePageMemoryModificationScope(Code code)
CodePageMemoryModificationScope::CodePageMemoryModificationScope( CodePageMemoryModificationScope::CodePageMemoryModificationScope(
BasicMemoryChunk* chunk) BasicMemoryChunk* chunk)
: :
#if V8_HEAP_USE_PTHREAD_JIT_WRITE_PROTECT #if V8_HEAP_USE_PTHREAD_JIT_WRITE_PROTECT || V8_HEAP_USE_PKU_JIT_WRITE_PROTECT
rwx_write_scope_("A part of CodePageMemoryModificationScope"), rwx_write_scope_("A part of CodePageMemoryModificationScope"),
#endif #endif
chunk_(chunk), chunk_(chunk),
......
...@@ -5532,6 +5532,13 @@ void Heap::SetUpSpaces(LinearAllocationArea& new_allocation_info, ...@@ -5532,6 +5532,13 @@ void Heap::SetUpSpaces(LinearAllocationArea& new_allocation_info,
} }
write_protect_code_memory_ = FLAG_write_protect_code_memory; write_protect_code_memory_ = FLAG_write_protect_code_memory;
#if V8_HEAP_USE_PKU_JIT_WRITE_PROTECT
if (RwxMemoryWriteScope::IsSupported()) {
// If PKU machinery is available then use it instead of conventional
// mprotect.
write_protect_code_memory_ = false;
}
#endif // V8_HEAP_USE_PKU_JIT_WRITE_PROTECT
if (isolate()->shared_isolate()) { if (isolate()->shared_isolate()) {
Heap* shared_heap = isolate()->shared_isolate()->heap(); Heap* shared_heap = isolate()->shared_isolate()->heap();
......
...@@ -2582,7 +2582,7 @@ class V8_NODISCARD CodeSpaceMemoryModificationScope { ...@@ -2582,7 +2582,7 @@ class V8_NODISCARD CodeSpaceMemoryModificationScope {
inline ~CodeSpaceMemoryModificationScope(); inline ~CodeSpaceMemoryModificationScope();
private: private:
#if V8_HEAP_USE_PTHREAD_JIT_WRITE_PROTECT #if V8_HEAP_USE_PTHREAD_JIT_WRITE_PROTECT || V8_HEAP_USE_PKU_JIT_WRITE_PROTECT
V8_NO_UNIQUE_ADDRESS RwxMemoryWriteScope rwx_write_scope_; V8_NO_UNIQUE_ADDRESS RwxMemoryWriteScope rwx_write_scope_;
#endif #endif
Heap* heap_; Heap* heap_;
...@@ -2596,7 +2596,7 @@ class V8_NODISCARD CodePageCollectionMemoryModificationScope { ...@@ -2596,7 +2596,7 @@ class V8_NODISCARD CodePageCollectionMemoryModificationScope {
inline ~CodePageCollectionMemoryModificationScope(); inline ~CodePageCollectionMemoryModificationScope();
private: private:
#if V8_HEAP_USE_PTHREAD_JIT_WRITE_PROTECT #if V8_HEAP_USE_PTHREAD_JIT_WRITE_PROTECT || V8_HEAP_USE_PKU_JIT_WRITE_PROTECT
V8_NO_UNIQUE_ADDRESS RwxMemoryWriteScope rwx_write_scope_; V8_NO_UNIQUE_ADDRESS RwxMemoryWriteScope rwx_write_scope_;
#endif #endif
Heap* heap_; Heap* heap_;
...@@ -2661,7 +2661,7 @@ class V8_NODISCARD CodePageMemoryModificationScope { ...@@ -2661,7 +2661,7 @@ class V8_NODISCARD CodePageMemoryModificationScope {
inline ~CodePageMemoryModificationScope(); inline ~CodePageMemoryModificationScope();
private: private:
#if V8_HEAP_USE_PTHREAD_JIT_WRITE_PROTECT #if V8_HEAP_USE_PTHREAD_JIT_WRITE_PROTECT || V8_HEAP_USE_PKU_JIT_WRITE_PROTECT
V8_NO_UNIQUE_ADDRESS RwxMemoryWriteScope rwx_write_scope_; V8_NO_UNIQUE_ADDRESS RwxMemoryWriteScope rwx_write_scope_;
#endif #endif
BasicMemoryChunk* chunk_; BasicMemoryChunk* chunk_;
......
...@@ -4244,9 +4244,9 @@ void FullEvacuator::RawEvacuatePage(MemoryChunk* chunk, intptr_t* live_bytes) { ...@@ -4244,9 +4244,9 @@ void FullEvacuator::RawEvacuatePage(MemoryChunk* chunk, intptr_t* live_bytes) {
marking_state->live_bytes(chunk)); marking_state->live_bytes(chunk));
break; break;
case kObjectsOldToOld: { case kObjectsOldToOld: {
CodePageHeaderModificationScope rwx_write_scope( RwxMemoryWriteScope rwx_write_scope(
"Clearing of markbits in Code spaces requires write access to " "Evacuation of objects in Code space requires write access for the "
"Code page headers"); "current worker thread.");
const bool success = LiveObjectVisitor::VisitBlackObjects( const bool success = LiveObjectVisitor::VisitBlackObjects(
chunk, marking_state, &old_space_visitor_, chunk, marking_state, &old_space_visitor_,
LiveObjectVisitor::kClearMarkbits, &failed_object); LiveObjectVisitor::kClearMarkbits, &failed_object);
...@@ -4277,6 +4277,7 @@ class PageEvacuationJob : public v8::JobTask { ...@@ -4277,6 +4277,7 @@ class PageEvacuationJob : public v8::JobTask {
tracer_(isolate->heap()->tracer()) {} tracer_(isolate->heap()->tracer()) {}
void Run(JobDelegate* delegate) override { void Run(JobDelegate* delegate) override {
RwxMemoryWriteScope::SetDefaultPermissionsForNewThread();
Evacuator* evacuator = (*evacuators_)[delegate->GetTaskId()].get(); Evacuator* evacuator = (*evacuators_)[delegate->GetTaskId()].get();
if (delegate->IsJoiningThread()) { if (delegate->IsJoiningThread()) {
TRACE_GC(tracer_, evacuator->GetTracingScope()); TRACE_GC(tracer_, evacuator->GetTracingScope());
...@@ -4650,6 +4651,7 @@ class PointersUpdatingJob : public v8::JobTask { ...@@ -4650,6 +4651,7 @@ class PointersUpdatingJob : public v8::JobTask {
background_scope_(background_scope) {} background_scope_(background_scope) {}
void Run(JobDelegate* delegate) override { void Run(JobDelegate* delegate) override {
RwxMemoryWriteScope::SetDefaultPermissionsForNewThread();
if (delegate->IsJoiningThread()) { if (delegate->IsJoiningThread()) {
TRACE_GC(tracer_, scope_); TRACE_GC(tracer_, scope_);
UpdatePointers(delegate); UpdatePointers(delegate);
......
...@@ -734,10 +734,24 @@ bool MemoryAllocator::SetPermissionsOnExecutableMemoryChunk(VirtualMemory* vm, ...@@ -734,10 +734,24 @@ bool MemoryAllocator::SetPermissionsOnExecutableMemoryChunk(VirtualMemory* vm,
if (vm->SetPermissions(pre_guard_page, page_size, if (vm->SetPermissions(pre_guard_page, page_size,
PageAllocator::kNoAccess)) { PageAllocator::kNoAccess)) {
// Commit the executable code body. // Commit the executable code body.
if (vm->SetPermissions( bool set_permission_successed = false;
code_area, area_size, #if V8_HEAP_USE_PKU_JIT_WRITE_PROTECT
jitless ? PageAllocator::kReadWrite if (!jitless && RwxMemoryWriteScope::IsSupported()) {
: MemoryChunk::GetCodeModificationPermission())) { base::AddressRegion region(code_area, area_size);
set_permission_successed =
base::MemoryProtectionKey::SetPermissionsAndKey(
code_page_allocator_, region,
PageAllocator::kReadWriteExecute,
RwxMemoryWriteScope::memory_protection_key());
} else
#endif
{
set_permission_successed = vm->SetPermissions(
code_area, area_size,
jitless ? PageAllocator::kReadWrite
: MemoryChunk::GetCodeModificationPermission());
}
if (set_permission_successed) {
// Create the post-code guard page. // Create the post-code guard page.
if (vm->SetPermissions(post_guard_page, page_size, if (vm->SetPermissions(post_guard_page, page_size,
PageAllocator::kNoAccess)) { PageAllocator::kNoAccess)) {
......
...@@ -199,6 +199,9 @@ ScavengerCollector::JobTask::JobTask( ...@@ -199,6 +199,9 @@ ScavengerCollector::JobTask::JobTask(
promotion_list_(promotion_list) {} promotion_list_(promotion_list) {}
void ScavengerCollector::JobTask::Run(JobDelegate* delegate) { void ScavengerCollector::JobTask::Run(JobDelegate* delegate) {
// The task accesses code pages and thus the permissions must be set to
// default state.
RwxMemoryWriteScope::SetDefaultPermissionsForNewThread();
DCHECK_LT(delegate->GetTaskId(), scavengers_->size()); DCHECK_LT(delegate->GetTaskId(), scavengers_->size());
Scavenger* scavenger = (*scavengers_)[delegate->GetTaskId()].get(); Scavenger* scavenger = (*scavengers_)[delegate->GetTaskId()].get();
if (delegate->IsJoiningThread()) { if (delegate->IsJoiningThread()) {
......
...@@ -68,6 +68,7 @@ class Sweeper::SweeperJob final : public JobTask { ...@@ -68,6 +68,7 @@ class Sweeper::SweeperJob final : public JobTask {
SweeperJob& operator=(const SweeperJob&) = delete; SweeperJob& operator=(const SweeperJob&) = delete;
void Run(JobDelegate* delegate) final { void Run(JobDelegate* delegate) final {
RwxMemoryWriteScope::SetDefaultPermissionsForNewThread();
if (delegate->IsJoiningThread()) { if (delegate->IsJoiningThread()) {
TRACE_GC(tracer_, GCTracer::Scope::MC_SWEEP); TRACE_GC(tracer_, GCTracer::Scope::MC_SWEEP);
RunImpl(delegate); RunImpl(delegate);
......
...@@ -1039,6 +1039,9 @@ class Ticker : public sampler::Sampler { ...@@ -1039,6 +1039,9 @@ class Ticker : public sampler::Sampler {
perThreadData_->thread_id()) || perThreadData_->thread_id()) ||
perThreadData_->thread_state() != nullptr)) perThreadData_->thread_state() != nullptr))
return; return;
#if V8_HEAP_USE_PKU_JIT_WRITE_PROTECT
i::RwxMemoryWriteScope::SetDefaultPermissionsForSignalHandler();
#endif
TickSample sample; TickSample sample;
sample.Init(isolate, state, TickSample::kIncludeCEntryFrame, true); sample.Init(isolate, state, TickSample::kIncludeCEntryFrame, true);
profiler_->Insert(&sample); profiler_->Insert(&sample);
......
...@@ -48,6 +48,9 @@ class CpuSampler : public sampler::Sampler { ...@@ -48,6 +48,9 @@ class CpuSampler : public sampler::Sampler {
ProfilerStats::Reason::kIsolateNotLocked); ProfilerStats::Reason::kIsolateNotLocked);
return; return;
} }
#if V8_HEAP_USE_PKU_JIT_WRITE_PROTECT
i::RwxMemoryWriteScope::SetDefaultPermissionsForSignalHandler();
#endif
TickSample* sample = processor_->StartTickSample(); TickSample* sample = processor_->StartTickSample();
if (sample == nullptr) { if (sample == nullptr) {
ProfilerStats::Instance()->AddReason( ProfilerStats::Instance()->AddReason(
......
...@@ -2178,16 +2178,6 @@ bool WasmCodeManager::MemoryProtectionKeyWritable() { ...@@ -2178,16 +2178,6 @@ bool WasmCodeManager::MemoryProtectionKeyWritable() {
#endif // V8_HAS_PKU_JIT_WRITE_PROTECT #endif // V8_HAS_PKU_JIT_WRITE_PROTECT
} }
// static
void WasmCodeManager::InitializeMemoryProtectionKeyPermissionsIfSupported() {
if (!HasMemoryProtectionKeySupport()) return;
// The default permission is {kDisableAccess}. Switch from that to
// {kDisableWrite}. Leave other permissions untouched, as the thread did
// already use the memory protection key in that case.
RwxMemoryWriteScope initialize_permission_scope(
"For initialization if PKU is in kNoAccess permission case.");
}
base::AddressRegion WasmCodeManager::AllocateAssemblerBufferSpace(int size) { base::AddressRegion WasmCodeManager::AllocateAssemblerBufferSpace(int size) {
#if V8_HAS_PKU_JIT_WRITE_PROTECT #if V8_HAS_PKU_JIT_WRITE_PROTECT
if (MemoryProtectionKeysEnabled()) { if (MemoryProtectionKeysEnabled()) {
......
...@@ -1108,10 +1108,6 @@ class V8_EXPORT_PRIVATE WasmCodeManager final { ...@@ -1108,10 +1108,6 @@ class V8_EXPORT_PRIVATE WasmCodeManager final {
// Can only be called if {HasMemoryProtectionKeySupport()} is {true}. // Can only be called if {HasMemoryProtectionKeySupport()} is {true}.
static bool MemoryProtectionKeyWritable(); static bool MemoryProtectionKeyWritable();
// Initialize the current thread's permissions for the memory protection key,
// if we have support.
static void InitializeMemoryProtectionKeyPermissionsIfSupported();
// Allocate new memory for assembler buffers, potentially protected by PKU. // Allocate new memory for assembler buffers, potentially protected by PKU.
base::AddressRegion AllocateAssemblerBufferSpace(int size); base::AddressRegion AllocateAssemblerBufferSpace(int size);
......
...@@ -1006,12 +1006,6 @@ void WasmEngine::AddIsolate(Isolate* isolate) { ...@@ -1006,12 +1006,6 @@ void WasmEngine::AddIsolate(Isolate* isolate) {
DCHECK_EQ(0, isolates_.count(isolate)); DCHECK_EQ(0, isolates_.count(isolate));
isolates_.emplace(isolate, std::make_unique<IsolateInfo>(isolate)); isolates_.emplace(isolate, std::make_unique<IsolateInfo>(isolate));
// The isolate might access existing (cached) code without ever compiling any.
// In that case, the current thread might still have the default permissions
// for the memory protection key (== no access). Thus initialize the
// permissions now.
WasmCodeManager::InitializeMemoryProtectionKeyPermissionsIfSupported();
// Install sampling GC callback. // Install sampling GC callback.
// TODO(v8:7424): For now we sample module sizes in a GC callback. This will // TODO(v8:7424): For now we sample module sizes in a GC callback. This will
// bias samples towards apps with high memory pressure. We should switch to // bias samples towards apps with high memory pressure. We should switch to
......
...@@ -72,6 +72,7 @@ class ConcurrentAllocationThread final : public v8::base::Thread { ...@@ -72,6 +72,7 @@ class ConcurrentAllocationThread final : public v8::base::Thread {
pending_(pending) {} pending_(pending) {}
void Run() override { void Run() override {
RwxMemoryWriteScope::SetDefaultPermissionsForNewThread();
LocalHeap local_heap(heap_, ThreadKind::kBackground); LocalHeap local_heap(heap_, ThreadKind::kBackground);
UnparkedScope unparked_scope(&local_heap); UnparkedScope unparked_scope(&local_heap);
AllocateSomeObjects(&local_heap); AllocateSomeObjects(&local_heap);
...@@ -450,6 +451,7 @@ class ConcurrentRecordRelocSlotThread final : public v8::base::Thread { ...@@ -450,6 +451,7 @@ class ConcurrentRecordRelocSlotThread final : public v8::base::Thread {
value_(value) {} value_(value) {}
void Run() override { void Run() override {
RwxMemoryWriteScope::SetDefaultPermissionsForNewThread();
LocalHeap local_heap(heap_, ThreadKind::kBackground); LocalHeap local_heap(heap_, ThreadKind::kBackground);
UnparkedScope unparked_scope(&local_heap); UnparkedScope unparked_scope(&local_heap);
// Modification of Code object requires write access. // Modification of Code object requires write access.
...@@ -514,6 +516,10 @@ UNINITIALIZED_TEST(ConcurrentRecordRelocSlot) { ...@@ -514,6 +516,10 @@ UNINITIALIZED_TEST(ConcurrentRecordRelocSlot) {
CHECK(heap->incremental_marking()->marking_state()->IsWhite(value)); CHECK(heap->incremental_marking()->marking_state()->IsWhite(value));
{ {
// TODO(v8:13023): remove ResetPKUPermissionsForThreadSpawning in the
// future when RwxMemoryWriteScope::SetDefaultPermissionsForNewThread() is
// stable.
ResetPKUPermissionsForThreadSpawning thread_scope;
auto thread = auto thread =
std::make_unique<ConcurrentRecordRelocSlotThread>(heap, code, value); std::make_unique<ConcurrentRecordRelocSlotThread>(heap, code, value);
CHECK(thread->Start()); CHECK(thread->Start());
......
...@@ -202,6 +202,7 @@ class JumpTablePatcher : public v8::base::Thread { ...@@ -202,6 +202,7 @@ class JumpTablePatcher : public v8::base::Thread {
jump_table_mutex_(jump_table_mutex) {} jump_table_mutex_(jump_table_mutex) {}
void Run() override { void Run() override {
RwxMemoryWriteScope::SetDefaultPermissionsForNewThread();
TRACE("Patcher %p is starting ...\n", this); TRACE("Patcher %p is starting ...\n", this);
RwxMemoryWriteScopeForTesting rwx_write_scope; RwxMemoryWriteScopeForTesting rwx_write_scope;
Address slot_address = Address slot_address =
......
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