Hardened runtime detection enhancements

A new HardenedRuntimeFlags::kDualMapping flag has been introduced to
detect whether dual mapping is provided by the target platform. This
flag can be set even when hardened runtime is not enforced in cases,
in which dual mapping is not available.

This fixes running unit tests on Apple hardware where dual mapping
is not available, but MAP_JIT is (AArch64 hardware).

Additionally, this changeset fixes using -msse2 flag on non-x86
targets, where compiler don't mind "-msse2" flag, but warns about
it. This makes the build 100% clean.
This commit is contained in:
kobalicek
2024-01-20 12:23:11 +01:00
parent 8210620f3e
commit b4b2ff3109
4 changed files with 63 additions and 12 deletions

View File

@@ -686,11 +686,29 @@ if (NOT ASMJIT_EMBED)
if (NOT (ASMJIT_NO_BUILDER OR ASMJIT_NO_COMPILER))
# Vectorcall tests and XMM tests require at least SSE2 in 32-bit mode (in 64-bit mode it's implicit).
set(sse2_flags "")
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC" OR "x${CMAKE_CXX_COMPILER_FRONTEND_VARIANT}" STREQUAL "xMSVC")
asmjit_detect_cflags(sse2_flags "-arch:SSE2")
else()
asmjit_detect_cflags(sse2_flags "-msse2")
# Some compilers don't like passing -msse2 for 64-bit targets, and some compilers targeting non-x86
# would pass "-msse2" compile flag check, but with a warning not detected by CMake. Thus, verify that
# our target is really 32-bit X86 and only use -msse2 or -arch:SSE2 flags when necessary.
set(ASMJIT_SSE2_CFLAGS "")
check_cxx_source_compiles("
#if defined(_M_IX86) || defined(__X86__) || defined(__i386__)
int target_is_32_bit_x86() { return 1; }
#else
// Compile error...
#endif
int main() {
return target_is_32_bit_x86();
}
" ASMJIT_TARGET_IS_32_BIT_X86)
if (ASMJIT_TARGET_IS_32_BIT_X86)
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC" OR "x${CMAKE_CXX_COMPILER_FRONTEND_VARIANT}" STREQUAL "xMSVC")
asmjit_detect_cflags(ASMJIT_SSE2_CFLAGS "-arch:SSE2")
else()
asmjit_detect_cflags(ASMJIT_SSE2_CFLAGS "-msse2")
endif()
endif()
asmjit_add_target(asmjit_test_compiler TEST
SOURCES test/asmjit_test_compiler.cpp
@@ -698,7 +716,7 @@ if (NOT ASMJIT_EMBED)
test/asmjit_test_compiler_a64.cpp
test/asmjit_test_compiler_x86.cpp
LIBRARIES asmjit::asmjit
CFLAGS ${ASMJIT_PRIVATE_CFLAGS} ${sse2_flags}
CFLAGS ${ASMJIT_PRIVATE_CFLAGS} ${ASMJIT_SSE2_CFLAGS}
CFLAGS_DBG ${ASMJIT_PRIVATE_CFLAGS_DBG}
CFLAGS_REL ${ASMJIT_PRIVATE_CFLAGS_REL})
endif()

View File

@@ -1399,11 +1399,12 @@ static void test_jit_allocator_alloc_release() noexcept {
using Opt = JitAllocatorOptions;
VirtMem::HardenedRuntimeInfo hri = VirtMem::hardenedRuntimeInfo();
TestParams testParams[] = {
{ "Default" , Opt::kNone, 0, 0 },
{ "16MB blocks" , Opt::kNone, 16 * 1024 * 1024, 0 },
{ "256B granularity" , Opt::kNone, 0, 256 },
{ "kUseDualMapping" , Opt::kUseDualMapping , 0, 0 },
{ "kUseMultiplePools" , Opt::kUseMultiplePools, 0, 0 },
{ "kFillUnusedMemory" , Opt::kFillUnusedMemory, 0, 0 },
{ "kImmediateRelease" , Opt::kImmediateRelease, 0, 0 },
@@ -1411,6 +1412,7 @@ static void test_jit_allocator_alloc_release() noexcept {
{ "kUseLargePages" , Opt::kUseLargePages, 0, 0 },
{ "kUseLargePages | kFillUnusedMemory" , Opt::kUseLargePages | Opt::kFillUnusedMemory, 0, 0 },
{ "kUseLargePages | kAlignBlockSizeToLargePage", Opt::kUseLargePages | Opt::kAlignBlockSizeToLargePage, 0, 0 },
{ "kUseDualMapping" , Opt::kUseDualMapping , 0, 0 },
{ "kUseDualMapping | kFillUnusedMemory" , Opt::kUseDualMapping | Opt::kFillUnusedMemory, 0, 0 }
};
@@ -1427,6 +1429,12 @@ static void test_jit_allocator_alloc_release() noexcept {
}
for (uint32_t testId = 0; testId < ASMJIT_ARRAY_SIZE(testParams); testId++) {
// Don't try to allocate dual-mapping if dual mapping is not possible - it would fail the test.
if (Support::test(testParams[testId].options, JitAllocatorOptions::kUseDualMapping) &&
!Support::test(hri.flags, VirtMem::HardenedRuntimeFlags::kDualMapping)) {
continue;
}
INFO("JitAllocator(%s)", testParams[testId].name);
JitAllocator::CreateParams params {};

View File

@@ -141,6 +141,11 @@ static size_t detectLargePageSize() noexcept {
return ::GetLargePageMinimum();
}
static bool hasDualMappingSupport() noexcept {
// TODO: This assumption works on X86 platforms, this may not work on AArch64.
return true;
}
// Returns windows-specific protectFlags from \ref MemoryFlags.
static DWORD protectFlagsFromMemoryFlags(MemoryFlags memoryFlags) noexcept {
DWORD protectFlags;
@@ -165,7 +170,12 @@ static DWORD desiredAccessFromMemoryFlags(MemoryFlags memoryFlags) noexcept {
}
static HardenedRuntimeFlags getHardenedRuntimeFlags() noexcept {
return HardenedRuntimeFlags::kNone;
HardenedRuntimeFlags flags = HardenedRuntimeFlags::kNone;
if (hasDualMappingSupport())
flags |= HardenedRuntimeFlags::kDualMapping;
return flags;
}
Error alloc(void** p, size_t size, MemoryFlags memoryFlags) noexcept {
@@ -703,8 +713,8 @@ static bool hasHardenedRuntime() noexcept {
// Detects whether MAP_JIT is available.
static inline bool hasMapJitSupport() noexcept {
#if defined(__APPLE__) && TARGET_OS_OSX && ASMJIT_ARCH_ARM >= 64
// OSX on AArch64 always uses hardened runtime + MAP_JIT:
#if defined(__APPLE__) && TARGET_OS_OSX && ASMJIT_ARCH_X86 == 0
// Apple platforms always use hardened runtime + MAP_JIT on non-x86 hardware:
// - https://developer.apple.com/documentation/apple_silicon/porting_just-in-time_compilers_to_apple_silicon
return true;
#elif defined(__APPLE__) && TARGET_OS_OSX
@@ -746,6 +756,15 @@ static inline int mmMapJitFromMemoryFlags(MemoryFlags memoryFlags) noexcept {
#endif
}
static inline bool hasDualMappingSupport() noexcept {
#if defined(__APPLE__) && TARGET_OS_OSX && ASMJIT_ARCH_X86 == 0
// Apple platforms don't allow dual-mapping on non-x86 hardware.
return false;
#else
return true;
#endif
}
static HardenedRuntimeFlags getHardenedRuntimeFlags() noexcept {
HardenedRuntimeFlags flags = HardenedRuntimeFlags::kNone;
@@ -755,6 +774,9 @@ static HardenedRuntimeFlags getHardenedRuntimeFlags() noexcept {
if (hasMapJitSupport())
flags |= HardenedRuntimeFlags::kMapJit;
if (hasDualMappingSupport())
flags |= HardenedRuntimeFlags::kDualMapping;
return flags;
}

View File

@@ -215,8 +215,11 @@ enum class HardenedRuntimeFlags : uint32_t {
//! architecture.
kEnabled = 0x00000001u,
//! Read+Write+Execute can only be allocated with MAP_JIT flag (Apple specific, only available on OSX).
kMapJit = 0x00000002u
//! Read+Write+Execute can only be allocated with MAP_JIT flag (Apple specific, only available on Apple platforms).
kMapJit = 0x00000002u,
//! Read+Write+Executa can be allocated with dual mapping approach (one region with RW and the other with RX).
kDualMapping = 0x00000004u
};
ASMJIT_DEFINE_ENUM_FLAGS(HardenedRuntimeFlags)