summaryrefslogtreecommitdiff
path: root/src/pal
diff options
context:
space:
mode:
authorJan Vorlicek <janvorli@microsoft.com>2019-09-27 10:47:27 +0200
committerGitHub <noreply@github.com>2019-09-27 10:47:27 +0200
commitd289ccc48f1a4d62cd37be17a35d6c37371b7226 (patch)
tree5db22455d1cddfe159283b4e32f18a8220eb06f6 /src/pal
parent281a383d913e7b6013068ec430fc21cf81f43350 (diff)
downloadcoreclr-d289ccc48f1a4d62cd37be17a35d6c37371b7226.tar.gz
coreclr-d289ccc48f1a4d62cd37be17a35d6c37371b7226.tar.bz2
coreclr-d289ccc48f1a4d62cd37be17a35d6c37371b7226.zip
Port to Release/3.1 - enabling MHR support (#26803)
* Move JIT_WriteBarrier that is modified at runtime to a dynamically allocated memory instead of making a page in libcoreclr.dylib RWX. * Fix JIT_Stelem_Ref calls to JIT_WriteBarrier * Update PAL to add MEM_JIT flag for allocations and reservations of executable memory. * Update native runtime in EH and stack unwinding areas so that it can unwind from the write barrier copy. That code has no unwind info, so without special handling, runtime would not be able to unwind from it.
Diffstat (limited to 'src/pal')
-rw-r--r--src/pal/src/include/pal/utils.h2
-rw-r--r--src/pal/src/map/map.cpp40
-rw-r--r--src/pal/src/map/virtual.cpp38
-rw-r--r--src/pal/src/misc/utils.cpp40
4 files changed, 88 insertions, 32 deletions
diff --git a/src/pal/src/include/pal/utils.h b/src/pal/src/include/pal/utils.h
index f381d957ab..115cf06a62 100644
--- a/src/pal/src/include/pal/utils.h
+++ b/src/pal/src/include/pal/utils.h
@@ -182,6 +182,8 @@ void UTIL_SetLastErrorFromMach(kern_return_t MachReturn);
#endif //HAVE_VM_ALLOCATE
+BOOL IsRunningOnMojaveHardenedRuntime();
+
#ifdef __cplusplus
}
#endif // __cplusplus
diff --git a/src/pal/src/map/map.cpp b/src/pal/src/map/map.cpp
index f6a15f299d..a8d8af2999 100644
--- a/src/pal/src/map/map.cpp
+++ b/src/pal/src/map/map.cpp
@@ -2157,17 +2157,36 @@ MAPmmapAndRecord(
_ASSERTE(pPEBaseAddress != NULL);
PAL_ERROR palError = NO_ERROR;
- LPVOID pvBaseAddress = NULL;
-
off_t adjust = offset & (GetVirtualPageSize() - 1);
+ LPVOID pvBaseAddress = static_cast<char *>(addr) - adjust;
- pvBaseAddress = mmap(static_cast<char *>(addr) - adjust, len + adjust, prot, flags, fd, offset - adjust);
- if (MAP_FAILED == pvBaseAddress)
+#ifdef __APPLE__
+ if ((prot & PROT_EXEC) != 0 && IsRunningOnMojaveHardenedRuntime())
{
- ERROR_(LOADER)( "mmap failed with code %d: %s.\n", errno, strerror( errno ) );
- palError = FILEGetLastErrorFromErrno();
+ // Mojave hardened runtime doesn't allow executable mappings of a file. So we have to create an
+ // anonymous mapping and read the file contents into it instead.
+
+ // Set the requested mapping with forced PROT_WRITE to ensure data from the file can be read there,
+ // read the data in and finally remove the forced PROT_WRITE
+ if ((mprotect(pvBaseAddress, len + adjust, prot | PROT_WRITE) == -1) ||
+ (pread(fd, pvBaseAddress, len + adjust, offset - adjust) == -1) ||
+ (((prot & PROT_WRITE) == 0) && mprotect(pvBaseAddress, len + adjust, prot) == -1))
+ {
+ palError = FILEGetLastErrorFromErrno();
+ }
}
else
+#endif
+ {
+ pvBaseAddress = mmap(static_cast<char *>(addr) - adjust, len + adjust, prot, flags, fd, offset - adjust);
+ if (MAP_FAILED == pvBaseAddress)
+ {
+ ERROR_(LOADER)( "mmap failed with code %d: %s.\n", errno, strerror( errno ) );
+ palError = FILEGetLastErrorFromErrno();
+ }
+ }
+
+ if (NO_ERROR == palError)
{
palError = MAPRecordMapping(pMappingObject, pPEBaseAddress, pvBaseAddress, len, prot);
if (NO_ERROR != palError)
@@ -2359,7 +2378,14 @@ void * MAPMapPEFile(HANDLE hFile)
#endif // FEATURE_ENABLE_NO_ADDRESS_SPACE_RANDOMIZATION
// MAC64 requires we pass MAP_SHARED (or MAP_PRIVATE) flags - otherwise, the call is failed.
// Refer to mmap documentation at http://www.manpagez.com/man/2/mmap/ for details.
- loadedBase = mmap(usedBaseAddr, virtualSize, PROT_NONE, MAP_ANON|MAP_PRIVATE, -1, 0);
+ int mapFlags = MAP_ANON|MAP_PRIVATE;
+#ifdef __APPLE__
+ if (IsRunningOnMojaveHardenedRuntime())
+ {
+ mapFlags |= MAP_JIT;
+ }
+#endif // __APPLE__
+ loadedBase = mmap(usedBaseAddr, virtualSize, PROT_NONE, mapFlags, -1, 0);
}
if (MAP_FAILED == loadedBase)
diff --git a/src/pal/src/map/virtual.cpp b/src/pal/src/map/virtual.cpp
index ff5cde94ad..ca27b7390c 100644
--- a/src/pal/src/map/virtual.cpp
+++ b/src/pal/src/map/virtual.cpp
@@ -916,6 +916,10 @@ static LPVOID VIRTUALReserveMemory(
if (pRetVal == NULL)
{
// Try to reserve memory from the OS
+ if ((flProtect & 0xff) == PAGE_EXECUTE_READWRITE)
+ {
+ flAllocationType |= MEM_RESERVE_EXECUTABLE;
+ }
pRetVal = ReserveVirtualMemory(pthrCurrent, (LPVOID)StartBoundary, MemSize, flAllocationType);
}
@@ -969,24 +973,7 @@ static LPVOID ReserveVirtualMemory(
// Most platforms will only commit memory if it is dirtied,
// so this should not consume too much swap space.
- int mmapFlags = 0;
-
-#if HAVE_VM_ALLOCATE
- // Allocate with vm_allocate first, then map at the fixed address.
- int result = vm_allocate(mach_task_self(),
- &StartBoundary,
- MemSize,
- ((LPVOID) StartBoundary != nullptr) ? FALSE : TRUE);
-
- if (result != KERN_SUCCESS)
- {
- ERROR("vm_allocate failed to allocated the requested region!\n");
- pthrCurrent->SetLastError(ERROR_INVALID_ADDRESS);
- return nullptr;
- }
-
- mmapFlags |= MAP_FIXED;
-#endif // HAVE_VM_ALLOCATE
+ int mmapFlags = MAP_ANON | MAP_PRIVATE;
if ((fAllocationType & MEM_LARGE_PAGES) != 0)
{
@@ -1001,7 +988,12 @@ static LPVOID ReserveVirtualMemory(
#endif
}
- mmapFlags |= MAP_ANON | MAP_PRIVATE;
+#ifdef __APPLE__
+ if ((fAllocationType & MEM_RESERVE_EXECUTABLE) && IsRunningOnMojaveHardenedRuntime())
+ {
+ mmapFlags |= MAP_JIT;
+ }
+#endif
LPVOID pRetVal = mmap((LPVOID) StartBoundary,
MemSize,
@@ -1014,10 +1006,6 @@ static LPVOID ReserveVirtualMemory(
{
ERROR( "Failed due to insufficient memory.\n" );
-#if HAVE_VM_ALLOCATE
- vm_deallocate(mach_task_self(), StartBoundary, MemSize);
-#endif // HAVE_VM_ALLOCATE
-
pthrCurrent->SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return nullptr;
}
@@ -2160,7 +2148,7 @@ void ExecutableMemoryAllocator::TryReserveInitialMemory()
// Do actual memory reservation.
do
{
- m_startAddress = ReserveVirtualMemory(pthrCurrent, (void*)preferredStartAddress, sizeOfAllocation, 0 /* fAllocationType */);
+ m_startAddress = ReserveVirtualMemory(pthrCurrent, (void*)preferredStartAddress, sizeOfAllocation, MEM_RESERVE_EXECUTABLE);
if (m_startAddress != nullptr)
{
break;
@@ -2190,7 +2178,7 @@ void ExecutableMemoryAllocator::TryReserveInitialMemory()
// - The code heap allocator for the JIT can allocate from this address space. Beyond this reservation, one can use
// the COMPlus_CodeHeapReserveForJumpStubs environment variable to reserve space for jump stubs.
sizeOfAllocation = MaxExecutableMemorySize;
- m_startAddress = ReserveVirtualMemory(pthrCurrent, nullptr, sizeOfAllocation, 0 /* fAllocationType */);
+ m_startAddress = ReserveVirtualMemory(pthrCurrent, nullptr, sizeOfAllocation, MEM_RESERVE_EXECUTABLE);
if (m_startAddress == nullptr)
{
return;
diff --git a/src/pal/src/misc/utils.cpp b/src/pal/src/misc/utils.cpp
index 4eefd749ed..f1d1236e08 100644
--- a/src/pal/src/misc/utils.cpp
+++ b/src/pal/src/misc/utils.cpp
@@ -26,6 +26,8 @@ SET_DEFAULT_DEBUG_CHANNEL(MISC); // some headers have code with asserts, so do t
#include <mach/message.h>
#endif //HAVE_VM_ALLOCATE
+#include <sys/mman.h>
+
#include "pal/utils.h"
#include "pal/file.h"
@@ -323,3 +325,41 @@ void UTIL_SetLastErrorFromMach(kern_return_t MachReturn)
}
#endif //HAVE_VM_ALLOCATE
+#ifdef __APPLE__
+
+/*++
+Function:
+ IsRunningOnMojaveHardenedRuntime() - Test if the current process is running on Mojave hardened runtime
+--*/
+BOOL IsRunningOnMojaveHardenedRuntime()
+{
+ static volatile int isRunningOnMojaveHardenedRuntime = -1;
+
+ if (isRunningOnMojaveHardenedRuntime == -1)
+ {
+ BOOL mhrDetected = FALSE;
+ int pageSize = sysconf(_SC_PAGE_SIZE);
+ // Try to map a page with read-write-execute protection. It should fail on Mojave hardened runtime.
+ void* testPage = mmap(NULL, pageSize, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
+ if (testPage == MAP_FAILED && (errno == EACCES))
+ {
+ // The mapping has failed with EACCES, check if making the same mapping with MAP_JIT flag works
+ testPage = mmap(NULL, pageSize, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANONYMOUS | MAP_PRIVATE | MAP_JIT, -1, 0);
+ if (testPage != MAP_FAILED)
+ {
+ mhrDetected = TRUE;
+ }
+ }
+
+ if (testPage != MAP_FAILED)
+ {
+ munmap(testPage, pageSize);
+ }
+
+ isRunningOnMojaveHardenedRuntime = (int)mhrDetected;
+ }
+
+ return (BOOL)isRunningOnMojaveHardenedRuntime;
+}
+
+#endif // __APPLE__