Skip to content

Commit

Permalink
Rework based on PR feedback
Browse files Browse the repository at this point in the history
  • Loading branch information
janvorli committed May 23, 2024
1 parent 42e81bc commit c737b9a
Show file tree
Hide file tree
Showing 5 changed files with 62 additions and 114 deletions.
1 change: 1 addition & 0 deletions src/coreclr/minipal/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
include_directories(.)
include_directories(${CLR_SRC_NATIVE_DIR})
if (CLR_CMAKE_HOST_UNIX)
add_subdirectory(Unix)
else (CLR_CMAKE_HOST_UNIX)
Expand Down
119 changes: 5 additions & 114 deletions src/coreclr/minipal/Unix/doublemapping.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,22 +20,7 @@
#define memfd_create(...) syscall(__NR_memfd_create, __VA_ARGS__)
#endif // TARGET_LINUX && !MFD_CLOEXEC
#include "minipal.h"

#if defined(TARGET_OSX) && defined(TARGET_AMD64)
#include <mach/mach.h>
#include <sys/sysctl.h>

bool IsProcessTranslated()
{
int ret = 0;
size_t size = sizeof(ret);
if (sysctlbyname("sysctl.proc_translated", &ret, &size, NULL, 0) == -1)
{
return false;
}
return ret == 1;
}
#endif // TARGET_OSX && TARGET_AMD64
#include "minipal/cpufeatures.h"

#ifndef TARGET_OSX

Expand All @@ -47,92 +32,14 @@ static const off_t MaxDoubleMappedSize = UINT_MAX;

#endif // TARGET_OSX

#if defined(TARGET_AMD64) && !defined(TARGET_OSX)

extern "C" int VerifyDoubleMapping1();
extern "C" void VerifyDoubleMapping1_End();
extern "C" int VerifyDoubleMapping2();
extern "C" void VerifyDoubleMapping2_End();

// Verify that the double mapping works correctly, including cases when the executable code page is modified after
// the code is executed.
bool VerifyDoubleMapping(int fd)
bool VMToOSInterface::CreateDoubleMemoryMapper(void** pHandle, size_t *pMaxExecutableCodeSize)
{
bool result = false;
void *mapperHandle = (void*)(size_t)fd;
void *pCommittedPage = NULL;
void *pWriteablePage = NULL;
int testCallResult;

typedef int (*VerificationFunctionPtr)();
VerificationFunctionPtr pVerificationFunction;

size_t pageSize = getpagesize();

void *pExecutablePage = VMToOSInterface::ReserveDoubleMappedMemory(mapperHandle, 0, pageSize, NULL, NULL);

if (pExecutablePage == NULL)
{
goto Cleanup;
}

pCommittedPage = VMToOSInterface::CommitDoubleMappedMemory(pExecutablePage, pageSize, true);
if (pCommittedPage == NULL)
{
goto Cleanup;
}

pWriteablePage = VMToOSInterface::GetRWMapping(mapperHandle, pCommittedPage, 0, pageSize);
if (pWriteablePage == NULL)
{
goto Cleanup;
}

// First copy a method of a simple function that returns 1 into the writeable mapping
memcpy(pWriteablePage, (void*)VerifyDoubleMapping1, (char*)VerifyDoubleMapping1_End - (char*)VerifyDoubleMapping1);
pVerificationFunction = (VerificationFunctionPtr)pExecutablePage;
// Invoke the function via the executable mapping. It should return 1.
testCallResult = pVerificationFunction();
if (testCallResult != 1)
{
goto Cleanup;
}

VMToOSInterface::ReleaseRWMapping(pWriteablePage, pageSize);
pWriteablePage = VMToOSInterface::GetRWMapping(mapperHandle, pCommittedPage, 0, pageSize);
if (pWriteablePage == NULL)
{
goto Cleanup;
}

// Now overwrite the first function by a second one that returns 2 using the writeable mapping
memcpy(pWriteablePage, (void*)VerifyDoubleMapping2, (char*)VerifyDoubleMapping2_End - (char*)VerifyDoubleMapping2);
pVerificationFunction = (VerificationFunctionPtr)pExecutablePage;
testCallResult = pVerificationFunction();
// Invoke the function via the executable mapping again. It should return 2 now.
// This doesn't work when running x64 code in docker on macOS Arm64 where the code is not re-translated by Rosetta
if (testCallResult == 2)
{
result = true;
}

Cleanup:
if (pWriteablePage != NULL)
{
VMToOSInterface::ReleaseRWMapping(pWriteablePage, pageSize);
}

if (pExecutablePage != NULL)
if (minipal_detect_emulation())
{
VMToOSInterface::ReleaseDoubleMappedMemory(mapperHandle, pExecutablePage, 0, pageSize);
// Rosetta or QEMU doesn't support double mapping correctly
return false;
}

return result;
}
#endif // TARGET_AMD64 && !TARGET_OSX

bool VMToOSInterface::CreateDoubleMemoryMapper(void** pHandle, size_t *pMaxExecutableCodeSize)
{
#ifndef TARGET_OSX

#ifdef TARGET_FREEBSD
Expand All @@ -158,26 +65,10 @@ bool VMToOSInterface::CreateDoubleMemoryMapper(void** pHandle, size_t *pMaxExecu
return false;
}

#if defined(TARGET_AMD64) && !defined(TARGET_OSX)
if (!VerifyDoubleMapping(fd))
{
close(fd);
return false;
}
#endif // TARGET_AMD64 && !TARGET_OSX

*pMaxExecutableCodeSize = MaxDoubleMappedSize;
*pHandle = (void*)(size_t)fd;
#else // !TARGET_OSX

#ifdef TARGET_AMD64
if (IsProcessTranslated())
{
// Rosetta doesn't support double mapping correctly
return false;
}
#endif // TARGET_AMD64

*pMaxExecutableCodeSize = SIZE_MAX;
*pHandle = NULL;
#endif // !TARGET_OSX
Expand Down
7 changes: 7 additions & 0 deletions src/coreclr/minipal/Windows/doublemapping.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <inttypes.h>
#include <assert.h>
#include "minipal.h"
#include "minipal/cpufeatures.h"

#define HIDWORD(_qw) ((ULONG)((_qw) >> 32))
#define LODWORD(_qw) ((ULONG)(_qw))
Expand Down Expand Up @@ -60,6 +61,12 @@ inline void *GetBotMemoryAddress(void)

bool VMToOSInterface::CreateDoubleMemoryMapper(void **pHandle, size_t *pMaxExecutableCodeSize)
{
if (minipal_detect_emulation())
{
// Rosetta or QEMU doesn't support double mapping correctly
return false;
}

*pMaxExecutableCodeSize = (size_t)MaxDoubleMappedSize;
*pHandle = CreateFileMapping(
INVALID_HANDLE_VALUE, // use paging file
Expand Down
48 changes: 48 additions & 0 deletions src/native/minipal/cpufeatures.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <string.h>

#include "cpufeatures.h"
#include "cpuid.h"
Expand Down Expand Up @@ -473,3 +475,49 @@ int minipal_getcpufeatures(void)

return result;
}

bool minipal_detect_emulation(void)
{
#ifdef HOST_AMD64
// Check for CPU brand indicating emulation
int regs[4];
char brand[49];

// Get the maximum value for extended function CPUID info
__cpuid(regs, (int)0x80000000);
if ((unsigned int)regs[0] < 0x80000004)
{
return false; // Extended CPUID not supported
}

// Retrieve the CPU brand string
for (unsigned int i = 0x80000002; i <= 0x80000004; ++i)
{
__cpuid(regs, (int)i);
memcpy(brand + (i - 0x80000002) * sizeof(regs), regs, sizeof(regs));
}
brand[sizeof(brand) - 1] = '\0';

// Check if CPU brand indicates emulation
if (strstr(brand, "VirtualApple") != NULL || strstr(brand, "QEMU") != NULL)
{
return true;
}
#endif

// Check for process name of PID 1 indicating emulation
char cmdline[256];
FILE *cmdline_file = fopen("/proc/1/cmdline", "r");
if (cmdline_file != NULL)
{
fgets(cmdline, sizeof(cmdline), cmdline_file);
fclose(cmdline_file);

if (strstr(cmdline, "qemu") != NULL || strstr(cmdline, "rosetta") != NULL)
{
return true;
}
}

return false;
}
1 change: 1 addition & 0 deletions src/native/minipal/cpufeatures.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ extern "C"
#endif // __cplusplus

int minipal_getcpufeatures(void);
bool minipal_detect_emulation(void);

#ifdef __cplusplus
}
Expand Down

0 comments on commit c737b9a

Please sign in to comment.