Skip to content

Commit dc0567c

Browse files
mralephCommit Queue
authored andcommitted
[vm] Rework RX workarounds
* Disable RX workarounds on simulators: they cause host OS to crash; * Remove Mac OS test for RX workarounds: it can cause host OS to crash; * Remove old outdated code paths for OS versions that don't need workarounds. * Add code which detects whether workaround worked or not by intercepting EXC_BAD_ACCESS. * Avoid using vm_remap in FFI callbacks when JITing, it does not work. Instead restore old logic which manually copies page content, but adjust FFI callbacks implementation to respect possible dual mapping. This reworks changes done in commit d194fce after additional testing on a wider range of hardware. TEST=manually on physical devices and simulators Change-Id: I339379386183c532fec25e88fa436ff0b970c2cf Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/435120 Reviewed-by: Martin Kustermann <[email protected]> Commit-Queue: Slava Egorov <[email protected]>
1 parent 807e7d3 commit dc0567c

10 files changed

+465
-323
lines changed

runtime/platform/globals.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,9 @@
125125
#if TARGET_OS_WATCH
126126
#define DART_HOST_OS_WATCH 1
127127
#endif
128+
#if TARGET_OS_SIMULATOR
129+
#define DART_HOST_OS_SIMULATOR 1
130+
#endif
128131

129132
#elif defined(_WIN32)
130133

runtime/platform/utils_macos.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,10 @@ int32_t DarwinVersion();
2222

2323
} // namespace internal
2424

25-
#if defined(DART_HOST_OS_IOS)
25+
// iOS simulators run on underlying Mac OS X kernel, so questions about
26+
// iOS version are meaningless (and return incorrect results because we
27+
// rely on `uname`).
28+
#if defined(DART_HOST_OS_IOS) && !defined(DART_HOST_OS_SIMULATOR)
2629

2730
// Run-time OS version checks.
2831
#define DEFINE_IS_OS_FUNCS(VERSION_NAME, VALUE) \
@@ -51,7 +54,7 @@ DEFINE_IS_OS_FUNCS(10_14)
5154
// current and expected versions.
5255
char* CheckIsAtLeastMinRequiredMacOSXVersion();
5356

54-
#endif // defined(DART_HOST_OS_IOS)
57+
#endif // defined(DART_HOST_OS_IOS) && !defined(DART_HOST_OS_SIMULATOR)
5558

5659
inline uint16_t Utils::HostToBigEndian16(uint16_t value) {
5760
return OSSwapHostToBigInt16(value);

runtime/tests/vm/dart/macos_dual_mapping_smoke_script.dart

Lines changed: 0 additions & 26 deletions
This file was deleted.

runtime/tests/vm/dart/macos_dual_mapping_smoke_test.dart

Lines changed: 0 additions & 91 deletions
This file was deleted.

runtime/tests/vm/vm.status

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -336,9 +336,6 @@ dart/stacktrace_mixin_application_test: SkipByDesign # Relies symbol names in st
336336
[ $compiler == dart2analyzer || $compiler == dart2js ]
337337
dart/data_uri*test: Skip # Data uri's not supported by dart2js or the analyzer.
338338

339-
[ $compiler != dartk || $runtime != vm || $system != macos ]
340-
dart/macos_dual_mapping_smoke: SkipByDesign
341-
342339
[ $mode == debug || $runtime != dart_precompiled || $system == android ]
343340
dart/emit_aot_size_info_flag_test: SkipByDesign # This test is for VM AOT only and is quite slow (so we don't run it in debug mode).
344341
dart/split_aot_kernel_generation2_test: SkipByDesign # This test is for VM AOT only and is quite slow (so we don't run it in debug mode).

runtime/vm/ffi_callback_metadata.cc

Lines changed: 55 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -97,11 +97,17 @@ FfiCallbackMetadata* FfiCallbackMetadata::Instance() {
9797
return singleton_;
9898
}
9999

100+
namespace {
101+
uword RXAreaStart(VirtualMemory* page) {
102+
return page->start() + page->OffsetToExecutableAlias();
103+
}
104+
} // namespace
105+
100106
void FfiCallbackMetadata::FillRuntimeFunction(VirtualMemory* page,
101107
uword index,
102108
void* function) {
103-
void** slot =
104-
reinterpret_cast<void**>(page->start() + RuntimeFunctionOffset(index));
109+
void** slot = reinterpret_cast<void**>(RXAreaStart(page) +
110+
RuntimeFunctionOffset(index));
105111
*slot = function;
106112
}
107113

@@ -113,13 +119,21 @@ VirtualMemory* FfiCallbackMetadata::AllocateTrampolinePage() {
113119
return nullptr;
114120
#else
115121

116-
#if defined(DART_HOST_OS_MACOS) && !defined(DART_PRECOMPILED_RUNTIME)
122+
#if defined(DART_HOST_OS_MACOS) && defined(DART_PRECOMPILED_RUNTIME)
123+
const bool should_remap_stub_page = true;
124+
#else
125+
const bool should_remap_stub_page = false; // No support for remapping.
126+
#endif
127+
128+
#if defined(DART_HOST_OS_MACOS)
117129
// If we are not going to use vm_remap then we need to pass
118-
// is_executable=true so that pages get allocated with MAP_JIT flag if
119-
// necessary. Otherwise OS will kill us with a codesigning violation if
120-
// hardened runtime is enabled.
121-
const bool is_executable = !VirtualMemory::ShouldDualMapExecutablePages();
130+
// is_executable=true so that pages get allocated with MAP_JIT flag or
131+
// using RX workarounds if necessary. Otherwise OS will kill us with a
132+
// codesigning violation if hardened runtime is enabled or we will simply
133+
// not be able to execute trampoline code.
134+
const bool is_executable = !should_remap_stub_page;
122135
#else
136+
// On other operating systems we can simply flip RW->RX as necessary.
123137
const bool is_executable = false;
124138
#endif
125139

@@ -130,17 +144,45 @@ VirtualMemory* FfiCallbackMetadata::AllocateTrampolinePage() {
130144
return nullptr;
131145
}
132146

133-
if (!stub_page_->DuplicateRX(new_page)) {
134-
delete new_page;
135-
return nullptr;
147+
if (should_remap_stub_page) {
148+
#if defined(DART_HOST_OS_MACOS)
149+
if (!stub_page_->DuplicateRX(new_page)) {
150+
delete new_page;
151+
return nullptr;
152+
}
153+
#else
154+
static_assert(!should_remap_stub_page,
155+
"Remaping only supported on Mac OS X");
156+
#endif
157+
} else {
158+
// If we are creating executable mapping then simply fill it with code by
159+
// copying the page.
160+
const intptr_t aligned_size =
161+
Utils::RoundUp(stub_page_->size(), VirtualMemory::PageSize());
162+
ASSERT(new_page->start() >= stub_page_->end() ||
163+
new_page->end() <= stub_page_->start());
164+
memcpy(new_page->address(), stub_page_->address(), // NOLINT
165+
stub_page_->size());
166+
167+
if (VirtualMemory::ShouldDualMapExecutablePages()) {
168+
ASSERT(new_page->OffsetToExecutableAlias() != 0);
169+
VirtualMemory::Protect(new_page->address(), aligned_size,
170+
VirtualMemory::kReadOnly);
171+
VirtualMemory::Protect(
172+
reinterpret_cast<void*>(RXAreaStart(new_page) + RXMappingSize()),
173+
RWMappingSize(), VirtualMemory::kReadWrite);
174+
} else {
175+
VirtualMemory::Protect(new_page->address(), aligned_size,
176+
VirtualMemory::kReadExecute);
177+
}
136178
}
137179

138180
#if !defined(PRODUCT) || defined(FORCE_INCLUDE_DISASSEMBLER)
139181
if (FLAG_support_disassembler && FLAG_disassemble_stubs) {
140182
DisassembleToStdout formatter;
141183
THR_Print("Code for duplicated stub 'FfiCallbackTrampoline' {\n");
142184
const uword code_start =
143-
new_page->start() + offset_of_first_trampoline_in_page_;
185+
RXAreaStart(new_page) + offset_of_first_trampoline_in_page_;
144186
Disassembler::Disassemble(code_start, code_start + kPageSize, &formatter,
145187
/*comments=*/nullptr);
146188
THR_Print("}\n");
@@ -176,8 +218,8 @@ void FfiCallbackMetadata::EnsureFreeListNotEmptyLocked() {
176218

177219
// Add all the trampolines to the free list.
178220
const intptr_t trampolines_per_page = NumCallbackTrampolinesPerPage();
179-
MetadataEntry* metadata_entry =
180-
reinterpret_cast<MetadataEntry*>(new_page->start() + MetadataOffset());
221+
MetadataEntry* metadata_entry = reinterpret_cast<MetadataEntry*>(
222+
RXAreaStart(new_page) + MetadataOffset());
181223
for (intptr_t i = 0; i < trampolines_per_page; ++i) {
182224
AddToFreeListLocked(&metadata_entry[i]);
183225
}

runtime/vm/virtual_memory.cc

Lines changed: 0 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,6 @@
77
#include "platform/assert.h"
88
#include "platform/utils.h"
99

10-
#if defined(DART_HOST_OS_MACOS)
11-
#include <mach/mach.h>
12-
#endif
13-
1410
namespace dart {
1511

1612
bool VirtualMemory::InSamePage(uword address0, uword address1) {
@@ -44,62 +40,4 @@ VirtualMemory* VirtualMemory::ForImagePage(void* pointer, uword size) {
4440
return memory;
4541
}
4642

47-
#if !defined(DART_TARGET_OS_FUCHSIA)
48-
// TODO(52579): Reenable on Fuchsia.
49-
50-
bool VirtualMemory::DuplicateRX(VirtualMemory* target) {
51-
const intptr_t aligned_size = Utils::RoundUp(size(), PageSize());
52-
ASSERT_LESS_OR_EQUAL(aligned_size, target->size());
53-
54-
#if defined(DART_HOST_OS_MACOS)
55-
#if defined(DART_PRECOMPILED_RUNTIME)
56-
const bool should_remap = true;
57-
#else
58-
const bool should_remap = ShouldDualMapExecutablePages();
59-
#endif
60-
61-
if (should_remap) {
62-
// Mac is special cased because iOS doesn't allow allocating new executable
63-
// memory, so the default approach would fail. We are allowed to make new
64-
// mappings of existing executable memory using vm_remap though, which is
65-
// effectively the same for non-writable memory.
66-
const mach_port_t task = mach_task_self();
67-
const vm_address_t source_address =
68-
reinterpret_cast<vm_address_t>(address());
69-
const vm_size_t mem_size = aligned_size;
70-
const vm_prot_t read_execute = VM_PROT_READ | VM_PROT_EXECUTE;
71-
vm_prot_t current_protection = read_execute;
72-
vm_prot_t max_protection = read_execute;
73-
vm_address_t target_address =
74-
reinterpret_cast<vm_address_t>(target->address());
75-
kern_return_t status = vm_remap(
76-
task, &target_address, mem_size,
77-
/*mask=*/0,
78-
/*flags=*/VM_FLAGS_FIXED | VM_FLAGS_OVERWRITE, task, source_address,
79-
/*copy=*/true, &current_protection, &max_protection,
80-
/*inheritance=*/VM_INHERIT_NONE);
81-
if (status != KERN_SUCCESS) {
82-
return false;
83-
}
84-
ASSERT(reinterpret_cast<void*>(target_address) == target->address());
85-
ASSERT_EQUAL(current_protection & read_execute, read_execute);
86-
ASSERT_EQUAL(max_protection & read_execute, read_execute);
87-
return true;
88-
}
89-
#endif // defined(DART_HOST_OS_MACOS)
90-
91-
// TODO(52497): Use dual mapping on platforms where it's supported.
92-
// Check that target doesn't overlap with this.
93-
ASSERT(target->start() >= end() || target->end() <= start());
94-
memcpy(target->address(), address(), size()); // NOLINT
95-
Protect(
96-
target->address(), aligned_size,
97-
VirtualMemory::ShouldDualMapExecutablePages() ? kReadOnly : kReadExecute);
98-
RELEASE_ASSERT(!VirtualMemory::ShouldDualMapExecutablePages() ||
99-
target->OffsetToExecutableAlias() != 0);
100-
return true;
101-
}
102-
103-
#endif // !defined(DART_TARGET_OS_FUCHSIA)
104-
10543
} // namespace dart

0 commit comments

Comments
 (0)