Skip to content

Commit c42cc0d

Browse files
committed
[jsc] Add option to force the address of JIT memory
https://bugs.webkit.org/show_bug.cgi?id=297560 rdar://158636737 Reviewed by Yusuke Suzuki and Mark Lam. The new JSC_jitMemoryReservationAddress option allows us to specify an address at which to mmap JIT memory. If the operating system is not able to do so, then we will instead crash. Notably we do not use MAP_FIXED as we don't want to clobber existing mappings, but would rather bail out if someone else has already mapped the location requested. Canonical link: https://commits.webkit.org/299073@main
1 parent 2c5db7f commit c42cc0d

12 files changed

Lines changed: 85 additions & 71 deletions

File tree

‎Source/JavaScriptCore/jit/ExecutableAllocator.cpp‎

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -389,20 +389,23 @@ static ALWAYS_INLINE JITReservation initializeJITPageReservation()
389389
jit_heap_runtime_config.max_segregated_object_size = 0;
390390
#endif
391391

392-
auto tryCreatePageReservation = [] (size_t reservationSize) {
392+
auto tryCreatePageReservation = [] (size_t reservationSize, uintptr_t hintAddress) {
393393
#if OS(LINUX)
394394
// On Linux, if we use uncommitted reservation, mmap operation is recorded with small page size in perf command's output.
395395
// This makes the following JIT code logging broken and some of JIT code is not recorded correctly.
396396
// To avoid this problem, we use committed reservation if we need perf JITDump logging.
397397
if (Options::useJITDump())
398-
return PageReservation::tryReserveAndCommitWithGuardPages(reservationSize, OSAllocator::JSJITCodePages, EXECUTABLE_POOL_WRITABLE, true, false);
398+
return PageReservation::tryReserveWithGuardPages(reservationSize, OSAllocator::JSJITCodePages, hintAddress, EXECUTABLE_POOL_WRITABLE, true, true, false);
399399
#endif
400400
if (Options::useJITCage() && JSC_ALLOW_JIT_CAGE_SPECIFIC_RESERVATION)
401-
return PageReservation::tryReserve(reservationSize, OSAllocator::JSJITCodePages, EXECUTABLE_POOL_WRITABLE, true, Options::useJITCage());
402-
return PageReservation::tryReserveWithGuardPages(reservationSize, OSAllocator::JSJITCodePages, EXECUTABLE_POOL_WRITABLE, true, false);
401+
return PageReservation::tryReserve(reservationSize, OSAllocator::JSJITCodePages, hintAddress, EXECUTABLE_POOL_WRITABLE, true, false, Options::useJITCage());
402+
return PageReservation::tryReserveWithGuardPages(reservationSize, OSAllocator::JSJITCodePages, hintAddress, EXECUTABLE_POOL_WRITABLE, true, false, false);
403403
};
404404

405-
reservation.pageReservation = tryCreatePageReservation(reservation.size);
405+
406+
reservation.pageReservation = tryCreatePageReservation(reservation.size, Options::jitMemoryReservationAddress());
407+
if (Options::jitMemoryReservationAddress())
408+
RELEASE_ASSERT(std::bit_cast<uintptr_t>(reservation.pageReservation.base()) == Options::jitMemoryReservationAddress() && "Failed to accomodate JSC_jitMemoryReservationAddress");
406409

407410
if (Options::verboseExecutablePoolAllocation())
408411
dataLog(getpid(), ": Got executable pool reservation at ", RawPointer(reservation.pageReservation.base()), "...", RawPointer(reservation.pageReservation.end()), ", while I'm at ", RawPointer(reinterpret_cast<void*>(initializeJITPageReservation)), "\n");

‎Source/JavaScriptCore/runtime/OptionsList.h‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ bool hasCapacityToUseLargeGigacage();
9999
v(Bool, crashOnDisallowedVMEntry, ASSERT_ENABLED, Normal, "Forces a crash if we attempt to enter the VM when disallowed"_s) \
100100
v(Bool, crashIfCantAllocateJITMemory, false, Normal, nullptr) \
101101
v(Unsigned, jitMemoryReservationSize, 0, Normal, "Set this number to change the executable allocation size in ExecutableAllocatorFixedVMPool. (In bytes.)"_s) \
102+
v(Size, jitMemoryReservationAddress, 0, Restricted, "If non-zero, we will attempt to allocate JIT memory at the address provided and crash if we cannot.") \
102103
\
103104
v(Bool, forceCodeBlockLiveness, false, Normal, nullptr) \
104105
v(Bool, forceICFailure, false, Normal, nullptr) \

‎Source/WTF/wtf/MallocSpan.h‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,9 +109,9 @@ template<typename T, typename Malloc = FastMalloc> class MallocSpan {
109109
}
110110

111111
#if HAVE(MMAP)
112-
static MallocSpan mmap(size_t sizeInBytes, int pageProtection, int options, int fileDescriptor)
112+
static MallocSpan mmap(void* addr, size_t sizeInBytes, int pageProtection, int options, int fileDescriptor)
113113
{
114-
return MallocSpan { static_cast<T*>(Malloc::mmap(sizeInBytes, pageProtection, options, fileDescriptor)), sizeInBytes };
114+
return MallocSpan { static_cast<T*>(Malloc::mmap(addr, sizeInBytes, pageProtection, options, fileDescriptor)), sizeInBytes };
115115
}
116116
#endif
117117

‎Source/WTF/wtf/Mmap.h‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,9 @@
3333
namespace WTF {
3434

3535
struct Mmap {
36-
static void* mmap(size_t size, int pageProtection, int options, int fileDescriptor)
36+
static void* mmap(void* addr, size_t size, int pageProtection, int options, int fileDescriptor)
3737
{
38-
auto* data = ::mmap(0, size, pageProtection, options, fileDescriptor, 0);
38+
auto* data = ::mmap(addr, size, pageProtection, options, fileDescriptor, 0);
3939
return data == MAP_FAILED ? nullptr : data;
4040
}
4141

‎Source/WTF/wtf/OSAllocator.h‎

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -43,14 +43,14 @@ class OSAllocator {
4343
// The memory returned by this cannot be released as on Windows there's no guaranteed API to
4444
// get an aligned address and the size + alignment then rounding trick cannot release the unused parts
4545
// due to how the Windows syscalls work.
46-
WTF_EXPORT_PRIVATE static void* tryReserveUncommittedAligned(size_t size, size_t alignment, Usage = UnknownUsage, bool writable = true, bool executable = false, bool jitCageEnabled = false, bool includesGuardPages = false);
46+
WTF_EXPORT_PRIVATE static void* tryReserveUncommittedAligned(size_t, size_t alignment, Usage = UnknownUsage, void* address = nullptr, bool writable = true, bool executable = false, bool jitCageEnabled = false, bool includesGuardPages = false);
4747

4848
// These methods are symmetric; reserveUncommitted allocates VM in an uncommitted state,
4949
// releaseDecommitted should be called on a region of VM allocated by a single reservation,
5050
// the memory must all currently be in a decommitted state. reserveUncommitted returns to
5151
// you memory that is zeroed.
52-
WTF_EXPORT_PRIVATE static void* reserveUncommitted(size_t, Usage = UnknownUsage, bool writable = true, bool executable = false, bool jitCageEnabled = false, bool includesGuardPages = false);
53-
WTF_EXPORT_PRIVATE static void* tryReserveUncommitted(size_t, Usage = UnknownUsage, bool writable = true, bool executable = false, bool jitCageEnabled = false, bool includesGuardPages = false);
52+
WTF_EXPORT_PRIVATE static void* reserveUncommitted(size_t, Usage = UnknownUsage, void* address = nullptr, bool writable = true, bool executable = false, bool jitCageEnabled = false, bool includesGuardPages = false);
53+
WTF_EXPORT_PRIVATE static void* tryReserveUncommitted(size_t, Usage = UnknownUsage, void* address = nullptr, bool writable = true, bool executable = false, bool jitCageEnabled = false, bool includesGuardPages = false);
5454
WTF_EXPORT_PRIVATE static void releaseDecommitted(void*, size_t);
5555

5656
// These methods are symmetric; they commit or decommit a region of VM (uncommitted VM should
@@ -62,14 +62,14 @@ class OSAllocator {
6262
// These methods are symmetric; reserveAndCommit allocates VM in an committed state,
6363
// decommitAndRelease should be called on a region of VM allocated by a single reservation,
6464
// the memory must all currently be in a committed state.
65-
WTF_EXPORT_PRIVATE static void* reserveAndCommit(size_t, Usage = UnknownUsage, bool writable = true, bool executable = false, bool jitCageEnabled = false, bool includesGuardPages = false);
66-
WTF_EXPORT_PRIVATE static void* tryReserveAndCommit(size_t, Usage = UnknownUsage, bool writable = true, bool executable = false, bool jitCageEnabled = false, bool includesGuardPages = false);
65+
WTF_EXPORT_PRIVATE static void* reserveAndCommit(size_t, Usage = UnknownUsage, void* address = nullptr, bool writable = true, bool executable = false, bool jitCageEnabled = false, bool includesGuardPages = false);
66+
WTF_EXPORT_PRIVATE static void* tryReserveAndCommit(size_t, Usage = UnknownUsage, void* address = nullptr, bool writable = true, bool executable = false, bool jitCageEnabled = false, bool includesGuardPages = false);
6767
static void decommitAndRelease(void* base, size_t size);
6868

6969
// These methods are akin to reserveAndCommit/decommitAndRelease, above - however rather than
7070
// committing/decommitting the entire region additional parameters allow a subregion to be
7171
// specified.
72-
WTF_EXPORT_PRIVATE static void* reserveAndCommit(size_t reserveSize, size_t commitSize, Usage = UnknownUsage, bool writable = true, bool executable = false, bool jitCageEnabled = false);
72+
WTF_EXPORT_PRIVATE static void* reserveAndCommit(size_t reserveSize, size_t commitSize, Usage = UnknownUsage, void* address = nullptr, bool writable = true, bool executable = false, bool jitCageEnabled = false);
7373

7474
// Reallocate an existing, committed allocation.
7575
// The prior allocation must be fully comitted, and the new size will also be fully committed.
@@ -84,9 +84,9 @@ class OSAllocator {
8484
WTF_EXPORT_PRIVATE static bool tryProtect(void*, size_t, bool readable, bool writable);
8585
};
8686

87-
inline void* OSAllocator::reserveAndCommit(size_t reserveSize, size_t commitSize, Usage usage, bool writable, bool executable, bool jitCageEnabled)
87+
inline void* OSAllocator::reserveAndCommit(size_t reserveSize, size_t commitSize, Usage usage, void* address, bool writable, bool executable, bool jitCageEnabled)
8888
{
89-
void* base = reserveUncommitted(reserveSize, usage, writable, executable, jitCageEnabled);
89+
void* base = reserveUncommitted(reserveSize, usage, address, writable, executable, jitCageEnabled);
9090
commit(base, commitSize, writable, executable);
9191
return base;
9292
}
@@ -99,7 +99,7 @@ inline void OSAllocator::decommitAndRelease(void* releaseBase, size_t releaseSiz
9999
template<typename T>
100100
inline T* OSAllocator::reallocateCommitted(T* oldBase, size_t oldSize, size_t newSize, Usage usage, bool writable, bool executable, bool jitCageEnabled)
101101
{
102-
void* newBase = reserveAndCommit(newSize, usage, writable, executable, jitCageEnabled);
102+
void* newBase = reserveAndCommit(newSize, usage, nullptr, writable, executable, jitCageEnabled);
103103
memcpy(newBase, oldBase, std::min(oldSize, newSize));
104104
decommitAndRelease(oldBase, oldSize);
105105
return static_cast<T*>(newBase);

‎Source/WTF/wtf/PageAllocation.h‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ class PageAllocation : private PageBlock {
8484
static PageAllocation allocate(size_t size, OSAllocator::Usage usage = OSAllocator::UnknownUsage, bool writable = true, bool executable = false)
8585
{
8686
ASSERT(isPageAligned(size));
87-
return PageAllocation(OSAllocator::reserveAndCommit(size, usage, writable, executable), size);
87+
return PageAllocation(OSAllocator::reserveAndCommit(size, usage, nullptr, writable, executable), size);
8888
}
8989

9090
void deallocate()

‎Source/WTF/wtf/PageReservation.h‎

Lines changed: 26 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -89,41 +89,48 @@ class PageReservation : private PageBlock {
8989
return m_committed;
9090
}
9191

92-
static PageReservation reserve(size_t size, OSAllocator::Usage usage = OSAllocator::UnknownUsage, bool writable = true, bool executable = false, bool jitCageEnabled = false)
92+
static PageReservation reserve(size_t size, OSAllocator::Usage usage = OSAllocator::UnknownUsage, std::optional<uintptr_t> address = { }, bool writable = true, bool executable = false, bool commit = false, bool jitCageEnabled = false)
9393
{
9494
ASSERT(isPageAligned(size));
95-
return PageReservation(OSAllocator::reserveUncommitted(size, usage, writable, executable, jitCageEnabled, false), size, writable, executable, jitCageEnabled, false);
95+
ASSERT(isPageAligned(address.value_or(0)));
96+
if (commit)
97+
return PageReservation(OSAllocator::reserveAndCommit(size, usage, std::bit_cast<void*>(address.value_or(0)), writable, executable, jitCageEnabled, false), size, writable, executable, jitCageEnabled, false);
98+
return PageReservation(OSAllocator::reserveUncommitted(size, usage, std::bit_cast<void*>(address.value_or(0)), writable, executable, jitCageEnabled, false), size, writable, executable, jitCageEnabled, false);
9699
}
97100

98-
static PageReservation tryReserve(size_t size, OSAllocator::Usage usage = OSAllocator::UnknownUsage, bool writable = true, bool executable = false, bool jitCageEnabled = false)
101+
static PageReservation tryReserve(size_t size, OSAllocator::Usage usage = OSAllocator::UnknownUsage, std::optional<uintptr_t> address = { }, bool writable = true, bool executable = false, bool commit = false, bool jitCageEnabled = false)
99102
{
100103
ASSERT(isPageAligned(size));
101-
return PageReservation(OSAllocator::tryReserveUncommitted(size, usage, writable, executable, jitCageEnabled, false), size, writable, executable, jitCageEnabled, false);
104+
ASSERT(isPageAligned(address.value_or(0)));
105+
if (commit)
106+
return PageReservation(OSAllocator::tryReserveAndCommit(size, usage, std::bit_cast<void*>(address.value_or(0)), writable, executable, jitCageEnabled, false), size, writable, executable, jitCageEnabled, false);
107+
return PageReservation(OSAllocator::tryReserveUncommitted(size, usage, std::bit_cast<void*>(address.value_or(0)), writable, executable, jitCageEnabled, false), size, writable, executable, jitCageEnabled, false);
102108
}
103109

104-
static PageReservation reserveWithGuardPages(size_t size, OSAllocator::Usage usage = OSAllocator::UnknownUsage, bool writable = true, bool executable = false, bool jitCageEnabled = false)
110+
static PageReservation reserveWithGuardPages(size_t size, OSAllocator::Usage usage = OSAllocator::UnknownUsage, std::optional<uintptr_t> address = { }, bool writable = true, bool executable = false, bool commit = false, bool jitCageEnabled = false)
105111
{
106112
ASSERT(isPageAligned(size));
107-
return PageReservation(OSAllocator::reserveUncommitted(size + pageSize() * 2, usage, writable, executable, jitCageEnabled, true), size, writable, executable, jitCageEnabled, true);
113+
ASSERT(isPageAligned(address.value_or(0)));
114+
// Account for guard pages
115+
if (address.has_value() && address.value() >= pageSize())
116+
address.value() -= pageSize();
117+
if (commit)
118+
return PageReservation(OSAllocator::reserveAndCommit(size + pageSize() * 2, usage, std::bit_cast<void*>(address.value_or(0)), writable, executable, jitCageEnabled, true), size, writable, executable, jitCageEnabled, true);
119+
return PageReservation(OSAllocator::reserveUncommitted(size + pageSize() * 2, usage, std::bit_cast<void*>(address.value_or(0)), writable, executable, jitCageEnabled, true), size, writable, executable, jitCageEnabled, true);
108120
}
109121

110-
static PageReservation tryReserveWithGuardPages(size_t size, OSAllocator::Usage usage = OSAllocator::UnknownUsage, bool writable = true, bool executable = false, bool jitCageEnabled = false)
122+
static PageReservation tryReserveWithGuardPages(size_t size, OSAllocator::Usage usage = OSAllocator::UnknownUsage, std::optional<uintptr_t> address = { }, bool writable = true, bool executable = false, bool commit = false, bool jitCageEnabled = false)
111123
{
112124
ASSERT(isPageAligned(size));
113-
return PageReservation(OSAllocator::tryReserveUncommitted(size + pageSize() * 2, usage, writable, executable, jitCageEnabled, true), size, writable, executable, jitCageEnabled, true);
125+
ASSERT(isPageAligned(address.value_or(0)));
126+
// Account for guard pages
127+
if (address.has_value() && address.value() >= pageSize())
128+
address.value() -= pageSize();
129+
if (commit)
130+
return PageReservation(OSAllocator::tryReserveAndCommit(size + pageSize() * 2, usage, std::bit_cast<void*>(address.value_or(0)), writable, executable, jitCageEnabled, true), size, writable, executable, jitCageEnabled, true);
131+
return PageReservation(OSAllocator::tryReserveUncommitted(size + pageSize() * 2, usage, std::bit_cast<void*>(address.value_or(0)), writable, executable, jitCageEnabled, true), size, writable, executable, jitCageEnabled, true);
114132
}
115133

116-
static PageReservation reserveAndCommitWithGuardPages(size_t size, OSAllocator::Usage usage = OSAllocator::UnknownUsage, bool writable = true, bool executable = false, bool jitCageEnabled = false)
117-
{
118-
ASSERT(isPageAligned(size));
119-
return PageReservation(OSAllocator::reserveAndCommit(size + pageSize() * 2, usage, writable, executable, jitCageEnabled, true), size, writable, executable, jitCageEnabled, true);
120-
}
121-
122-
static PageReservation tryReserveAndCommitWithGuardPages(size_t size, OSAllocator::Usage usage = OSAllocator::UnknownUsage, bool writable = true, bool executable = false, bool jitCageEnabled = false)
123-
{
124-
ASSERT(isPageAligned(size));
125-
return PageReservation(OSAllocator::tryReserveAndCommit(size + pageSize() * 2, usage, writable, executable, jitCageEnabled, true), size, writable, executable, jitCageEnabled, true);
126-
}
127134

128135
void deallocate()
129136
{

‎Source/WTF/wtf/playstation/OSAllocatorPlayStation.cpp‎

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,12 @@
3838

3939
namespace WTF {
4040

41-
void* OSAllocator::tryReserveAndCommit(size_t bytes, Usage usage, bool writable, bool executable, bool jitCageEnabled, bool includesGuardPages)
41+
void* OSAllocator::tryReserveAndCommit(size_t bytes, Usage usage, void* address, bool writable, bool executable, bool jitCageEnabled, bool includesGuardPages)
4242
{
4343
ASSERT_UNUSED(includesGuardPages, !includesGuardPages);
4444
ASSERT_UNUSED(jitCageEnabled, !jitCageEnabled);
4545
ASSERT_UNUSED(executable, !executable);
46+
ASSERT_UNUSED(address, !address);
4647

4748
void* result = memory_extra::vss::reserve(bytes);
4849
if (!result)
@@ -57,13 +58,14 @@ void* OSAllocator::tryReserveAndCommit(size_t bytes, Usage usage, bool writable,
5758
return result;
5859
}
5960

60-
void* OSAllocator::tryReserveUncommitted(size_t bytes, Usage usage, bool writable, bool executable, bool jitCageEnabled, bool includesGuardPages)
61+
void* OSAllocator::tryReserveUncommitted(size_t bytes, Usage usage, void* address, bool writable, bool executable, bool jitCageEnabled, bool includesGuardPages)
6162
{
6263
UNUSED_PARAM(usage);
6364
UNUSED_PARAM(writable);
6465
ASSERT_UNUSED(includesGuardPages, !includesGuardPages);
6566
ASSERT_UNUSED(jitCageEnabled, !jitCageEnabled);
6667
ASSERT_UNUSED(executable, !executable);
68+
ASSERT_UNUSED(address, !address);
6769

6870
void* result = memory_extra::vss::reserve(bytes);
6971
if (!result)
@@ -72,18 +74,19 @@ void* OSAllocator::tryReserveUncommitted(size_t bytes, Usage usage, bool writabl
7274
return result;
7375
}
7476

75-
void* OSAllocator::reserveUncommitted(size_t bytes, Usage usage, bool writable, bool executable, bool jitCageEnabled, bool includesGuardPages)
77+
void* OSAllocator::reserveUncommitted(size_t bytes, Usage usage, void* address, bool writable, bool executable, bool jitCageEnabled, bool includesGuardPages)
7678
{
77-
void* result = tryReserveUncommitted(bytes, usage, writable, executable, jitCageEnabled, includesGuardPages);
79+
void* result = tryReserveUncommitted(bytes, usage, address, writable, executable, jitCageEnabled, includesGuardPages);
7880
RELEASE_ASSERT(result);
7981
return result;
8082
}
8183

82-
void* OSAllocator::tryReserveUncommittedAligned(size_t bytes, size_t alignment, Usage usage, bool writable, bool executable, bool jitCageEnabled, bool includesGuardPages)
84+
void* OSAllocator::tryReserveUncommittedAligned(size_t bytes, size_t alignment, Usage usage, void* address, bool writable, bool executable, bool jitCageEnabled, bool includesGuardPages)
8385
{
8486
ASSERT_UNUSED(includesGuardPages, !includesGuardPages);
8587
ASSERT_UNUSED(jitCageEnabled, !jitCageEnabled);
8688
ASSERT_UNUSED(executable, !executable);
89+
ASSERT_UNUSED(address, !address);
8790
UNUSED_PARAM(usage);
8891
UNUSED_PARAM(writable);
8992
ASSERT(hasOneBitSet(alignment) && alignment >= pageSize());
@@ -95,9 +98,9 @@ void* OSAllocator::tryReserveUncommittedAligned(size_t bytes, size_t alignment,
9598
return result;
9699
}
97100

98-
void* OSAllocator::reserveAndCommit(size_t bytes, Usage usage, bool writable, bool executable, bool jitCageEnabled, bool includesGuardPages)
101+
void* OSAllocator::reserveAndCommit(size_t bytes, Usage usage, void* address, bool writable, bool executable, bool jitCageEnabled, bool includesGuardPages)
99102
{
100-
void* result = tryReserveAndCommit(bytes, usage, writable, executable, jitCageEnabled, includesGuardPages);
103+
void* result = tryReserveAndCommit(bytes, usage, address, writable, executable, jitCageEnabled, includesGuardPages);
101104
RELEASE_ASSERT(result);
102105
return result;
103106
}

‎Source/WTF/wtf/posix/FileHandlePOSIX.cpp‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ std::optional<MappedFileData> FileHandle::map(MappedFileMode mapMode, FileOpenMo
182182
#endif
183183
}
184184

185-
auto fileData = MallocSpan<uint8_t, Mmap>::mmap(size, pageProtection, MAP_FILE | (mapMode == MappedFileMode::Shared ? MAP_SHARED : MAP_PRIVATE), platformHandle());
185+
auto fileData = MallocSpan<uint8_t, Mmap>::mmap(nullptr, size, pageProtection, MAP_FILE | (mapMode == MappedFileMode::Shared ? MAP_SHARED : MAP_PRIVATE), platformHandle());
186186
if (!fileData)
187187
return { };
188188

0 commit comments

Comments
 (0)