diff options
author | Jan Vorlicek <janvorli@microsoft.com> | 2019-09-27 10:47:27 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-09-27 10:47:27 +0200 |
commit | d289ccc48f1a4d62cd37be17a35d6c37371b7226 (patch) | |
tree | 5db22455d1cddfe159283b4e32f18a8220eb06f6 /src/pal | |
parent | 281a383d913e7b6013068ec430fc21cf81f43350 (diff) | |
download | coreclr-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.h | 2 | ||||
-rw-r--r-- | src/pal/src/map/map.cpp | 40 | ||||
-rw-r--r-- | src/pal/src/map/virtual.cpp | 38 | ||||
-rw-r--r-- | src/pal/src/misc/utils.cpp | 40 |
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__ |