mirror of
https://github.com/justinian/jsix.git
synced 2025-12-09 16:04:32 -08:00
[kernel] Fix freelist-clobber bug in heap allocator
The heap_allocator::get_free(order) function returns a reference to the head pointer of the given freelist, so that it can be manipulated. However, split_off was also taking a reference to a pointer for an out param - passing the freelist pointer in here caused split_off to modify the freelist. I cleaned up a bunch of the places the freelist pointers were being touched to make the usage more explicit.
This commit is contained in:
@@ -13,8 +13,9 @@ uint32_t & get_map_key(heap_allocator::block_info &info) { return info.offset; }
|
|||||||
|
|
||||||
struct heap_allocator::free_header
|
struct heap_allocator::free_header
|
||||||
{
|
{
|
||||||
void clear(unsigned new_order) {
|
void clear(unsigned new_order, free_header *new_next = nullptr) {
|
||||||
prev = next = nullptr;
|
prev = nullptr;
|
||||||
|
next = new_next;
|
||||||
order = new_order;
|
order = new_order;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,9 +105,11 @@ heap_allocator::free(void *p)
|
|||||||
heap_allocator::free_header *
|
heap_allocator::free_header *
|
||||||
heap_allocator::pop_free(unsigned order)
|
heap_allocator::pop_free(unsigned order)
|
||||||
{
|
{
|
||||||
free_header *block = get_free(order);
|
free_header *& head = get_free(order);
|
||||||
|
free_header *block = head;
|
||||||
if (block) {
|
if (block) {
|
||||||
get_free(order) = block->next;
|
kassert(block->prev == nullptr, "freelist head had non-null prev");
|
||||||
|
head = block->next;
|
||||||
block->remove();
|
block->remove();
|
||||||
}
|
}
|
||||||
return block;
|
return block;
|
||||||
@@ -119,14 +122,17 @@ heap_allocator::merge_block(free_header *block)
|
|||||||
|
|
||||||
unsigned order = block->order;
|
unsigned order = block->order;
|
||||||
while (order < max_order) {
|
while (order < max_order) {
|
||||||
block_info *info = m_map.find(map_key(block->buddy()));
|
free_header *buddy = block->buddy();
|
||||||
|
|
||||||
|
block_info *info = m_map.find(map_key(buddy));
|
||||||
if (!info || !info->free || info->order != order)
|
if (!info || !info->free || info->order != order)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
free_header *buddy = block->buddy();
|
free_header *&head = get_free(order);
|
||||||
if (get_free(order) == buddy)
|
if (head == buddy)
|
||||||
get_free(order) = buddy->next;
|
pop_free(order);
|
||||||
buddy->remove();
|
else
|
||||||
|
buddy->remove();
|
||||||
|
|
||||||
block = block->eldest() ? block : buddy;
|
block = block->eldest() ? block : buddy;
|
||||||
|
|
||||||
@@ -166,30 +172,31 @@ heap_allocator::register_free_block(free_header *block, unsigned order)
|
|||||||
info.free = true;
|
info.free = true;
|
||||||
info.order = order;
|
info.order = order;
|
||||||
|
|
||||||
block->clear(order);
|
free_header *& head = get_free(order);
|
||||||
block->next = get_free(order);
|
if (head)
|
||||||
get_free(order) = block;
|
head->prev = block;
|
||||||
|
|
||||||
|
block->clear(order, head);
|
||||||
|
head = block;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
heap_allocator::split_off(unsigned order, free_header *&block)
|
heap_allocator::split_off(unsigned order, free_header *&split)
|
||||||
{
|
{
|
||||||
// The lock needs to be held while calling split_off
|
// The lock needs to be held while calling split_off
|
||||||
|
|
||||||
const unsigned next = order + 1;
|
const unsigned next = order + 1;
|
||||||
if (next > max_order) {
|
if (next > max_order)
|
||||||
block = nullptr;
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
block = pop_free(next);
|
free_header *block = pop_free(next);
|
||||||
if (!block && !split_off(next, block))
|
if (!block && !split_off(next, block))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
block->order = order;
|
block->order = order;
|
||||||
free_header *buddy = block->buddy();
|
|
||||||
register_free_block(block->buddy(), order);
|
register_free_block(block->buddy(), order);
|
||||||
m_map[map_key(block)].order = order;
|
m_map[map_key(block)].order = order;
|
||||||
|
|
||||||
|
split = block;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -88,9 +88,9 @@ protected:
|
|||||||
/// Helper to get a block of the given order by splitting existing
|
/// Helper to get a block of the given order by splitting existing
|
||||||
/// larger blocks. Returns false if there were no larger blocks.
|
/// larger blocks. Returns false if there were no larger blocks.
|
||||||
/// \arg order Order (2^N) of the block we want
|
/// \arg order Order (2^N) of the block we want
|
||||||
/// \arg block [out] Receives a pointer to the requested block
|
/// \arg split [out] Receives a pointer to the requested block
|
||||||
/// \returns True if a split was done
|
/// \returns True if a split was done
|
||||||
bool split_off(unsigned order, free_header *&block);
|
bool split_off(unsigned order, free_header *&split);
|
||||||
|
|
||||||
uintptr_t m_start, m_end;
|
uintptr_t m_start, m_end;
|
||||||
size_t m_maxsize;
|
size_t m_maxsize;
|
||||||
|
|||||||
Reference in New Issue
Block a user