Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 44186df

Browse files
creliercommit-bot@chromium.org
authored andcommitted
[VM runtime] Dual mapping of executable pages.
Change-Id: Iaad78d324e25462ce951f4df26974a6a368c50b7 Reviewed-on: https://dart-review.googlesource.com/c/93377 Commit-Queue: Régis Crelier <[email protected]> Reviewed-by: Ryan Macnak <[email protected]>
1 parent 298866e commit 44186df

18 files changed

+398
-73
lines changed

runtime/tests/vm/vm.status

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
cc/AllocGeneric_Overflow: Crash, Fail # These tests are expected to crash on all platforms.
66
cc/ArrayNew_Overflow_Crash: Crash, Fail # These tests are expected to crash on all platforms.
7+
cc/CodeExecutability: Crash, Fail # These tests are expected to crash on all platforms.
78
cc/CodeImmutability: Crash, Fail # These tests are expected to crash on all platforms.
89
cc/Dart2JSCompileAll: Fail, Crash # Issue 27369
910
cc/Dart2JSCompilerStats: Fail, Crash # Issue 27369
@@ -28,6 +29,7 @@ dart/use_bare_instructions_flag_test: Pass, Slow # Spawns several subprocesses
2829
dart/appjit_cha_deopt_test: Pass, Slow # Quite slow in debug mode, uses --optimization-counter-threshold=100
2930

3031
[ $builder_tag == asan ]
32+
cc/CodeExecutability: Fail, OK # Address Sanitizer turns a crash into a failure.
3133
cc/CodeImmutability: Fail, OK # Address Sanitizer turns a crash into a failure.
3234

3335
[ $builder_tag == optimization_counter_threshold ]

runtime/vm/code_patcher.cc

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,14 @@ namespace dart {
1212

1313
DEFINE_FLAG(bool, write_protect_code, true, "Write protect jitted code");
1414

15+
#if (defined(HOST_OS_LINUX) || defined(HOST_OS_FUCHSIA)) && \
16+
!defined(TARGET_ARCH_IA32) && !defined(TARGET_ARCH_DBC)
17+
DEFINE_FLAG(bool, dual_map_code, true, "Dual map jitted code, RW and RX");
18+
#else
19+
DEFINE_FLAG(bool, dual_map_code, false, "Dual map jitted code, RW and RX");
20+
#endif
21+
22+
#if defined(TARGET_ARCH_IA32) || defined(TARGET_ARCH_DBC)
1523
WritableInstructionsScope::WritableInstructionsScope(uword address,
1624
intptr_t size)
1725
: address_(address), size_(size) {
@@ -27,6 +35,7 @@ WritableInstructionsScope::~WritableInstructionsScope() {
2735
VirtualMemory::kReadExecute);
2836
}
2937
}
38+
#endif // defined(TARGET_ARCH_IA32) || defined(TARGET_ARCH_DBC)
3039

3140
bool MatchesPattern(uword end, const int16_t* pattern, intptr_t size) {
3241
// When breaking within generated code in GDB, it may overwrite individual

runtime/vm/code_patcher.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,12 @@ class RawCode;
1919
class RawFunction;
2020
class RawObject;
2121

22+
#if defined(TARGET_ARCH_IA32) || defined(TARGET_ARCH_DBC)
2223
// Stack-allocated class to create a scope where the specified region
2324
// [address, address + size] has write access enabled. This is used
2425
// when patching generated code. Access is reset to read-execute in
2526
// the destructor of this scope.
27+
// Dual mapping of instructions pages is not supported on these target arch.
2628
class WritableInstructionsScope : public ValueObject {
2729
public:
2830
WritableInstructionsScope(uword address, intptr_t size);
@@ -32,6 +34,7 @@ class WritableInstructionsScope : public ValueObject {
3234
const uword address_;
3335
const intptr_t size_;
3436
};
37+
#endif // defined(TARGET_ARCH_IA32) || defined(TARGET_ARCH_DBC)
3538

3639
class CodePatcher : public AllStatic {
3740
public:

runtime/vm/compiler/relocation.cc

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include "vm/compiler/relocation.h"
66

77
#include "vm/code_patcher.h"
8+
#include "vm/heap/pages.h"
89
#include "vm/instructions.h"
910
#include "vm/object_store.h"
1011
#include "vm/stub_code.h"
@@ -362,8 +363,11 @@ void CodeRelocator::ResolveCallToDestination(UnresolvedCall* unresolved_call,
362363
auto caller = Code::InstructionsOf(unresolved_call->caller);
363364
const int32_t distance = destination_text - call_text_offset;
364365
{
365-
PcRelativeCallPattern call(Instructions::PayloadStart(caller) +
366-
call_offset);
366+
uword addr = Instructions::PayloadStart(caller) + call_offset;
367+
if (FLAG_write_protect_code) {
368+
addr -= HeapPage::Of(caller)->AliasOffset();
369+
}
370+
PcRelativeCallPattern call(addr);
367371
ASSERT(call.IsValid());
368372
call.set_distance(static_cast<int32_t>(distance));
369373
ASSERT(call.distance() == distance);

runtime/vm/heap/marker.cc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,10 @@ class MarkingVisitorBase : public ObjectPointerVisitor {
368368
}
369369

370370
static bool TryAcquireMarkBit(RawObject* raw_obj) {
371+
if (FLAG_write_protect_code && raw_obj->IsInstructions()) {
372+
// A non-writable alias mapping may exist for instruction pages.
373+
raw_obj = HeapPage::ToWritable(raw_obj);
374+
}
371375
if (!sync) {
372376
raw_obj->SetMarkBitUnsynchronized();
373377
return true;

runtime/vm/heap/pages.cc

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -57,11 +57,8 @@ DEFINE_FLAG(bool, log_growth, false, "Log PageSpace growth policy decisions.");
5757
HeapPage* HeapPage::Allocate(intptr_t size_in_words,
5858
PageType type,
5959
const char* name) {
60-
bool is_executable = (type == kExecutable);
61-
// Create the new page executable (RWX) only if we're not in W^X mode
62-
bool create_executable = !FLAG_write_protect_code && is_executable;
6360
VirtualMemory* memory = VirtualMemory::AllocateAligned(
64-
size_in_words << kWordSizeLog2, kPageSize, create_executable, name);
61+
size_in_words << kWordSizeLog2, kPageSize, type == kExecutable, name);
6562
if (memory == NULL) {
6663
return NULL;
6764
}
@@ -214,7 +211,7 @@ void HeapPage::WriteProtect(bool read_only) {
214211

215212
VirtualMemory::Protection prot;
216213
if (read_only) {
217-
if (type_ == kExecutable) {
214+
if ((type_ == kExecutable) && (memory_->AliasOffset() == 0)) {
218215
prot = VirtualMemory::kReadExecute;
219216
} else {
220217
prot = VirtualMemory::kReadOnly;

runtime/vm/heap/pages.h

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@ class HeapPage {
4040
HeapPage* next() const { return next_; }
4141
void set_next(HeapPage* next) { next_ = next; }
4242

43-
bool Contains(uword addr) { return memory_->Contains(addr); }
43+
bool Contains(uword addr) const { return memory_->Contains(addr); }
44+
intptr_t AliasOffset() const { return memory_->AliasOffset(); }
4445

4546
uword object_start() const { return memory_->start() + ObjectStartOffset(); }
4647
uword object_end() const { return object_end_; }
@@ -70,18 +71,54 @@ class HeapPage {
7071
}
7172

7273
// Warning: This does not work for objects on image pages because image pages
73-
// are not aligned.
74+
// are not aligned. However, it works for objects on large pages, because
75+
// only one object is allocated per large page.
7476
static HeapPage* Of(RawObject* obj) {
7577
ASSERT(obj->IsHeapObject());
7678
ASSERT(obj->IsOldObject());
7779
return reinterpret_cast<HeapPage*>(reinterpret_cast<uword>(obj) &
7880
kPageMask);
7981
}
8082

81-
static HeapPage* Of(uintptr_t addr) {
83+
// Warning: This does not work for addresses on image pages or on large pages.
84+
static HeapPage* Of(uword addr) {
8285
return reinterpret_cast<HeapPage*>(addr & kPageMask);
8386
}
8487

88+
// Warning: This does not work for objects on image pages.
89+
static RawObject* ToExecutable(RawObject* obj) {
90+
HeapPage* page = Of(obj);
91+
VirtualMemory* memory = page->memory_;
92+
const intptr_t alias_offset = memory->AliasOffset();
93+
if (alias_offset == 0) {
94+
return obj; // Not aliased.
95+
}
96+
uword addr = RawObject::ToAddr(obj);
97+
if (memory->Contains(addr)) {
98+
return RawObject::FromAddr(addr + alias_offset);
99+
}
100+
// obj is executable.
101+
ASSERT(memory->ContainsAlias(addr));
102+
return obj;
103+
}
104+
105+
// Warning: This does not work for objects on image pages.
106+
static RawObject* ToWritable(RawObject* obj) {
107+
HeapPage* page = Of(obj);
108+
VirtualMemory* memory = page->memory_;
109+
const intptr_t alias_offset = memory->AliasOffset();
110+
if (alias_offset == 0) {
111+
return obj; // Not aliased.
112+
}
113+
uword addr = RawObject::ToAddr(obj);
114+
if (memory->ContainsAlias(addr)) {
115+
return RawObject::FromAddr(addr - alias_offset);
116+
}
117+
// obj is writable.
118+
ASSERT(memory->Contains(addr));
119+
return obj;
120+
}
121+
85122
// 1 card = 128 slots.
86123
static const intptr_t kSlotsPerCardLog2 = 7;
87124
static const intptr_t kBytesPerCardLog2 = kWordSizeLog2 + kSlotsPerCardLog2;

runtime/vm/heap/verifier.cc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,10 @@ void VerifyPointersVisitor::VisitPointers(RawObject** first, RawObject** last) {
4949
RawObject* raw_obj = *current;
5050
if (raw_obj->IsHeapObject()) {
5151
if (!allocated_set_->Contains(raw_obj)) {
52+
if (raw_obj->IsInstructions() &&
53+
allocated_set_->Contains(HeapPage::ToWritable(raw_obj))) {
54+
continue;
55+
}
5256
uword raw_addr = RawObject::ToAddr(raw_obj);
5357
FATAL1("Invalid object pointer encountered %#" Px "\n", raw_addr);
5458
}

runtime/vm/object.cc

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2019,7 +2019,12 @@ RawError* Object::Init(Isolate* isolate,
20192019
bool Object::InVMHeap() const {
20202020
if (FLAG_verify_handles && raw()->IsVMHeapObject()) {
20212021
Heap* vm_isolate_heap = Dart::vm_isolate()->heap();
2022-
ASSERT(vm_isolate_heap->Contains(RawObject::ToAddr(raw())));
2022+
uword addr = RawObject::ToAddr(raw());
2023+
if (!vm_isolate_heap->Contains(addr)) {
2024+
ASSERT(FLAG_write_protect_code);
2025+
addr = RawObject::ToAddr(HeapPage::ToWritable(raw()));
2026+
ASSERT(vm_isolate_heap->Contains(addr));
2027+
}
20232028
}
20242029
return raw()->IsVMHeapObject();
20252030
}
@@ -2080,8 +2085,12 @@ void Object::CheckHandle() const {
20802085
Isolate* isolate = Isolate::Current();
20812086
Heap* isolate_heap = isolate->heap();
20822087
Heap* vm_isolate_heap = Dart::vm_isolate()->heap();
2083-
ASSERT(isolate_heap->Contains(RawObject::ToAddr(raw_)) ||
2084-
vm_isolate_heap->Contains(RawObject::ToAddr(raw_)));
2088+
uword addr = RawObject::ToAddr(raw_);
2089+
if (!isolate_heap->Contains(addr) && !vm_isolate_heap->Contains(addr)) {
2090+
ASSERT(FLAG_write_protect_code);
2091+
addr = RawObject::ToAddr(HeapPage::ToWritable(raw_));
2092+
ASSERT(isolate_heap->Contains(addr) || vm_isolate_heap->Contains(addr));
2093+
}
20852094
}
20862095
}
20872096
#endif
@@ -14569,6 +14578,24 @@ RawCode* Code::FinalizeCode(FlowGraphCompiler* compiler,
1456914578
object->raw());
1457014579
}
1457114580

14581+
// Write protect instructions and, if supported by OS, use dual mapping
14582+
// for execution.
14583+
if (FLAG_write_protect_code) {
14584+
uword address = RawObject::ToAddr(instrs.raw());
14585+
// Check if a dual mapping exists.
14586+
instrs = Instructions::RawCast(HeapPage::ToExecutable(instrs.raw()));
14587+
uword exec_address = RawObject::ToAddr(instrs.raw());
14588+
if (exec_address != address) {
14589+
VirtualMemory::Protect(reinterpret_cast<void*>(address),
14590+
instrs.raw()->HeapSize(),
14591+
VirtualMemory::kReadOnly);
14592+
address = exec_address;
14593+
}
14594+
VirtualMemory::Protect(reinterpret_cast<void*>(address),
14595+
instrs.raw()->HeapSize(),
14596+
VirtualMemory::kReadExecute);
14597+
}
14598+
1457214599
// Hook up Code and Instructions objects.
1457314600
code.SetActiveInstructions(instrs);
1457414601
code.set_instructions(instrs);
@@ -14579,13 +14606,6 @@ RawCode* Code::FinalizeCode(FlowGraphCompiler* compiler,
1457914606
code.set_object_pool(object_pool->raw());
1458014607
}
1458114608

14582-
if (FLAG_write_protect_code) {
14583-
uword address = RawObject::ToAddr(instrs.raw());
14584-
VirtualMemory::Protect(reinterpret_cast<void*>(address),
14585-
instrs.raw()->HeapSize(),
14586-
VirtualMemory::kReadExecute);
14587-
}
14588-
1458914609
#if defined(DART_PRECOMPILER)
1459014610
if (stats != nullptr) {
1459114611
stats->Finalize();

runtime/vm/object.h

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4652,6 +4652,7 @@ class ExceptionHandlers : public Object {
46524652

46534653
class Code : public Object {
46544654
public:
4655+
// When dual mapping, this returns the executable view.
46554656
RawInstructions* active_instructions() const {
46564657
#if defined(DART_PRECOMPILED_RUNTIME)
46574658
UNREACHABLE();
@@ -4661,6 +4662,7 @@ class Code : public Object {
46614662
#endif
46624663
}
46634664

4665+
// When dual mapping, these return the executable view.
46644666
RawInstructions* instructions() const { return raw_ptr()->instructions_; }
46654667
static RawInstructions* InstructionsOf(const RawCode* code) {
46664668
return code->ptr()->instructions_;
@@ -5141,6 +5143,7 @@ class Code : public Object {
51415143

51425144
FINAL_HEAP_OBJECT_IMPLEMENTATION(Code, Object);
51435145
friend class Class;
5146+
friend class CodeTestHelper;
51445147
friend class SnapshotWriter;
51455148
friend class StubCode; // for set_object_pool
51465149
friend class Precompiler; // for set_object_pool
@@ -9139,8 +9142,12 @@ DART_FORCE_INLINE void Object::SetRaw(RawObject* value) {
91399142
Isolate* isolate = Isolate::Current();
91409143
Heap* isolate_heap = isolate->heap();
91419144
Heap* vm_isolate_heap = Dart::vm_isolate()->heap();
9142-
ASSERT(isolate_heap->Contains(RawObject::ToAddr(raw_)) ||
9143-
vm_isolate_heap->Contains(RawObject::ToAddr(raw_)));
9145+
uword addr = RawObject::ToAddr(raw_);
9146+
if (!isolate_heap->Contains(addr) && !vm_isolate_heap->Contains(addr)) {
9147+
ASSERT(FLAG_write_protect_code);
9148+
addr = RawObject::ToAddr(HeapPage::ToWritable(raw_));
9149+
ASSERT(isolate_heap->Contains(addr) || vm_isolate_heap->Contains(addr));
9150+
}
91449151
}
91459152
#endif
91469153
}

0 commit comments

Comments
 (0)