diff --git a/src/mem/pagemap.h b/src/mem/pagemap.h index ad0d91a4..6a307729 100644 --- a/src/mem/pagemap.h +++ b/src/mem/pagemap.h @@ -10,6 +10,36 @@ namespace snmalloc static constexpr size_t PAGEMAP_NODE_BITS = 16; static constexpr size_t PAGEMAP_NODE_SIZE = 1ULL << PAGEMAP_NODE_BITS; + /** + * Structure describing the configuration of a pagemap. When querying a + * pagemap from a different instantiation of snmalloc, the pagemap is exposed + * as a `void*`. This structure allows the caller to check whether the + * pagemap is of the format that they expect. + */ + struct PagemapConfig + { + /** + * The version of the pagemap structure. This is always 1 in existing + * versions of snmalloc. This will be incremented every time the format + * changes in an incompatible way. Changes to the format may add fields to + * the end of this structure. + */ + uint32_t version; + /** + * Is this a flat pagemap? If this field is false, the pagemap is the + * hierarchical structure. + */ + bool is_flat_pagemap; + /** + * The number of bits of the address used to index into the pagemap. + */ + uint64_t pagemap_bits; + /** + * The size (in bytes) of a pagemap entry. + */ + size_t size_of_entry; + }; + template class Pagemap { @@ -168,6 +198,19 @@ namespace snmalloc } public: + static constexpr PagemapConfig config = { + 1, false, GRANULARITY_BITS, sizeof(T)}; + static Pagemap* cast_to_pagemap(void* pm, const PagemapConfig* c) + { + if ( + (c->version != 1) || (c->is_flat_pagemap) || + (c->pagemap_bits != GRANULARITY_BITS) || + (c->size_of_entry != sizeof(T)) || (!std::is_integral_v)) + { + return nullptr; + } + return static_cast(pm); + } /** * Returns the index of a pagemap entry within a given page. This is used * in code that propagates changes to the pagemap elsewhere. @@ -247,6 +290,20 @@ namespace snmalloc std::atomic top[ENTRIES]; public: + static constexpr PagemapConfig config = { + 1, true, GRANULARITY_BITS, sizeof(T)}; + + static FlatPagemap* cast_to_pagemap(void* pm, PagemapConfig* c) + { + if ( + (c->version != 1) || (!c->is_flat_pagemap) || + (c->pagemap_bits != GRANULARITY_BITS) || + (c->size_of_entry != sizeof(T)) || (!std::is_integral_v)) + { + return nullptr; + } + return static_cast(pm); + } T get(void* p) { return top[(size_t)p >> SHIFT].load(std::memory_order_relaxed); @@ -268,4 +325,5 @@ namespace snmalloc } while (length > 0); } }; + } diff --git a/src/override/malloc.cc b/src/override/malloc.cc index d2192b8b..61aa96e5 100644 --- a/src/override/malloc.cc +++ b/src/override/malloc.cc @@ -206,8 +206,16 @@ extern "C" } #ifdef SNMALLOC_EXPOSE_PAGEMAP - SNMALLOC_EXPORT void* SNMALLOC_NAME_MANGLE(snmalloc_get_global_pagemap)(void) + SNMALLOC_EXPORT void* SNMALLOC_NAME_MANGLE(snmalloc_get_global_pagemap)( + PagemapConfig const** config) { + if (config) + { + *config = &decltype(snmalloc::global_pagemap)::config; + assert( + decltype(snmalloc::global_pagemap)::cast_to_pagemap( + &snmalloc::global_pagemap, *config) == &snmalloc::global_pagemap); + } return &snmalloc::global_pagemap; } #endif diff --git a/src/test/func/two_alloc_types/alloc1.cc b/src/test/func/two_alloc_types/alloc1.cc index f7333e43..c86dcde0 100644 --- a/src/test/func/two_alloc_types/alloc1.cc +++ b/src/test/func/two_alloc_types/alloc1.cc @@ -4,6 +4,7 @@ #define USE_RESERVE_MULTIPLE 1 #define NO_BOOTSTRAP_ALLOCATOR #define IS_ADDRESS_SPACE_CONSTRAINED +#define SNMALLOC_EXPOSE_PAGEMAP #define SNMALLOC_NAME_MANGLE(a) enclave_##a // Redefine the namespace, so we can have two versions. #define snmalloc snmalloc_enclave diff --git a/src/test/func/two_alloc_types/alloc2.cc b/src/test/func/two_alloc_types/alloc2.cc index f3a151be..5338054b 100644 --- a/src/test/func/two_alloc_types/alloc2.cc +++ b/src/test/func/two_alloc_types/alloc2.cc @@ -1,6 +1,7 @@ #undef IS_ADDRESS_SPACE_CONSTRAINED #define SNMALLOC_NAME_MANGLE(a) host_##a #define NO_BOOTSTRAP_ALLOCATOR +#define SNMALLOC_EXPOSE_PAGEMAP // Redefine the namespace, so we can have two versions. #define snmalloc snmalloc_host -#include "../../../override/malloc.cc" \ No newline at end of file +#include "../../../override/malloc.cc" diff --git a/src/test/func/two_alloc_types/main.cc b/src/test/func/two_alloc_types/main.cc index bed9e874..4c4687dc 100644 --- a/src/test/func/two_alloc_types/main.cc +++ b/src/test/func/two_alloc_types/main.cc @@ -32,6 +32,11 @@ extern "C" void host_free(void*); extern "C" void* enclave_malloc(size_t); extern "C" void enclave_free(void*); +extern "C" void* +enclave_snmalloc_get_global_pagemap(snmalloc::PagemapConfig const**); +extern "C" void* +host_snmalloc_get_global_pagemap(snmalloc::PagemapConfig const**); + using namespace snmalloc; int main() { @@ -42,6 +47,10 @@ int main() oe_end = (uint8_t*)oe_base + size; std::cout << "Allocated region " << oe_base << " - " << oe_end << std::endl; + // Call these functions to trigger asserts if the cast-to-self doesn't work. + enclave_snmalloc_get_global_pagemap(nullptr); + host_snmalloc_get_global_pagemap(nullptr); + auto a = host_malloc(128); auto b = enclave_malloc(128);