StrictProvenance support in Backend

Wrap the FrontendSlabMetadata with a struct that holds the Arena-bounded
authority for Chunks that the Backend ships out to the Frontend or, for
non-StrictProvenance architecture, encapsulates the sleight of hand that turns
Chunk-bounded CapPtr-s to Arena-bounded ones.
This commit is contained in:
Nathaniel Wesley Filardo
2022-05-31 15:51:31 +01:00
committed by Nathaniel Filardo
parent 86124ba26c
commit 94957f0f72
4 changed files with 117 additions and 5 deletions

View File

@@ -246,6 +246,17 @@ namespace snmalloc
template<AalFeatures F, SNMALLOC_CONCEPT(ConceptAAL) AAL = Aal>
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<StrictProvenance> 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<StrictProvenance>;
} // namespace snmalloc
#ifdef __POINTER_WIDTH__

View File

@@ -22,6 +22,10 @@ namespace snmalloc
using Pal = PAL;
using SlabMetadata = typename PagemapEntry::SlabMetadata;
#ifdef __cpp_concepts
static_assert(IsSlabMeta_Arena<SlabMetadata>);
#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<void> arena = slab_metadata.arena_get(alloc);
local_state.get_meta_range().dealloc_range(
capptr::Arena<void>::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<void>::unsafe_from(alloc.unsafe_ptr());
local_state.get_object_range()->dealloc_range(arena, size);
}

View File

@@ -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<typename SlabMetadata>
class StrictProvenanceSlabMetadataMixin : public SlabMetadata
{
template<
SNMALLOC_CONCEPT(ConceptPAL) A1,
typename A2,
typename A3,
typename A4>
friend class BackendAllocator;
capptr::Arena<void> arena;
/* Set the arena pointer */
void arena_set(capptr::Arena<void> 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<void> arena_get(capptr::Alloc<void> 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<typename SlabMetadata>
struct LaxProvenanceSlabMetadataMixin : public SlabMetadata
{
/* On non-StrictProvenance architectures, there's nothing to do */
void arena_set(capptr::Arena<void>) {}
/* Just a type sleight of hand, "amplifying" the non-existant bounds */
capptr::Arena<void> arena_get(capptr::Alloc<void> c)
{
return capptr::Arena<void>::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<typename T>
concept IsSlabMeta_Arena = requires(T* t, capptr::Arena<void> p)
{
{
t->arena_set(p)
}
->ConceptSame<void>;
}
&&requires(T* t, capptr::Alloc<void> p)
{
{
t->arena_get(p)
}
->ConceptSame<capptr::Arena<void>>;
};
#endif
} // namespace snmalloc

View File

@@ -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<FrontendSlabMetadata>;
using DefaultPagemapEntry = DefaultPagemapEntryT<std::conditional_t<
backend_strict_provenance,
StrictProvenanceSlabMetadataMixin<FrontendSlabMetadata>,
LaxProvenanceSlabMetadataMixin<FrontendSlabMetadata>>>;
} // namespace snmalloc