Skip to content

Commit 8e5f966

Browse files
Merge pull request #34 from Microsoft/pagemap_access_refactor
Pagemap access refactor
2 parents 51fbdf3 + ca804c1 commit 8e5f966

8 files changed

Lines changed: 202 additions & 15 deletions

File tree

src/mem/alloc.h

Lines changed: 69 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -62,20 +62,82 @@ namespace snmalloc
6262
Pagemap<SUPERSLAB_BITS, uint8_t, 0>>;
6363

6464
HEADER_GLOBAL SuperslabPagemap global_pagemap;
65+
66+
/**
67+
* Mixin used by `SuperslabMap` to directly access the pagemap via a global
68+
* variable. This should be used from within the library or program that
69+
* owns the pagemap.
70+
*/
71+
struct GlobalPagemap
72+
{
73+
/**
74+
* Returns the pagemap.
75+
*/
76+
SuperslabPagemap& pagemap()
77+
{
78+
return global_pagemap;
79+
}
80+
};
81+
82+
/**
83+
* Optionally exported function that accesses the global pagemap provided by
84+
* a shared library.
85+
*/
86+
extern "C" void* snmalloc_pagemap_global_get(snmalloc::PagemapConfig const**);
87+
88+
/**
89+
* Mixin used by `SuperslabMap` to access the global pagemap via a
90+
* type-checked C interface. This should be used when another library (e.g.
91+
* your C standard library) uses snmalloc and you wish to use a different
92+
* configuration in your program or library, but wish to share a pagemap so
93+
* that either version can deallocate memory.
94+
*/
95+
class ExternalGlobalPagemap
96+
{
97+
/**
98+
* A pointer to the pagemap.
99+
*/
100+
SuperslabPagemap* external_pagemap;
101+
102+
public:
103+
/**
104+
* Constructor. Accesses the pagemap via the C ABI accessor and casts it to
105+
* the expected type, failing in cases of ABI mismatch.
106+
*/
107+
ExternalGlobalPagemap()
108+
{
109+
const snmalloc::PagemapConfig* c;
110+
external_pagemap =
111+
SuperslabPagemap::cast_to_pagemap(snmalloc_pagemap_global_get(&c), c);
112+
// FIXME: Report an error somehow in non-debug builds.
113+
assert(external_pagemap);
114+
}
115+
116+
/**
117+
* Returns the exported pagemap.
118+
*/
119+
SuperslabPagemap& pagemap()
120+
{
121+
return *external_pagemap;
122+
}
123+
};
124+
65125
/**
66126
* Class that defines an interface to the pagemap. This is provided to
67127
* `Allocator` as a template argument and so can be replaced by a compatible
68128
* implementation (for example, to move pagemap updates to a different
69129
* protection domain).
70130
*/
71-
struct SuperslabMap
131+
template<typename PagemapProvider = GlobalPagemap>
132+
struct SuperslabMap : public PagemapProvider
72133
{
134+
using PagemapProvider::PagemapProvider;
73135
/**
74136
* Get the pagemap entry corresponding to a specific address.
75137
*/
76138
uint8_t get(void* p)
77139
{
78-
return global_pagemap.get(p);
140+
return PagemapProvider::pagemap().get(p);
79141
}
80142
/**
81143
* Set a pagemap entry indicating that there is a superslab at the
@@ -121,11 +183,11 @@ namespace snmalloc
121183
for (size_t i = 0; i < size_bits - SUPERSLAB_BITS; i++)
122184
{
123185
size_t run = 1ULL << i;
124-
global_pagemap.set_range(
186+
PagemapProvider::pagemap().set_range(
125187
(void*)ss, (uint8_t)(64 + i + SUPERSLAB_BITS), run);
126188
ss = (uintptr_t)ss + SUPERSLAB_SIZE * run;
127189
}
128-
global_pagemap.set(p, (uint8_t)size_bits);
190+
PagemapProvider::pagemap().set(p, (uint8_t)size_bits);
129191
}
130192
/**
131193
* Update the pagemap to remove a large allocation, of `size` bytes from
@@ -136,7 +198,7 @@ namespace snmalloc
136198
size_t rounded_size = bits::next_pow2(size);
137199
assert(get(p) == bits::next_pow2_bits(size));
138200
auto count = rounded_size >> SUPERSLAB_BITS;
139-
global_pagemap.set_range((void*)p, PMNotOurs, count);
201+
PagemapProvider::pagemap().set_range((void*)p, PMNotOurs, count);
140202
}
141203

142204
private:
@@ -147,12 +209,12 @@ namespace snmalloc
147209
*/
148210
void set(void* p, uint8_t x)
149211
{
150-
global_pagemap.set(p, x);
212+
PagemapProvider::pagemap().set(p, x);
151213
}
152214
};
153215

154216
#ifndef SNMALLOC_DEFAULT_PAGEMAP
155-
# define SNMALLOC_DEFAULT_PAGEMAP snmalloc::SuperslabMap
217+
# define SNMALLOC_DEFAULT_PAGEMAP snmalloc::SuperslabMap<>
156218
#endif
157219

158220
/**

src/mem/allocconfig.h

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,6 @@
44

55
namespace snmalloc
66
{
7-
enum ZeroMem
8-
{
9-
NoZero,
10-
YesZero
11-
};
12-
137
// 0 intermediate bits results in power of 2 small allocs. 1 intermediate
148
// bit gives additional sizeclasses at the midpoint between each power of 2.
159
// 2 intermediate bits gives 3 intermediate sizeclasses, etc.

src/mem/pagemap.h

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,40 @@ namespace snmalloc
1010
static constexpr size_t PAGEMAP_NODE_BITS = 16;
1111
static constexpr size_t PAGEMAP_NODE_SIZE = 1ULL << PAGEMAP_NODE_BITS;
1212

13+
/**
14+
* Structure describing the configuration of a pagemap. When querying a
15+
* pagemap from a different instantiation of snmalloc, the pagemap is exposed
16+
* as a `void*`. This structure allows the caller to check whether the
17+
* pagemap is of the format that they expect.
18+
*/
19+
struct PagemapConfig
20+
{
21+
/**
22+
* The version of the pagemap structure. This is always 1 in existing
23+
* versions of snmalloc. This will be incremented every time the format
24+
* changes in an incompatible way. Changes to the format may add fields to
25+
* the end of this structure.
26+
*/
27+
uint32_t version;
28+
/**
29+
* Is this a flat pagemap? If this field is false, the pagemap is the
30+
* hierarchical structure.
31+
*/
32+
bool is_flat_pagemap;
33+
/**
34+
* Number of bytes in a pointer.
35+
*/
36+
uint8_t sizeof_pointer;
37+
/**
38+
* The number of bits of the address used to index into the pagemap.
39+
*/
40+
uint64_t pagemap_bits;
41+
/**
42+
* The size (in bytes) of a pagemap entry.
43+
*/
44+
size_t size_of_entry;
45+
};
46+
1347
template<size_t GRANULARITY_BITS, typename T, T default_content>
1448
class Pagemap
1549
{
@@ -168,6 +202,32 @@ namespace snmalloc
168202
}
169203

170204
public:
205+
/**
206+
* The pagemap configuration describing this instantiation of the template.
207+
*/
208+
static constexpr PagemapConfig config = {
209+
1, false, sizeof(void*), GRANULARITY_BITS, sizeof(T)};
210+
211+
/**
212+
* Cast a `void*` to a pointer to this template instantiation, given a
213+
* config describing the configuration. Return null if the configuration
214+
* passed does not correspond to this template instantiation.
215+
*
216+
* This intended to allow code that depends on the pagemap having a
217+
* specific representation to fail gracefully.
218+
*/
219+
static Pagemap* cast_to_pagemap(void* pm, const PagemapConfig* c)
220+
{
221+
if (
222+
(c->version != 1) || (c->is_flat_pagemap) ||
223+
(c->sizeof_pointer != sizeof(void*)) ||
224+
(c->pagemap_bits != GRANULARITY_BITS) ||
225+
(c->size_of_entry != sizeof(T)) || (!std::is_integral_v<T>))
226+
{
227+
return nullptr;
228+
}
229+
return static_cast<Pagemap*>(pm);
230+
}
171231
/**
172232
* Returns the index of a pagemap entry within a given page. This is used
173233
* in code that propagates changes to the pagemap elsewhere.
@@ -247,6 +307,32 @@ namespace snmalloc
247307
std::atomic<T> top[ENTRIES];
248308

249309
public:
310+
/**
311+
* The pagemap configuration describing this instantiation of the template.
312+
*/
313+
static constexpr PagemapConfig config = {
314+
1, true, sizeof(void*), GRANULARITY_BITS, sizeof(T)};
315+
316+
/**
317+
* Cast a `void*` to a pointer to this template instantiation, given a
318+
* config describing the configuration. Return null if the configuration
319+
* passed does not correspond to this template instantiation.
320+
*
321+
* This intended to allow code that depends on the pagemap having a
322+
* specific representation to fail gracefully.
323+
*/
324+
static FlatPagemap* cast_to_pagemap(void* pm, const PagemapConfig* c)
325+
{
326+
if (
327+
(c->version != 1) || (!c->is_flat_pagemap) ||
328+
(c->sizeof_pointer != sizeof(void*)) ||
329+
(c->pagemap_bits != GRANULARITY_BITS) ||
330+
(c->size_of_entry != sizeof(T)) || (!std::is_integral_v<T>))
331+
{
332+
return nullptr;
333+
}
334+
return static_cast<FlatPagemap*>(pm);
335+
}
250336
T get(void* p)
251337
{
252338
return top[(size_t)p >> SHIFT].load(std::memory_order_relaxed);

src/override/malloc.cc

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,8 +206,24 @@ extern "C"
206206
}
207207

208208
#ifdef SNMALLOC_EXPOSE_PAGEMAP
209-
SNMALLOC_EXPORT void* SNMALLOC_NAME_MANGLE(snmalloc_get_global_pagemap)(void)
209+
/**
210+
* Export the pagemap. The return value is a pointer to the pagemap
211+
* structure. The argument is used to return a pointer to a `PagemapConfig`
212+
* structure describing the type of the pagemap. Static methods on the
213+
* concrete pagemap templates can then be used to safely cast the return from
214+
* this function to the correct type. This allows us to preserve some
215+
* semblance of ABI safety via a pure C API.
216+
*/
217+
SNMALLOC_EXPORT void* SNMALLOC_NAME_MANGLE(snmalloc_pagemap_global_get)(
218+
PagemapConfig const** config)
210219
{
220+
if (config)
221+
{
222+
*config = &decltype(snmalloc::global_pagemap)::config;
223+
assert(
224+
decltype(snmalloc::global_pagemap)::cast_to_pagemap(
225+
&snmalloc::global_pagemap, *config) == &snmalloc::global_pagemap);
226+
}
211227
return &snmalloc::global_pagemap;
212228
}
213229
#endif

src/pal/pal_consts.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
#pragma once
2+
13
namespace snmalloc
24
{
35
/**
@@ -25,4 +27,19 @@ namespace snmalloc
2527
*/
2628
AlignedAllocation = (1 << 1)
2729
};
30+
/**
31+
* Flag indicating whether requested memory should be zeroed.
32+
*/
33+
enum ZeroMem
34+
{
35+
/**
36+
* Memory should not be zeroed, contents are undefined.
37+
*/
38+
NoZero,
39+
/**
40+
* Memory must be zeroed. This can be lazily allocated via a copy-on-write
41+
* mechanism as long as any load from the memory returns zero.
42+
*/
43+
YesZero
44+
};
2845
}

src/test/func/two_alloc_types/alloc1.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#define USE_RESERVE_MULTIPLE 1
55
#define NO_BOOTSTRAP_ALLOCATOR
66
#define IS_ADDRESS_SPACE_CONSTRAINED
7+
#define SNMALLOC_EXPOSE_PAGEMAP
78
#define SNMALLOC_NAME_MANGLE(a) enclave_##a
89
// Redefine the namespace, so we can have two versions.
910
#define snmalloc snmalloc_enclave
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#undef IS_ADDRESS_SPACE_CONSTRAINED
22
#define SNMALLOC_NAME_MANGLE(a) host_##a
33
#define NO_BOOTSTRAP_ALLOCATOR
4+
#define SNMALLOC_EXPOSE_PAGEMAP
45
// Redefine the namespace, so we can have two versions.
56
#define snmalloc snmalloc_host
6-
#include "../../../override/malloc.cc"
7+
#include "../../../override/malloc.cc"

src/test/func/two_alloc_types/main.cc

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@ extern "C" void host_free(void*);
3232
extern "C" void* enclave_malloc(size_t);
3333
extern "C" void enclave_free(void*);
3434

35+
extern "C" void*
36+
enclave_snmalloc_pagemap_global_get(snmalloc::PagemapConfig const**);
37+
extern "C" void*
38+
host_snmalloc_pagemap_global_get(snmalloc::PagemapConfig const**);
39+
3540
using namespace snmalloc;
3641
int main()
3742
{
@@ -42,6 +47,11 @@ int main()
4247
oe_end = (uint8_t*)oe_base + size;
4348
std::cout << "Allocated region " << oe_base << " - " << oe_end << std::endl;
4449

50+
// Call these functions to trigger asserts if the cast-to-self doesn't work.
51+
const PagemapConfig* c;
52+
enclave_snmalloc_pagemap_global_get(&c);
53+
host_snmalloc_pagemap_global_get(&c);
54+
4555
auto a = host_malloc(128);
4656
auto b = enclave_malloc(128);
4757

0 commit comments

Comments
 (0)