Fix the sandbox use case and add a test. (#269)
Summary of changes: - Add a new PAL that doesn't allocate memory, which can be used with a memory provider that is pre-initialised with a range of memory. - Add a `NoAllocation` PAL property so that the methods on a PAL that doesn't support dynamically reserving address space will never be called and therefore don't need to be implemented. - Slightly refactor the memory provider class so that it has a narrower interface with LargeAlloc and is easier to proxy. - Allow the address space manager and the memory provider to be initialised with a range of memory. This may eventually also remove the need for (or, at least, simplify) the Open Enclave PAL. This commit also ends up with a few other cleanups: - The `malloc_useable_size` CMake test that checks whether the parameter is const qualified was failing on FreeBSD where this function is declared in `malloc_np.h` but where including `malloc.h` raises an error. This should now be more robust. - The BSD aligned PAL inherited from the BSD PAL, which does not expose aligned allocation. This meant that it exposed both the aligned and non-aligned allocation interfaces and so happily accepted incorrect `constexpr` if blocks that expected one or the other but accidentally required both to exist. The unaligned function is now deleted so the same failures that appear in CI should appear locally for anyone using this PAL.
This commit is contained in:
@@ -191,14 +191,14 @@ namespace snmalloc
|
||||
if (res == nullptr)
|
||||
{
|
||||
// Allocation failed ask OS for more memory
|
||||
void* block;
|
||||
size_t block_size;
|
||||
void* block = nullptr;
|
||||
size_t block_size = 0;
|
||||
if constexpr (pal_supports<AlignedAllocation, PAL>)
|
||||
{
|
||||
block_size = PAL::minimum_alloc_size;
|
||||
block = PAL::template reserve_aligned<false>(block_size);
|
||||
}
|
||||
else
|
||||
else if constexpr (!pal_supports<NoAllocation, PAL>)
|
||||
{
|
||||
// Need at least 2 times the space to guarantee alignment.
|
||||
// Hold lock here as a race could cause additional requests to
|
||||
@@ -236,5 +236,21 @@ namespace snmalloc
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Default constructor. An address-space manager constructed in this way
|
||||
* does not own any memory at the start and will request any that it needs
|
||||
* from the PAL.
|
||||
*/
|
||||
AddressSpaceManager() = default;
|
||||
|
||||
/**
|
||||
* Constructor that pre-initialises the address-space manager with a region
|
||||
* of memory.
|
||||
*/
|
||||
AddressSpaceManager(void* base, size_t length)
|
||||
{
|
||||
add_range(base, length);
|
||||
}
|
||||
};
|
||||
} // namespace snmalloc
|
||||
|
||||
@@ -196,6 +196,7 @@ namespace snmalloc
|
||||
UNUSED(sc);
|
||||
|
||||
#ifdef USE_SNMALLOC_STATS
|
||||
SNMALLOC_ASSUME(sc < LARGE_N);
|
||||
large_pop_count[sc]++;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -75,9 +75,6 @@ namespace snmalloc
|
||||
*/
|
||||
std::atomic<size_t> peak_memory_used_bytes{0};
|
||||
|
||||
public:
|
||||
using Pal = PAL;
|
||||
|
||||
/**
|
||||
* Memory current available in large_stacks
|
||||
*/
|
||||
@@ -88,6 +85,51 @@ namespace snmalloc
|
||||
*/
|
||||
ModArray<NUM_LARGE_CLASSES, MPMCStack<Largeslab, RequiresInit>> large_stack;
|
||||
|
||||
public:
|
||||
using Pal = PAL;
|
||||
|
||||
/**
|
||||
* Pop an allocation from a large-allocation stack. This is safe to call
|
||||
* concurrently with other acceses. If there is no large allocation on a
|
||||
* particular stack then this will return `nullptr`.
|
||||
*/
|
||||
SNMALLOC_FAST_PATH void* pop_large_stack(size_t large_class)
|
||||
{
|
||||
void* p = large_stack[large_class].pop();
|
||||
if (p != nullptr)
|
||||
{
|
||||
const size_t rsize = bits::one_at_bit(SUPERSLAB_BITS) << large_class;
|
||||
available_large_chunks_in_bytes -= rsize;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
/**
|
||||
* Push `slab` onto the large-allocation stack associated with the size
|
||||
* class specified by `large_class`. Always succeeds.
|
||||
*/
|
||||
SNMALLOC_FAST_PATH void
|
||||
push_large_stack(Largeslab* slab, size_t large_class)
|
||||
{
|
||||
const size_t rsize = bits::one_at_bit(SUPERSLAB_BITS) << large_class;
|
||||
available_large_chunks_in_bytes += rsize;
|
||||
large_stack[large_class].push(slab);
|
||||
}
|
||||
|
||||
/**
|
||||
* Default constructor. This constructs a memory provider that doesn't yet
|
||||
* own any memory, but which can claim memory from the PAL.
|
||||
*/
|
||||
MemoryProviderStateMixin() = default;
|
||||
|
||||
/**
|
||||
* Construct a memory provider owning some memory. The PAL provided with
|
||||
* memory providers constructed in this way does not have to be able to
|
||||
* allocate memory, if the initial reservation is sufficient.
|
||||
*/
|
||||
MemoryProviderStateMixin(void* start, size_t len)
|
||||
: address_space(start, len)
|
||||
{}
|
||||
/**
|
||||
* Make a new memory provide for this PAL.
|
||||
*/
|
||||
@@ -253,7 +295,7 @@ namespace snmalloc
|
||||
if (large_class == 0)
|
||||
size = rsize;
|
||||
|
||||
void* p = memory_provider.large_stack[large_class].pop();
|
||||
void* p = memory_provider.pop_large_stack(large_class);
|
||||
|
||||
if (p == nullptr)
|
||||
{
|
||||
@@ -265,7 +307,6 @@ namespace snmalloc
|
||||
else
|
||||
{
|
||||
stats.superslab_pop();
|
||||
memory_provider.available_large_chunks_in_bytes -= rsize;
|
||||
|
||||
// Cross-reference alloc.h's large_dealloc decommitment condition.
|
||||
bool decommitted =
|
||||
@@ -323,8 +364,7 @@ namespace snmalloc
|
||||
}
|
||||
|
||||
stats.superslab_push();
|
||||
memory_provider.available_large_chunks_in_bytes += rsize;
|
||||
memory_provider.large_stack[large_class].push(static_cast<Largeslab*>(p));
|
||||
memory_provider.push_large_stack(static_cast<Largeslab*>(p), large_class);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user