Skip to content

Commit

Permalink
Fix the sandbox use case and add a test. (#269)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
davidchisnall authored Jan 11, 2021
1 parent 4837c82 commit c33f355
Show file tree
Hide file tree
Showing 10 changed files with 427 additions and 11 deletions.
11 changes: 11 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,16 @@ option(SNMALLOC_OPTIMISE_FOR_CURRENT_MACHINE "Compile for current machine archit
set(CACHE_FRIENDLY_OFFSET OFF CACHE STRING "Base offset to place linked-list nodes.")
set(SNMALLOC_STATIC_LIBRARY_PREFIX "sn_" CACHE STRING "Static library function prefix")

# malloc.h will error if you include it on FreeBSD, so this test must not
# unconditionally include it.
CHECK_C_SOURCE_COMPILES("
#if __has_include(<malloc_np.h>)
#include <malloc_np.h>
#if __has_include(<malloc/malloc.h>)
#include <malloc/malloc.h>
#else
#include <malloc.h>
#endif
size_t malloc_usable_size(const void* ptr) { return 0; }
int main() { return 0; }
" CONST_QUALIFIED_MALLOC_USABLE_SIZE)
Expand Down Expand Up @@ -307,6 +315,9 @@ if(NOT DEFINED SNMALLOC_ONLY_HEADER_LIBRARY)
if (${SUPER_SLAB_SIZE} STREQUAL "malloc")
target_compile_definitions(${TESTNAME} PRIVATE SNMALLOC_PASS_THROUGH)
endif()
if(CONST_QUALIFIED_MALLOC_USABLE_SIZE)
target_compile_definitions(${TESTNAME} PRIVATE -DMALLOC_USABLE_SIZE_QUALIFIER=const)
endif()
target_link_libraries(${TESTNAME} snmalloc_lib)
if (${TEST} MATCHES "release-.*")
message(STATUS "Adding test: ${TESTNAME} only for release configs")
Expand Down
22 changes: 19 additions & 3 deletions src/mem/address_space.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
1 change: 1 addition & 0 deletions src/mem/allocstats.h
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,7 @@ namespace snmalloc
UNUSED(sc);

#ifdef USE_SNMALLOC_STATS
SNMALLOC_ASSUME(sc < LARGE_N);
large_pop_count[sc]++;
#endif
}
Expand Down
54 changes: 47 additions & 7 deletions src/mem/largealloc.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand All @@ -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.
*/
Expand Down Expand Up @@ -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)
{
Expand All @@ -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 =
Expand Down Expand Up @@ -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);
}
};

Expand Down
1 change: 1 addition & 0 deletions src/pal/pal.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
# include "pal_haiku.h"
# include "pal_linux.h"
# include "pal_netbsd.h"
# include "pal_noalloc.h"
# include "pal_openbsd.h"
# include "pal_solaris.h"
# include "pal_windows.h"
Expand Down
12 changes: 12 additions & 0 deletions src/pal/pal_bsd_aligned.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,5 +50,17 @@ namespace snmalloc

return p;
}

/**
* Explicitly deleted method for returning non-aligned memory. This causes
* incorrect use of `constexpr if` to fail on platforms with aligned
* allocation. Without this, this PAL and its subclasses exported both
* allocation functions and so callers would type-check if they called
* either in `constexpr if` branches and then fail on platforms such as
* Linux or Windows, which expose only unaligned or aligned allocations,
* respectively.
*/
static std::pair<void*, size_t>
reserve_at_least(size_t size) noexcept = delete;
};
} // namespace snmalloc
3 changes: 2 additions & 1 deletion src/pal/pal_concept.h
Original file line number Diff line number Diff line change
Expand Up @@ -92,10 +92,11 @@ namespace snmalloc
ConceptPAL_memops<PAL> &&
(!(PAL::pal_features & LowMemoryNotification) ||
ConceptPAL_mem_low_notify<PAL>) &&
(!(PAL::pal_features & NoAllocation) && (
(!!(PAL::pal_features & AlignedAllocation) ||
ConceptPAL_reserve_at_least<PAL>) &&
(!(PAL::pal_features & AlignedAllocation) ||
ConceptPAL_reserve_aligned<PAL>);
ConceptPAL_reserve_aligned<PAL>)));

} // namespace snmalloc
#endif
5 changes: 5 additions & 0 deletions src/pal/pal_consts.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ namespace snmalloc
* exposed in the Pal.
*/
LazyCommit = (1 << 2),
/**
* This Pal does not support allocation. All memory used with this Pal
* should be pre-allocated.
*/
NoAllocation = (1 << 3),
};
/**
* Flag indicating whether requested memory should be zeroed.
Expand Down
80 changes: 80 additions & 0 deletions src/pal/pal_noalloc.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
#pragma once

namespace snmalloc
{
/**
* Platform abstraction layer that does not allow allocation.
*
* This is a minimal PAL for pre-reserved memory regions, where the
* address-space manager is initialised with all of the memory that it will
* ever use.
*
* It takes an error handler delegate as a template argument. This is
* expected to forward to the default PAL in most cases.
*/
template<typename ErrorHandler>
struct PALNoAlloc
{
/**
* Bitmap of PalFeatures flags indicating the optional features that this
* PAL supports.
*/
static constexpr uint64_t pal_features = NoAllocation;

static constexpr size_t page_size = Aal::smallest_page_size;

/**
* Print a stack trace.
*/
static void print_stack_trace()
{
ErrorHandler::print_stack_trace();
}

/**
* Report a fatal error an exit.
*/
[[noreturn]] static void error(const char* const str) noexcept
{
ErrorHandler::error(str);
}

/**
* Notify platform that we will not be using these pages.
*
* This is a no-op in this stub.
*/
static void notify_not_using(void*, size_t) noexcept {}

/**
* Notify platform that we will be using these pages.
*
* This is a no-op in this stub, except for zeroing memory if required.
*/
template<ZeroMem zero_mem>
static void notify_using(void* p, size_t size) noexcept
{
if constexpr (zero_mem == YesZero)
{
zero<true>(p, size);
}
else
{
UNUSED(p);
UNUSED(size);
}
}

/**
* OS specific function for zeroing memory.
*
* This just calls memset - we don't assume that we have access to any
* virtual-memory functions.
*/
template<bool page_aligned = false>
static void zero(void* p, size_t size) noexcept
{
memset(p, 0, size);
}
};
} // namespace snmalloc
Loading

0 comments on commit c33f355

Please sign in to comment.