Skip to content

Fix inconsistent supervisor heap #3482

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Oct 17, 2020
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 27 additions & 7 deletions supervisor/shared/memory.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,14 @@

#define CIRCUITPY_SUPERVISOR_ALLOC_COUNT (12)

// Using a zero length to mark an unused allocation makes the code a bit shorter (but makes it
// impossible to support zero-length allocations).
#define FREE 0

// The lowest two bits of a valid length are always zero, so we can use them to mark an allocation
// as freed by the client but not yet reclaimed into the FREE middle.
#define HOLE 1

static supervisor_allocation allocations[CIRCUITPY_SUPERVISOR_ALLOC_COUNT];
// We use uint32_t* to ensure word (4 byte) alignment.
uint32_t* low_address;
Expand Down Expand Up @@ -61,25 +69,31 @@ void free_memory(supervisor_allocation* allocation) {
}
if (allocation->ptr == high_address) {
high_address += allocation->length / 4;
allocation->length = FREE;
for (index++; index < CIRCUITPY_SUPERVISOR_ALLOC_COUNT; index++) {
if (allocations[index].ptr != NULL) {
if (!(allocations[index].length & HOLE)) {
break;
}
// Division automatically shifts out the HOLE bit.
high_address += allocations[index].length / 4;
allocations[index].length = FREE;
}
} else if (allocation->ptr + allocation->length / 4 == low_address) {
low_address = allocation->ptr;
allocation->length = FREE;
for (index--; index >= 0; index--) {
if (allocations[index].ptr != NULL) {
if (!(allocations[index].length & HOLE)) {
break;
}
low_address -= allocations[index].length / 4;
allocations[index].length = FREE;
}
} else {
// Freed memory isn't in the middle so skip updating bounds. The memory will be added to the
// middle when the memory to the inside is freed.
// middle when the memory to the inside is freed. We still need its length, but setting
// only the lowest bit is nondestructive.
allocation->length |= HOLE;
}
allocation->ptr = NULL;
}

supervisor_allocation* allocation_from_ptr(void *ptr) {
Expand All @@ -99,7 +113,7 @@ supervisor_allocation* allocate_remaining_memory(void) {
}

supervisor_allocation* allocate_memory(uint32_t length, bool high) {
if ((high_address - low_address) * 4 < (int32_t) length || length % 4 != 0) {
if (length == 0 || length % 4 != 0) {
return NULL;
}
uint8_t index = 0;
Expand All @@ -108,15 +122,21 @@ supervisor_allocation* allocate_memory(uint32_t length, bool high) {
index = CIRCUITPY_SUPERVISOR_ALLOC_COUNT - 1;
direction = -1;
}
supervisor_allocation* alloc;
for (; index < CIRCUITPY_SUPERVISOR_ALLOC_COUNT; index += direction) {
if (allocations[index].ptr == NULL) {
alloc = &allocations[index];
if (alloc->length == FREE && (high_address - low_address) * 4 >= (int32_t) length) {
break;
}
// If a hole matches in length exactly, we can reuse it.
if (alloc->length == (length | HOLE)) {
alloc->length = length;
return alloc;
}
}
if (index >= CIRCUITPY_SUPERVISOR_ALLOC_COUNT) {
return NULL;
}
supervisor_allocation* alloc = &allocations[index];
if (high) {
high_address -= length / 4;
alloc->ptr = high_address;
Expand Down