Skip to content

Commit 6f4ff35

Browse files
authored
Merge pull request #3 from Zia-Rashid/dev
Dev
2 parents 6ce5a6c + 04a506c commit 6f4ff35

File tree

4 files changed

+125
-105
lines changed

4 files changed

+125
-105
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ docs/notes.md
22
perf.data
33
include/
44
src/
5+
zialloc_wrapper.cpp
56

67
# Build directories
78
build/

docs/zialloc-design.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,8 @@ Within one page, all slots have identical stride/usable size and allocation stat
3535

3636
XL allocations semi-bypass the page/segment class system and are mmapped as standalone mappings with inline XL headers.
3737

38-
Metadata model is mixed inline + allocator-owned(OOL):
39-
- Inline per regular chunk header: owning page pointer, slot index, magic value (canary)
38+
Metadata model is entirely allocator-owned(OOL):
39+
- Chunks can resolve their owning page and slot idx using pointer arithmetic on themselves
4040
- Per-page metadata: bitmap, used counts, owner TID, deferred-free ring
4141
- Per-segment metadata: class, page array, full-page count, chunk-geometry lock-in, integrity key/canary
4242
- XL metadata is inline in front of returned pointer (`XLHeader`)

zialloc/mem.h

Lines changed: 0 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,6 @@
11
#ifndef SEGMENTS_H
22
#define SEGMENTS_H
33

4-
/*
5-
Heap-Level: |Metadata|Segment|guard|Segment|guard|Segment|...| # size =
6-
2GB Segment-Level: |Metadata|guard|slot|slot|slot|...| # size = 4MB->(32MB*)
7-
(this would mean that about 2048 segments couldfit inside of our heap at any
8-
time which is almost a bit too much so what I will actualy do is set this value
9-
higher meaning each segment is larger. if we use the same pointer access trick
10-
as before and maintain our alignment then we should be able to access any given
11-
page in constant time so it hsouldn't be that big of a deal that we have a
12-
larger segment size. Thus set segment size to say 32MB?) Page-Level:
13-
|Metadata|chunk|chunk|chunk|...|guard| # size : small=64KB,
14-
med=512KB, large=4MiB ---> this means we can fit multiple large pages w/i a
15-
segment, XL allocations will still be handled using MMAP.
16-
17-
Heap metadata: we should track how many segments are active, their types,
18-
the location of the metadata corresponding to a given chunk so that we can
19-
access it if it contains a size and space we need.
20-
.
21-
It contains information about the sizes that the pages w/i it support, a
22-
bitmap that tracks the allocation status of the contained pages. This can be
23-
found by querying the metadata of each page and checking if used == 0. Maybe
24-
also track the status of the segment itself; any given segment could be
25-
completely empty, active, or full. But we should probably make sure there is a
26-
minimum number (1sm, 1md) active at all times.
27-
28-
29-
I don't intend to implement any form of coalescing - much like partition
30-
alloc, I will release memory to OS but keep the vmem reservation
31-
32-
*/
33-
// lets make sure our guard pages are the same size as the chunks so that we can
34-
// handle indexing w/ offsets normally
35-
364
#include "types.h"
375
#include <cstdint>
386
#include <iostream>
Lines changed: 122 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -76,15 +76,8 @@ static inline size_t class_index_for_kind(page_kind_t kind) {
7676
class Page;
7777
class Segment;
7878

79-
static constexpr uint32_t CHUNK_MAGIC = 0xC47A110CU;
8079
static constexpr uint64_t XL_MAGIC = 0x584C4F43484B4559ULL; // "XLOCHKEY"
8180

82-
struct ChunkHeader {
83-
Page *owner;
84-
uint32_t slot;
85-
uint32_t magic;
86-
};
87-
8881
struct XLHeader {
8982
uint64_t magic;
9083
size_t mapping_size;
@@ -170,7 +163,6 @@ class Page {
170163
void *base;
171164
page_kind_t size_class;
172165
size_t page_span;
173-
size_t chunk_stride;
174166
size_t chunk_usable;
175167
uint32_t capacity;
176168
uint32_t used;
@@ -181,17 +173,27 @@ class Page {
181173
std::vector<uint64_t> used_bitmap;
182174
DeferredRing deferred_frees;
183175

184-
ChunkHeader *slot_header(uint32_t slot) const {
185-
return reinterpret_cast<ChunkHeader *>(static_cast<char *>(base) +
186-
static_cast<size_t>(slot) * chunk_stride);
176+
void *slot_ptr(uint32_t slot) const {
177+
return static_cast<void *>(static_cast<char *>(base) +
178+
static_cast<size_t>(slot) * chunk_usable);
187179
}
188180

189-
void *slot_user_ptr(uint32_t slot) const {
190-
return static_cast<void *>(reinterpret_cast<char *>(slot_header(slot)) + sizeof(ChunkHeader));
191-
}
181+
bool ptr_to_slot_idx(void *ptr, uint32_t *slot_out) const {
182+
if (!initialized || !ptr || !slot_out || chunk_usable == 0)
183+
return false;
184+
const uintptr_t p = reinterpret_cast<uintptr_t>(ptr);
185+
const uintptr_t b = reinterpret_cast<uintptr_t>(base);
192186

193-
ChunkHeader *user_to_header(void *ptr) const {
194-
return reinterpret_cast<ChunkHeader *>(static_cast<char *>(ptr) - sizeof(ChunkHeader));
187+
PTR_IN_BOUNDS(b < p, "Chunk pointer points before owning page..."); // if(p < b) => bad bad bad
188+
189+
const size_t offset = static_cast<size_t>(p - b);
190+
if ((offset % chunk_usable) != 0) // should be aligned at this point
191+
return false;
192+
const size_t slot = offset / chunk_usable;
193+
if (slot >= capacity)
194+
return false;
195+
*slot_out = static_cast<uint32_t>(slot);
196+
return true;
195197
}
196198

197199
bool bit_is_set(uint32_t idx) const {
@@ -212,17 +214,6 @@ class Page {
212214
used_bitmap[word] &= ~(1ULL << bit);
213215
}
214216

215-
bool validate_header(void *ptr, ChunkHeader *hdr) const {
216-
if (!hdr)
217-
return false;
218-
if (hdr->magic != CHUNK_MAGIC)
219-
return false;
220-
if (hdr->owner != this)
221-
return false;
222-
if (hdr->slot >= capacity)
223-
return false;
224-
return slot_user_ptr(hdr->slot) == ptr;
225-
}
226217

227218
void drain_deferred_locked(size_t max_to_drain) {
228219
size_t drained = 0;
@@ -238,7 +229,7 @@ class Page {
238229
public:
239230
Page()
240231
: owner_segment(nullptr), owner_segment_idx(0), base(nullptr), size_class(PAGE_SM),
241-
page_span(0), chunk_stride(0), chunk_usable(0), capacity(0), used(0),
232+
page_span(0), chunk_usable(0), capacity(0), used(0),
242233
first_hint(0), owner_tid(0), status(EMPTY), initialized(false), used_bitmap(),
243234
deferred_frees() {}
244235

@@ -252,21 +243,17 @@ class Page {
252243
return false;
253244

254245
const size_t span = page_size_for_kind(kind);
255-
size_t stride = 0;
256-
size_t cap = 0;
257-
const size_t norm_req = norm_chunk_req(kind, req_sz);
258-
stride = align_up(norm_req + sizeof(ChunkHeader), 16);
246+
const size_t stride = norm_chunk_req(kind, req_sz);
259247
if (stride == 0)
260248
return false;
261-
cap = span / stride;
249+
const size_t cap = span / stride;
262250
if (cap == 0 || cap > UINT32_MAX)
263251
return false;
264252

265253
base = page_base;
266254
size_class = kind;
267255
page_span = span;
268-
chunk_stride = stride;
269-
chunk_usable = stride - sizeof(ChunkHeader);
256+
chunk_usable = stride;
270257
capacity = static_cast<uint32_t>(cap);
271258
used = 0;
272259
first_hint = 0;
@@ -332,13 +319,8 @@ class Page {
332319
owner_tid = current_tid();
333320
status = (used == capacity) ? FULL : ACTIVE;
334321

335-
ChunkHeader *hdr = slot_header(slot);
336-
hdr->owner = this;
337-
hdr->slot = slot;
338-
hdr->magic = CHUNK_MAGIC;
339-
340322
*after = status;
341-
return slot_user_ptr(slot);
323+
return slot_ptr(slot);
342324
}
343325
}
344326
word_idx = (word_idx + 1) % words;
@@ -355,14 +337,12 @@ class Page {
355337
page_status_t *after) {
356338
if (!contains_ptr(ptr))
357339
return false;
358-
359-
ChunkHeader *hdr = user_to_header(ptr);
360-
if (!validate_header(ptr, hdr))
340+
uint32_t slot = 0;
341+
if (!ptr_to_slot_idx(ptr, &slot))
361342
return false;
362343

363344
*before = status;
364-
const uint32_t slot = hdr->slot;
365-
if (!bit_is_set(slot)) // double free detected. should probably make a macro for this.
345+
if (!bit_is_set(slot))
366346
std::abort();
367347

368348
if (g_zero_on_free.load(std::memory_order_relaxed)) {
@@ -385,10 +365,11 @@ class Page {
385365
bool enqueue_deferred_free(void *ptr, size_t *usable_out) {
386366
if (!contains_ptr(ptr))
387367
return false;
388-
389-
ChunkHeader *hdr = user_to_header(ptr);
390-
if (!validate_header(ptr, hdr))
368+
uint32_t slot = 0;
369+
if (!ptr_to_slot_idx(ptr, &slot))
391370
return false;
371+
if (!bit_is_set(slot))
372+
std::abort();
392373

393374
if (usable_out)
394375
*usable_out = chunk_usable;
@@ -398,13 +379,12 @@ class Page {
398379
size_t usable_size(void *ptr) const {
399380
if (!contains_ptr(ptr))
400381
return 0;
401-
402-
ChunkHeader *hdr = user_to_header(ptr);
403-
if (!validate_header(ptr, hdr))
382+
uint32_t slot = 0;
383+
if (!ptr_to_slot_idx(ptr, &slot))
404384
return 0;
405385

406386
if (g_uaf_check.load(std::memory_order_relaxed)) {
407-
if (!bit_is_set(hdr->slot))
387+
if (!bit_is_set(slot))
408388
std::abort();
409389
}
410390

@@ -478,6 +458,19 @@ class Segment {
478458
return p >= b && p < (b + SEGMENT_SIZE);
479459
}
480460

461+
bool resolve_page_for_ptr(void *ptr, Page **page_out) {
462+
if (!ptr || !page_out || !contains(ptr) || page_size == 0)
463+
return false;
464+
const uintptr_t b = reinterpret_cast<uintptr_t>(base);
465+
const uintptr_t p = reinterpret_cast<uintptr_t>(ptr);
466+
const size_t offset = static_cast<size_t>(p - b);
467+
const size_t idx = offset / page_size;
468+
if (idx >= page_count)
469+
return false;
470+
*page_out = &pages[idx];
471+
return true;
472+
}
473+
481474
bool has_free_pages() const {
482475
return full_pages.load(std::memory_order_relaxed) < page_count;
483476
}
@@ -781,6 +774,70 @@ class HeapState {
781774
return hdr->usable_size;
782775
}
783776

777+
bool resolve_segment_for_ptr(void *ptr, size_t *seg_idx_out, Segment **seg_out) {
778+
if (!ptr || !seg_idx_out || !seg_out)
779+
return false;
780+
781+
const uintptr_t p = reinterpret_cast<uintptr_t>(ptr);
782+
if (base && reserved_cursor > 0) {
783+
const uintptr_t b = reinterpret_cast<uintptr_t>(base);
784+
const uintptr_t end = b + reserved_cursor;
785+
if (p >= b && p < end) {
786+
const size_t idx = static_cast<size_t>((p - b) / SEGMENT_SIZE);
787+
if (idx < layout.size()) {
788+
Segment *seg = layout[idx].get();
789+
if (seg && seg->contains(ptr)) {
790+
*seg_idx_out = idx;
791+
*seg_out = seg;
792+
return true;
793+
}
794+
}
795+
}
796+
}
797+
// TODO: still not sure if '~' is correct since i already subtract 1 from segment shift in type.h
798+
const uintptr_t seg_base = p & ~static_cast<uintptr_t>(SEGMENT_MASK);
799+
for (size_t i = 0; i < seg_bases.size(); ++i) {
800+
if (reinterpret_cast<uintptr_t>(seg_bases[i]) != seg_base)
801+
continue;
802+
if (i >= layout.size())
803+
continue;
804+
Segment *seg = layout[i].get();
805+
if (seg && seg->contains(ptr)) {
806+
*seg_idx_out = i;
807+
*seg_out = seg;
808+
return true;
809+
}
810+
}
811+
// TODO: confirm neither this nor the above are necessary
812+
for (size_t i = 0; i < layout.size(); ++i) {
813+
Segment *seg = layout[i].get();
814+
if (seg && seg->contains(ptr)) {
815+
*seg_idx_out = i;
816+
*seg_out = seg;
817+
return true;
818+
}
819+
}
820+
821+
return false;
822+
}
823+
824+
bool resolve_page_for_ptr(void *ptr, size_t *seg_idx_out, Segment **seg_out,
825+
Page **page_out) {
826+
if (!ptr || !seg_idx_out || !seg_out || !page_out)
827+
return false;
828+
size_t seg_idx = 0;
829+
Segment *seg = nullptr;
830+
if (!resolve_segment_for_ptr(ptr, &seg_idx, &seg))
831+
return false;
832+
Page *page = nullptr;
833+
if (!seg->resolve_page_for_ptr(ptr, &page))
834+
return false;
835+
*seg_idx_out = seg_idx;
836+
*seg_out = seg;
837+
*page_out = page;
838+
return true;
839+
}
840+
784841
public:
785842
HeapState()
786843
: base(nullptr), reserved_size(0), num_segments(0), layout(),
@@ -827,9 +884,7 @@ class HeapState {
827884

828885
page_kind_t kind = class_for_size(size);
829886
if (kind == PAGE_XL) {
830-
// goto xl path?
831-
const size_t large_fit_limit = LARGE_PAGE_SIZE - sizeof(ChunkHeader);
832-
if (size <= large_fit_limit) {
887+
if (size <= LARGE_PAGE_SIZE) {
833888
kind = PAGE_LG;
834889
} else {
835890
return alloc_xl(size);
@@ -972,15 +1027,10 @@ class HeapState {
9721027
return true;
9731028

9741029
ThreadCache *tc = ThreadCache::current();
975-
976-
ChunkHeader *chdr = reinterpret_cast<ChunkHeader *>(
977-
static_cast<char *>(ptr) - sizeof(ChunkHeader));
978-
if (chdr->magic == CHUNK_MAGIC && chdr->owner) {
979-
Page *page = chdr->owner;
980-
Segment *seg = page->get_owner_segment();
981-
if (!seg)
982-
return false;
983-
1030+
size_t seg_idx = 0;
1031+
Segment *seg = nullptr;
1032+
Page *page = nullptr;
1033+
if (resolve_page_for_ptr(ptr, &seg_idx, &seg, &page)) {
9841034
const page_kind_t kind = page->get_size_class();
9851035
const pid_t owner_tid = page->get_owner_tid();
9861036
const bool remote_owner = (owner_tid != 0 && owner_tid != tc->get_tid());
@@ -999,10 +1049,10 @@ class HeapState {
9991049
}
10001050

10011051
if (!ok)
1002-
std::abort();
1052+
return false;
10031053

10041054
if (!remote_owner && before == FULL && after != FULL) {
1005-
enqueue_non_full_segment(kind, page->get_segment_index());
1055+
enqueue_non_full_segment(kind, seg_idx);
10061056
}
10071057

10081058
if (tc->get_active()) {
@@ -1024,10 +1074,11 @@ class HeapState {
10241074
if (!ptr)
10251075
return 0;
10261076

1027-
ChunkHeader *chdr = reinterpret_cast<ChunkHeader *>(
1028-
static_cast<char *>(ptr) - sizeof(ChunkHeader));
1029-
if (chdr->magic == CHUNK_MAGIC && chdr->owner)
1030-
return chdr->owner->usable_size(ptr);
1077+
size_t seg_idx = 0;
1078+
Segment *seg = nullptr;
1079+
Page *page = nullptr;
1080+
if (resolve_page_for_ptr(ptr, &seg_idx, &seg, &page))
1081+
return page->usable_size(ptr);
10311082

10321083
return usable_xl(ptr);
10331084
}

0 commit comments

Comments
 (0)