diff --git a/src/snmalloc/aal/aal.h b/src/snmalloc/aal/aal.h index 257e78e..98606eb 100644 --- a/src/snmalloc/aal/aal.h +++ b/src/snmalloc/aal/aal.h @@ -246,6 +246,17 @@ namespace snmalloc template constexpr static bool aal_supports = (AAL::aal_features & F) == F; + + /* + * The backend's leading-order response to StrictProvenance is entirely + * within its data structures and not actually anything to do with the + * architecture. Rather than test aal_supports or + * defined(__CHERI_PURE_CAPABILITY__) or such therein, using this + * backend_strict_provenance flag makes it easy to test a lot of machinery + * on non-StrictProvenance architectures. + */ + static constexpr bool backend_strict_provenance = + aal_supports; } // namespace snmalloc #ifdef __POINTER_WIDTH__ diff --git a/src/snmalloc/backend/backend.h b/src/snmalloc/backend/backend.h index 1e95768..2870a5f 100644 --- a/src/snmalloc/backend/backend.h +++ b/src/snmalloc/backend/backend.h @@ -22,6 +22,10 @@ namespace snmalloc using Pal = PAL; using SlabMetadata = typename PagemapEntry::SlabMetadata; +#ifdef __cpp_concepts + static_assert(IsSlabMeta_Arena); +#endif + public: /** * Provide a block of meta-data with size and align. @@ -100,6 +104,7 @@ namespace snmalloc return {nullptr, nullptr}; } + meta->arena_set(p); typename Pagemap::Entry t(meta, ras); Pagemap::set_metaentry(address_cast(p), size, t); @@ -139,13 +144,16 @@ namespace snmalloc Pagemap::get_metaentry(address_cast(alloc)).get_slab_metadata()); Pagemap::set_metaentry(address_cast(alloc), size, t); + /* + * On CHERI, the passed alloc has had its bounds narrowed to just the + * Chunk, and so we retrieve the Arena-bounded cap for use in the + * remainder of the backend. + */ + capptr::Arena arena = slab_metadata.arena_get(alloc); + local_state.get_meta_range().dealloc_range( capptr::Arena::unsafe_from(&slab_metadata), sizeof(SlabMetadata)); - // On non-CHERI platforms, we don't need to re-derive to get a pointer to - // the chunk. On CHERI platforms this will need to be stored in the - // SlabMetadata or similar. - auto arena = capptr::Arena::unsafe_from(alloc.unsafe_ptr()); local_state.get_object_range()->dealloc_range(arena, size); } diff --git a/src/snmalloc/backend_helpers/cheri_slabmetadata_mixin.h b/src/snmalloc/backend_helpers/cheri_slabmetadata_mixin.h new file mode 100644 index 0000000..2e0d75d --- /dev/null +++ b/src/snmalloc/backend_helpers/cheri_slabmetadata_mixin.h @@ -0,0 +1,89 @@ +#pragma once +#include "../pal/pal.h" + +namespace snmalloc +{ + /** + * In CHERI, we must retain, internal to the allocator, the authority to + * entire backing arenas, as there is no architectural mechanism to splice + * together two capabilities. Additionally, these capabilities will retain + * the VMAP software permission, conveying our authority to manipulate the + * address space mappings for said arenas. + * + * We stash these pointers inside the SlabMetadata structures for parts of + * the address space for which SlabMetadata exists. (In other parts of the + * system, we will stash them directly in the pagemap.) This requires that + * we inherit from the FrontendSlabMetadata. + */ + template + class StrictProvenanceSlabMetadataMixin : public SlabMetadata + { + template< + SNMALLOC_CONCEPT(ConceptPAL) A1, + typename A2, + typename A3, + typename A4> + friend class BackendAllocator; + + capptr::Arena arena; + + /* Set the arena pointer */ + void arena_set(capptr::Arena a) + { + arena = a; + } + + /* + * Retrieve the stashed pointer for a chunk; the caller must ensure that + * this is the correct arena for the indicated chunk. The latter is unused + * except in debug builds, as there is no architectural amplification. + */ + capptr::Arena arena_get(capptr::Alloc c) + { + SNMALLOC_ASSERT(address_cast(arena) == address_cast(c)); + UNUSED(c); + return arena; + } + }; + + /** + * A dummy implementation of StrictProvenanceBackendSlabMetadata that has no + * computational content, for use on non-StrictProvenance architectures. + */ + template + struct LaxProvenanceSlabMetadataMixin : public SlabMetadata + { + /* On non-StrictProvenance architectures, there's nothing to do */ + void arena_set(capptr::Arena) {} + + /* Just a type sleight of hand, "amplifying" the non-existant bounds */ + capptr::Arena arena_get(capptr::Alloc c) + { + return capptr::Arena::unsafe_from(c.unsafe_ptr()); + } + }; + +#ifdef __cpp_concepts + /** + * Rather than having the backend test backend_strict_provenance in several + * places and doing sleights of hand with the type system, we encapsulate + * the amplification + */ + template + concept IsSlabMeta_Arena = requires(T* t, capptr::Arena p) + { + { + t->arena_set(p) + } + ->ConceptSame; + } + &&requires(T* t, capptr::Alloc p) + { + { + t->arena_get(p) + } + ->ConceptSame>; + }; +#endif + +} // namespace snmalloc diff --git a/src/snmalloc/backend_helpers/defaultpagemapentry.h b/src/snmalloc/backend_helpers/defaultpagemapentry.h index f063ca7..dc95ce6 100644 --- a/src/snmalloc/backend_helpers/defaultpagemapentry.h +++ b/src/snmalloc/backend_helpers/defaultpagemapentry.h @@ -1,6 +1,7 @@ #pragma once #include "../mem/mem.h" +#include "cheri_slabmetadata_mixin.h" namespace snmalloc { @@ -63,6 +64,9 @@ namespace snmalloc SNMALLOC_FAST_PATH DefaultPagemapEntryT() = default; }; - using DefaultPagemapEntry = DefaultPagemapEntryT; + using DefaultPagemapEntry = DefaultPagemapEntryT, + LaxProvenanceSlabMetadataMixin>>; } // namespace snmalloc