Commit Graph

379 Commits

Author SHA1 Message Date
Istvan Haller
c01a1215c6 Cleanup and made new variant work with Verona 2021-08-23 21:07:51 +01:00
Istvan Haller
769c61e716 Moved SharedStateHandle to Pool class instead of methods since all of them use it 2021-08-23 20:08:27 +01:00
Istvan Haller
c89a085c90 Removed code duplication by making generoc Pool also be static 2021-08-23 19:54:26 +01:00
Istvan Haller
35c9422913 Comment fixes 2021-08-23 19:19:58 +01:00
Istvan Haller
4d2bf93b7a Deleted the ability to implicitly copy LocalAllocator 2021-08-23 16:37:21 +01:00
Istvan Haller
99f57646da Re-enabled generic Pool which uses ChunkAllocator. Allocator pool renamed to AllocPool. 2021-08-20 15:28:38 +01:00
Nathaniel Wesley Filardo
4501c0ed81 corealloc: spurious sizeof()
We mean to be allocating MIN_ALLOC_SIZE (== 2 * sizeof(void*)), not
sizeof(MIN_ALLOC_SIZE) (== sizeof(size_t)).  This doesn't matter in practice
since, well, MIN_ALLOC_SIZE is the minimum allocation size, and so requesting
either will have the same effect.  Still, best to say what we mean.
2021-08-13 16:46:00 +01:00
Nathaniel Wesley Filardo
7a02ae949f Remove stale static_assert
With snmalloc2, slabs are linked through the Metaslab structure directly rather
than in-band in a free allocation, so we no longer need to store a SlabLink in
even the smallest allocation classes.
2021-08-13 16:46:00 +01:00
Matthew Parkinson
f38a5a63d5 Make threshold for waking always a percentage
The threshold for waking is used to ensure that we only use a slab when
it has sufficient space that we won't hit the slow path to soon after
using this slab. In the checked version, this is also used to give some
entropy in layout.  Changing this to always be a pertcentage in the
checked case increases the effectiveness of the free list to detect OOB
write.
2021-08-13 14:00:41 +01:00
David Chisnall
cd70a7856b Fix fallout from the merge.
- CI merge issues:
   - The malloc shim libraries are renamed.
   - CMake gets very unhappy if you don't enable the C language and
     tries to link with the C compiler instead of the C++ compiler if
     you do enable it.
   - The Ubuntu packages for QEMU install a `binfmt_misc` activator for
     PowerPC64 little-endian, but set the page size to 4 KiB.  We then
     tried to run the tests (which expect 64 KiB pages) and became very
     confused when `mmap` returned 4 KiB-aligned memory.
 - Test failures:
   - Fix all of the issues UBsan found.
     - Underflow in `pointer_offset` when used to add negative offsets.
     - `CoreAlloc`'s `LocalState` accessed on a null `CoreAlloc` pointer.
     - Out of bounds access in the sizeclass list on attempts to access
       more memory than fits in the VA space.
     -
   - There was an integer overflow in `AddressSpace` that could cause it
     to try to allocate a zero-sized object, get a null pointer, and
     then try to do something with 0 - {size of the real allocation}.
   - The malloc tests weren't setting `errno` to 0 before doing
     calling `malloc`, which should set `errno` on failure, and then
     checking that `errno` was 0.
   - Don't call `PAL::error` on PAL allocation failure, return `nullptr`.
     The PALs were inconsistent about that and the new code expects to be
     able to report address-space exhaustion.
   - The malloc checks can behave differently with 0-sized allocations
     on different platforms but were very fragile about their
     expectations.
   - The malloc test didn't report failure for all of the ways that it
     could fail and so was spuriously passing on some platforms.
   - The perf test for external pointer is currently very slow on
     Windows.  The number of loops have been reduced and a timeout added
     for the Windows CI runs.
   - The logic to capture `errno` across calls was using
     `decltype(errno)`, which on some platforms where `errno` is a macro
     evaluated to `int&` and so they captured a reference rather than
     the value and failed to reset `errno`.
   - The Apple PAL can set `errno` on `notify_using` if it's called with
     memory that was not previously passed to `notify_not_using` but was
     not adequately protected against this and so would sometimes cause
     `malloc` to set `errno` to `EINVAL`.
2021-08-06 14:00:56 +01:00
David Chisnall
e8374479f4 Snmalloc2 API cleanups for sandbox use. (#359)
This is the set of changes required for snmalloc2 to be usable by the
process sandboxing code and incorporates some API changes that reduce
the amount of code required to embed snmalloc.  Highlights:

 - Merge the config and back-end classes.
 - Everything in config is now global (all methods are static)
 - The GlobalState class is gone (all global state is managed by global
   methods on the config class)
 - LocalState is now a member of the config class, all methods are
   instance methods.
 - Not every configuration needs to use the lazy initialisation hooks.
   They now need to be provided only if they are used.  If the
   configuration does not provide an `ensure_init` method, it is not
   called.  If it does not provide an `is_initialised` method then the
   global initialisation state is not checked.
 - There is now an `snmalloc::Options` class that default initialises
   itself to the default behaviour.  Every configuration must provide a
   `constexpr` instance of this class.  Each flag can be separately
   overridden and new flags can be added without breaking any existing
   API consumers.

The config classes are moved into the backend directory.
2021-08-05 15:08:12 +01:00
Matthew Parkinson
81bf341732 XOR encoded next_object
This commit adds a simple XOR encoding to the next_object pointer in
FreeObjects.  This removes the trivial way of getting hold of a physical
address from the system by observing the free list pointers in
deallocated objects.
2021-07-26 15:32:32 +01:00
Nathaniel Wesley Filardo
84a5fb9450 Correct REMOTE_MIN_ALIGN
Take the maximum of...

* CACHELINE_SIZE (for performance)

* next_pow2(NUM_SIZECLASSES + 1) so that, when the pagemap points to a Remote,
  the (small) size class stuffed in the bottom bits can be removed by alignment

* next_pow2(NUM_LARGE_CLASSES + 1) so that, when the pagemap isn't pointing to
  a Remote, when the associated chunk is (part of) a large allocation, aligning
  the Remote* results in 0.

The last of these conditions will almost never be the deciding factor, as there
are generally many more small size classes than large ones, but it shouldn't
hurt to be safe.
2021-07-23 13:51:39 +01:00
Matthew Parkinson
0cfa8f2cff Remove globalconfig.h includes. 2021-07-23 10:21:27 +01:00
Matthew Parkinson
529b90b01b Default some statistics 2021-07-23 07:23:35 +01:00
Matthew Parkinson
6b53c29600 Fix typo. 2021-07-23 07:23:35 +01:00
Matthew Parkinson
c1001ae7a4 Merge remote-tracking branch 'origin/master' into snmalloc2 2021-07-21 09:58:22 +01:00
Matthew Parkinson
0b7929327a Add errors for failing to initialise the system. 2021-07-21 09:36:06 +01:00
Matthew Parkinson
8a8669f957 Add randomised start to pagemap layout
The pagemap contains a lot of important data.  This commit makes the
checked mode overallocate, and then start the pagemap at a random
offset within this range.
2021-07-21 09:36:06 +01:00
Matthew Parkinson
9df0101dfd Enable guard pages in CHECK_CLIENT
Change the behaviour to use PROT_NONE for reservations in CHECK_CLIENT
mode.  This means that we only provide access once data is actually
being used.
2021-07-21 09:36:06 +01:00
Matthew Parkinson
02d2ab8f7e Pagemap requires registration of used space
This PR exposes a pagemap interface to specify ranges that are being
used. The overall invariant is that any memory in the address space
manager has the pagemap committed. This means that individual operations
do not need to commit entries.

This is important for Windows that does not support lazy commit.  It is
also important if we want to PROT_NONE most of the pagemap to reduce the
risk of memory safety issues getting access to the pagemap.

There are minor changes to test to pull memory directly from the Pal.
There are also bug fixes in the pagemap tests.
2021-07-21 09:36:06 +01:00
Nathaniel Wesley Filardo
2ba0de76b3 mem/metaslab: use uintptr_t alignment functions
It is UB to offset from `nullptr` (except perhaps with a 0 offset).  Apparently
clang is able to use this to reason, given `void* p`, that comparing
`__builtin_align_down(p, x)` against `handle.fake_large_remote` (i.e., a `static
inline constexpr` `nullptr`) must be the same as comparing `p` itself against
`nullptr`.

In `MetaEntry`'s constructor, converting the provided `RemoteAllocator*` to
`uintptr_t` before offsetting avoids the UB.  (While here, don't use
`address_cast()`, as `address_t` will, on CHERI, be `ptraddr_t` and not
`uintptr_t`.)

Doing the alignment in `get_remote` at `uintptr_t` before casting to
`RemoteAllocator*`, rather than converting and then aligning, prevents the
reasoning above from eliminating the alignment.
2021-07-20 14:42:53 +01:00
Matthew Parkinson
b501da69db Implements protection on remote messages queues
This extends the freelist protection to the remote message queues. They
effectively perform doubly linked list entries for the message queue
with the enqueue operation first linking in the previous pointer, and
then then atomically setting the next.  This ensures that the visible
states always satisfy the invariant that the forward and backward
pointers are correct for any visisble object.

There is a key_global that is used for all remote deallocations. The
remote cache uses the same protection to build the temporary lists
before forwarding to the next allocator.

The mpscq is integrated into the remoteallocator as it is no longer
a reusable datastructure, but a special purpose implementation.
2021-07-19 12:57:03 +01:00
Matthew Parkinson
c58b0a5f4d Removes a register copy from x86 codegen. 2021-07-19 12:57:03 +01:00
David Chisnall
e60dd3dc95 Don't depend on <entropy> if we don't need it. (#350)
Including <entropy> brings in <iostream>, which breaks the in-libc
build.
2021-07-19 11:40:27 +01:00
Matthew Parkinson
de8ef3efc7 Cleaner implementation of signed pointers. (#347)
* Cleaner implementation of signed pointers.

This encodes a back pointer in each node.  The back pointer is stored
in an encoded form so that it is hard to corrupt and trick the allocator
into following incorrect pointers.

This changes the encoding from previously being a Feistel network on
the next pointer that was using the prev as part of the key, to now
effectively using a doubly linked queue, where the back pointers are
scrambled, so it is hard to forge them.

This has the positive effects of
 - Not needing to store previous while building the list, as the append
   nows, curr and next at the point of writing into next, and does not
   need an additional previous.
 - The encoding is not affecting the actual next value, so more
   instructions can be executed in parallel by the CPU.

Future extensions, store a changing key in the FreeListBuilder so it
becomes harder to try to forge the previous token.

This approach can also be applied to the remote list, and will in a
subsequent PR.  This enables the idea to be tested.

* Remove unused header.

* Apply suggestions from code review

Co-authored-by: Nathaniel Wesley Filardo <VP331RHQ115POU58JFRLKB7OPA0L18E3@cmx.ietfng.org>

Co-authored-by: Nathaniel Wesley Filardo <VP331RHQ115POU58JFRLKB7OPA0L18E3@cmx.ietfng.org>
2021-07-15 18:31:28 +01:00
Istvan Haller
d0ecba5280 Improved OEPal integration with the new snmalloc architecture (#346)
* Improved OEPal integration with the new snmalloc architecture

* Applied PR feedback
2021-07-15 15:06:47 +01:00
Matthew Parkinson
4b9ead8066 Fix register_clean_up being called with ScopedAllocator. (#344) 2021-07-12 19:50:36 +01:00
Matthew Parkinson
f0e2ab702a Major refactor of snmalloc (#343)
# Pagemap
 
The Pagemap now stores all the meta-data for the object allocation. The meta-data in the pagemap is effectively a triple of the sizeclass, the remote allocator, and a pointer to a 64 byte block of meta-data for this chunk of memory. By storing the pointer to a block, it allows the pagemap to handle multiple slab sizes without branching on the fast path. There is one entry in the pagemap per 16KiB of address space, but by using the same entry in the pagemap for 4 adjacent entries, then we can treat a 64KiB range can be treated as a single slab of allocations.

This change also means there is almost no capability amplification required by the implementation on CHERI for finding meta-data. The only amplification is required, when we change the way a chunk is used to a size of object allocation.


# Backend

There is a second major aspect of the refactor that there is now a narrow API that abstracts the Pagemap, PAL and address space management. This should better enable the compartmentalisation and makes it easier to produce alternative backends for various research directions. This is a template parameter that can be used to specialised by the front-end in different ways.

# Thread local state

The thread local state has been refactored into two components, one (called 'localalloc') that is stored directly in the TLS and is constant initialised, and one that is allocated in the address space (called 'coreallloc') which is lazily created and pooled.

# Difference

This removes Superslabs/Medium slabs as there meta-data is now part of the pagemap.
2021-07-12 15:53:36 +01:00
David Chisnall
b598add0d1 Fix the build with GCC and libc cleanup. (#341)
GCC is a lot more picky about extern "C" definitions in namespaces than
clang and so the `friend` declaration wasn't picked up by the version of
the function declared in the `snmalloc` namespace.  Move it out to where
GCC is happy with it.
2021-07-01 11:07:31 +01:00
Matthew Parkinson
e77a5d9c58 Remove cache-friendly offset. 2021-05-18 14:58:15 +01:00
Matthew Parkinson
f0ebbebf74 Refactor is_start_of_object 2021-05-18 14:58:15 +01:00
Matthew Parkinson
5198821905 CR Feedback. 2021-05-18 14:58:15 +01:00
Matthew Parkinson
208ab9a8e8 Rederive allocator id for remotes.
Storing a pointer to an allocator id in an unused object could be a
gadget for escallating priviledge of an attacker, by enabling
use-after-free to corrupt the allocator structure, and then create more
damage.

This commit adds an alternative implementation that does not cache the
allocator id.
2021-05-18 14:58:15 +01:00
Matthew Parkinson
b3796c123e Move remote cache out of alloc.h
Consolidate the remote code into a single file.
2021-05-18 14:58:15 +01:00
Matthew Parkinson
e011c297e6 Allocslab doesn't need to know impl of Remote
The Allocslab only needs to know of the RemoteAllocators existance.
2021-05-18 14:58:15 +01:00
Matthew Parkinson
118eecb2e6 Check for heap corruption
The allocator id and sizeclass are stored in deallocated objects to
provide quick lookup.  This adds checks that these are actually correct.
2021-05-18 14:58:15 +01:00
Nathaniel Filardo
80c6e95210 C++20: Add TrivialInitAtomic
C++20 does away with trivial initializers for std::atomic<T>, which means our
global pagemaps always get zeroed, sometimes after other static ctors have run
(fun fun!).  Use the new std::atomic_ref<T> when available.  Abstract all this
behind an #ifdef-ful wrapper.
2021-04-22 01:28:24 +01:00
Nathaniel Filardo
8390a70a48 Fix static-sized alloc paths, add compilation test
The CapPtr refactoring was largely compiler guided; unfortunately, it turns out
that the static-sized alloc function is not well exercised.  As such, some type
errors and unnecessary unsafety lurked behind missing template instantiation.

Correct those and add calls to the test harness to make sure we always generate
at least one instance of each small/medium/large case.  While here, it doesn't
hurt to make sure that we call all three possible dealloc() flavors as well.
This will, if nothing else, force instantiation of the static-sized dealloc
template as well.
2021-04-20 12:17:46 +01:00
Nathaniel Filardo
c673b2e0ba SP: pass CBChunk, not CBArena, down to Superslab ops 2021-04-09 12:39:29 +01:00
Nathaniel Filardo
95871ff8a1 SP: free lists and remote queues are CBAlloc
Continue tightening the screws on pointer bounds.

Notably, pointers in remote queues are bounded to the free objects.  While we
believe that something like MTE is required to make in-band metadata safe, this
is a kind of defense in depth for StrictProvenance architectures: UAF for small
and medium objects expose mostly other (free) small or medium objects and not
allocator metadata (modulo some potential aliasing when Superslabs and
Mediumslabs interconvert).  This might shift the burdon on an attacker from
simply holding a UAF pointer to having had to farm several heap pointers.

The policy of bounding remote queue pointers may make the allocator's behavior
for small objects unexpected: while initial object construction during
allocation (that is, when the free list is empty) continues to cleave out
exportable pointers from elevated pointers to internal slabs, reuse pulls from
free lists of *already-bounded* objects.  These objects are queued by the
deallocation side, of course, but these paths now include "parallel
reconstruction" of a pointer to the free object from the amplified view of the
returned pointer, rather than queueing amplified pointers and leaving
reconstruction to the allocation side.

Medium objects are possibly similarly mysterious with the added twist that
medium slabs do not store pointers but rather always cleave from their
self-reference (but their interface has always operated using pointers).
Nevertheless, pointers to medium objects end up in remote queues, so we continue
to engage in "parallel reconstruction" in the deallocation paths.
2021-04-09 12:39:29 +01:00
Nathaniel Filardo
cf50fc5e55 SP: mediumslab: reduce capptr_export calls
Mediumslabs are strung on a dllist and used to feed the allocator there.  If we
ensure that these (and the root pointer to the list itself) are already
exported, then our alloc paths can bound these to arrive at exposible pointers.

The dealloc paths, where we might want a non-exported pointer, already have one,
as they have gone through amplification to get an arena-bounded pointer.

The sole wrinkle in this plan is that we might need a pointer without platform
constraints to manipulate the memory map for page-based zeroing.  Since we have
ample room in the Mediumslab header (a few kilobytes end up being used for
padding; the curious should see b8b5f305 and 3d3b0487), just cache therein a
copy of the CBChunk-bound pointer used in Mediumslab::init() for ::alloc().
2021-04-09 12:39:29 +01:00
Nathaniel Filardo
eff76309f5 SP: bound chunks in address space 2021-04-09 12:39:29 +01:00
Nathaniel Filardo
f7821e11eb SP: LargeAlloc return CBChunk & chase consequences
Even if we opt not to bound these pointers internally (if they aren't headed out
to the user program or we later derive bounded pointers), they should still be
annotated as something other than CBArena, ensuring that we do not attempt to
use them for general amplification.
2021-04-09 12:39:29 +01:00
Nathaniel Filardo
54fec3821f SP: alloc paths: begin using CBAllocE
Begin turning the screws on bounds: pointers the allocator is about to reveal
must be annotated as CBAllocE.  Use the PAL's capptr_export and the AAL's
capptr_bound<> to get them there.
2021-04-09 12:39:29 +01:00
Nathaniel Filardo
f2f1eb33d6 SP: amplify on dealloc and query paths 2021-04-09 12:39:29 +01:00
Nathaniel Filardo
6a7e82463c SP: MemoryProviderStateMixin, AddressSpaceManager
* The AddressSpaceManager now requests address space in specified granule
  sizes and registers those allocations with an external ArenaMap.

* The DefaultArenaMap is a (somewhat erroneously named) Pagemap sparse array /
  tree for these provenance roots.  Nothing is stored on non-StrictProvenance
  architectures.

* In the Sandbox test, give an example of a different ArenaMap structure, which
  confines amplification to sandbox memory.

* Adjust some other tests to compile.
2021-04-09 12:39:29 +01:00
Nathaniel Filardo
a9722667ab SP: add ArenaMap
The ArenaMap will store our internal provenance roots.  This commit simply
introduces the data structure and does not wire it up to the rest of the tree.
2021-04-09 12:39:29 +01:00
Nathaniel Filardo
d9822036b2 SP: add bounds placeholders to message queues
Like all the annotations so far, leave these as CBArena-bounded.

However, we can start to do a little better than we were doing before: for
not-large deallocations, we know that the internal superslab pointer is
sufficiently authoritative that we can use it to get to data structures rather
than also passing an amplified pointer to the allocation in question.  This,
then, partially reverts some of the earlier changes made while plumbing
CapPtr<>s through the system.

While here, avoid quite as many void*-s in favor of Remote* or SlabNext*.
2021-04-09 12:39:29 +01:00
Nathaniel Filardo
78ab0a7937 SP: external_pointer should not leak authority
Work with high-authority pointers internally, but then copy the address onto to
the low-authority pointer originally given to us (using the same primitive as
amplification, just backwards).  This ensures that clients cannot behave
non-monotonically even if they have access to external_pointer().  That is, we
are not an amplification oracle, though we might reveal the address of an object
somewhere clients cannot otherwise access.  Notably, we might reveal if a
{Super,Medium,}Slab has changed sizeclass (or been repurposed as or from a
Largealloc), forming a covert channel for data but not capabilities.  Of course,
if the pointer provided to external_alloc() is still useful, the implied UAF in
these scenarios is still its own problem.
2021-04-09 12:39:29 +01:00