Introduce PalEnforceAccess
The various Pals were given different meanings in CHECK_CLIENT and non-CHECK_CLIENT builds. This was because it is essential that in the CHECK_CLIENT builds access is prevented, when not requested. This PR separates the CHECK_CLIENT concept from how the Pal should be implemented.
This commit is contained in:
committed by
Matthew Parkinson
parent
b4efc40aa6
commit
55a7ad2d58
@@ -113,7 +113,7 @@ namespace snmalloc
|
||||
{
|
||||
SNMALLOC_ASSERT(is_aligned_block<page_size>(p, size));
|
||||
|
||||
# if defined(SNMALLOC_CHECK_CLIENT) && !defined(NDEBUG)
|
||||
# if !defined(NDEBUG)
|
||||
memset(p, 0x5a, size);
|
||||
# endif
|
||||
|
||||
@@ -126,12 +126,13 @@ namespace snmalloc
|
||||
while (madvise(p, size, MADV_FREE_REUSABLE) == -1 && errno == EAGAIN)
|
||||
;
|
||||
|
||||
# ifdef SNMALLOC_CHECK_CLIENT
|
||||
// This must occur after `MADV_FREE_REUSABLE`.
|
||||
//
|
||||
// `mach_vm_protect` is observably slower in benchmarks.
|
||||
mprotect(p, size, PROT_NONE);
|
||||
# endif
|
||||
if constexpr (PalEnforceAccess)
|
||||
{
|
||||
// This must occur after `MADV_FREE_REUSABLE`.
|
||||
//
|
||||
// `mach_vm_protect` is observably slower in benchmarks.
|
||||
mprotect(p, size, PROT_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -180,12 +181,13 @@ namespace snmalloc
|
||||
}
|
||||
}
|
||||
|
||||
# ifdef SNMALLOC_CHECK_CLIENT
|
||||
// Mark pages as writable for `madvise` below.
|
||||
//
|
||||
// `mach_vm_protect` is observably slower in benchmarks.
|
||||
mprotect(p, size, PROT_READ | PROT_WRITE);
|
||||
# endif
|
||||
if constexpr (PalEnforceAccess)
|
||||
{
|
||||
// Mark pages as writable for `madvise` below.
|
||||
//
|
||||
// `mach_vm_protect` is observably slower in benchmarks.
|
||||
mprotect(p, size, PROT_READ | PROT_WRITE);
|
||||
}
|
||||
|
||||
// `MADV_FREE_REUSE` can only be applied to writable pages,
|
||||
// otherwise it's an error.
|
||||
@@ -205,7 +207,7 @@ namespace snmalloc
|
||||
// Apple's `mmap` doesn't support user-specified alignment and only
|
||||
// guarantees mappings are aligned to the system page size, so we use
|
||||
// `mach_vm_map` instead.
|
||||
template<bool committed>
|
||||
template<bool state_using>
|
||||
static void* reserve_aligned(size_t size) noexcept
|
||||
{
|
||||
SNMALLOC_ASSERT(bits::is_pow2(size));
|
||||
@@ -219,11 +221,9 @@ namespace snmalloc
|
||||
// must be initialized to 0 or addr is interepreted as a lower-bound.
|
||||
mach_vm_address_t addr = 0;
|
||||
|
||||
# ifdef SNMALLOC_CHECK_CLIENT
|
||||
vm_prot_t prot = committed ? VM_PROT_READ | VM_PROT_WRITE : VM_PROT_NONE;
|
||||
# else
|
||||
vm_prot_t prot = VM_PROT_READ | VM_PROT_WRITE;
|
||||
# endif
|
||||
vm_prot_t prot = (state_using || !PalEnforceAccess) ?
|
||||
VM_PROT_READ | VM_PROT_WRITE :
|
||||
VM_PROT_NONE;
|
||||
|
||||
kern_return_t kr = mach_vm_map(
|
||||
mach_task_self(),
|
||||
|
||||
@@ -34,14 +34,16 @@ namespace snmalloc
|
||||
static void notify_not_using(void* p, size_t size) noexcept
|
||||
{
|
||||
SNMALLOC_ASSERT(is_aligned_block<OS::page_size>(p, size));
|
||||
// Call this Pal to simulate the Windows decommit in CI.
|
||||
#if defined(SNMALLOC_CHECK_CLIENT) && !defined(NDEBUG)
|
||||
|
||||
#if !defined(NDEBUG)
|
||||
memset(p, 0x5a, size);
|
||||
#endif
|
||||
madvise(p, size, MADV_FREE);
|
||||
#ifdef SNMALLOC_CHECK_CLIENT
|
||||
mprotect(p, size, PROT_NONE);
|
||||
#endif
|
||||
|
||||
if constexpr (PalEnforceAccess)
|
||||
{
|
||||
mprotect(p, size, PROT_NONE);
|
||||
}
|
||||
}
|
||||
};
|
||||
} // namespace snmalloc
|
||||
|
||||
@@ -28,7 +28,7 @@ namespace snmalloc
|
||||
/**
|
||||
* Reserve memory at a specific alignment.
|
||||
*/
|
||||
template<bool committed>
|
||||
template<bool state_using>
|
||||
static void* reserve_aligned(size_t size) noexcept
|
||||
{
|
||||
// Alignment must be a power of 2.
|
||||
@@ -37,11 +37,8 @@ namespace snmalloc
|
||||
|
||||
int log2align = static_cast<int>(bits::next_pow2_bits(size));
|
||||
|
||||
#ifdef SNMALLOC_CHECK_CLIENT
|
||||
auto prot = committed ? PROT_READ | PROT_WRITE : PROT_NONE;
|
||||
#else
|
||||
auto prot = PROT_READ | PROT_WRITE;
|
||||
#endif
|
||||
auto prot =
|
||||
state_using || !PalEnforceAccess ? PROT_READ | PROT_WRITE : PROT_NONE;
|
||||
|
||||
void* p = mmap(
|
||||
nullptr,
|
||||
|
||||
@@ -6,6 +6,21 @@
|
||||
|
||||
namespace snmalloc
|
||||
{
|
||||
/**
|
||||
* Pal implementations should query this flag to see whether they
|
||||
* are allowed to optimise memory access, or that they must provide
|
||||
* exceptions/segfaults if accesses do not obey the
|
||||
* - using
|
||||
* - using_readonly
|
||||
* - not_using
|
||||
* model.
|
||||
*/
|
||||
#ifdef SNMALLOC_CHECK_CLIENT
|
||||
static constexpr bool PalEnforceAccess = true;
|
||||
#else
|
||||
static constexpr bool PalEnforceAccess = false;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Flags in a bitfield of optional features that a PAL may support. These
|
||||
* should be set in the PAL's `pal_features` static constexpr field.
|
||||
|
||||
@@ -59,7 +59,7 @@ namespace snmalloc
|
||||
::bzero(p, size);
|
||||
}
|
||||
|
||||
template<bool committed>
|
||||
template<bool state_using>
|
||||
static void* reserve_aligned(size_t size) noexcept
|
||||
{
|
||||
SNMALLOC_ASSERT(bits::is_pow2(size));
|
||||
@@ -80,7 +80,7 @@ namespace snmalloc
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
if (committed)
|
||||
if (state_using)
|
||||
{
|
||||
if (
|
||||
kmem_back(kernel_object, addr, size, M_ZERO | M_WAITOK) !=
|
||||
|
||||
@@ -178,17 +178,21 @@ namespace snmalloc
|
||||
static void notify_not_using(void* p, size_t size) noexcept
|
||||
{
|
||||
SNMALLOC_ASSERT(is_aligned_block<OS::page_size>(p, size));
|
||||
#ifdef SNMALLOC_CHECK_CLIENT
|
||||
// Fill memory so that when we switch the pages back on we don't make
|
||||
// assumptions on the content.
|
||||
# if !defined(NDEBUG)
|
||||
memset(p, 0x5a, size);
|
||||
# endif
|
||||
mprotect(p, size, PROT_NONE);
|
||||
#else
|
||||
UNUSED(p);
|
||||
UNUSED(size);
|
||||
|
||||
if constexpr (PalEnforceAccess)
|
||||
{
|
||||
#if !defined(NDEBUG)
|
||||
// Fill memory so that when we switch the pages back on we don't make
|
||||
// assumptions on the content.
|
||||
memset(p, 0x5a, size);
|
||||
#endif
|
||||
mprotect(p, size, PROT_NONE);
|
||||
}
|
||||
else
|
||||
{
|
||||
UNUSED(p);
|
||||
UNUSED(size);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -204,12 +208,13 @@ namespace snmalloc
|
||||
SNMALLOC_ASSERT(
|
||||
is_aligned_block<OS::page_size>(p, size) || (zero_mem == NoZero));
|
||||
|
||||
#ifdef SNMALLOC_CHECK_CLIENT
|
||||
mprotect(p, size, PROT_READ | PROT_WRITE);
|
||||
#else
|
||||
UNUSED(p);
|
||||
UNUSED(size);
|
||||
#endif
|
||||
if constexpr (PalEnforceAccess)
|
||||
mprotect(p, size, PROT_READ | PROT_WRITE);
|
||||
else
|
||||
{
|
||||
UNUSED(p);
|
||||
UNUSED(size);
|
||||
}
|
||||
|
||||
if constexpr (zero_mem == YesZero)
|
||||
zero<true>(p, size);
|
||||
@@ -225,12 +230,13 @@ namespace snmalloc
|
||||
{
|
||||
SNMALLOC_ASSERT(is_aligned_block<OS::page_size>(p, size));
|
||||
|
||||
#ifdef SNMALLOC_CHECK_CLIENT
|
||||
mprotect(p, size, PROT_READ);
|
||||
#else
|
||||
UNUSED(p);
|
||||
UNUSED(size);
|
||||
#endif
|
||||
if constexpr (PalEnforceAccess)
|
||||
mprotect(p, size, PROT_READ);
|
||||
else
|
||||
{
|
||||
UNUSED(p);
|
||||
UNUSED(size);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -286,11 +292,10 @@ namespace snmalloc
|
||||
*/
|
||||
static void* reserve(size_t size) noexcept
|
||||
{
|
||||
#ifdef SNMALLOC_CHECK_CLIENT
|
||||
auto prot = PROT_NONE;
|
||||
#else
|
||||
auto prot = PROT_READ | PROT_WRITE;
|
||||
#endif
|
||||
// If enforcing access, map pages initially as None, and then
|
||||
// add permissions as required. Otherwise, immediately give all
|
||||
// access as this is the most efficient to implement.
|
||||
auto prot = PalEnforceAccess ? PROT_NONE : PROT_READ | PROT_WRITE;
|
||||
|
||||
void* p = mmap(
|
||||
nullptr,
|
||||
|
||||
@@ -158,7 +158,7 @@ namespace snmalloc
|
||||
}
|
||||
|
||||
# ifdef PLATFORM_HAS_VIRTUALALLOC2
|
||||
template<bool committed>
|
||||
template<bool state_using>
|
||||
static void* reserve_aligned(size_t size) noexcept
|
||||
{
|
||||
SNMALLOC_ASSERT(bits::is_pow2(size));
|
||||
@@ -166,7 +166,7 @@ namespace snmalloc
|
||||
|
||||
DWORD flags = MEM_RESERVE;
|
||||
|
||||
if (committed)
|
||||
if (state_using)
|
||||
flags |= MEM_COMMIT;
|
||||
|
||||
// If we're on Windows 10 or newer, we can use the VirtualAlloc2
|
||||
|
||||
Reference in New Issue
Block a user