1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-26 02:47:34 +00:00

LibJS: Fix undefined behavior in HeapBlock

In C++, it's invalid to cast a block of memory to a complex type without
invoking its constructor. It's even more invalid to simply cast a pointer to a
block of memory to a pointer to *an abstract type*.

To fix this, make sure FreelistEntry is a concrete type, and call its
constructor whenever appropriate.
This commit is contained in:
Sergey Bugaev 2020-06-01 17:01:04 +03:00 committed by Andreas Kling
parent 53a94b8bbd
commit 2fbc37befc
2 changed files with 23 additions and 12 deletions

View file

@ -61,15 +61,16 @@ HeapBlock::HeapBlock(Heap& heap, size_t cell_size)
: m_heap(heap) : m_heap(heap)
, m_cell_size(cell_size) , m_cell_size(cell_size)
{ {
for (size_t i = 0; i < cell_count(); ++i) { ASSERT(cell_size >= sizeof(FreelistEntry));
auto* freelist_entry = static_cast<FreelistEntry*>(cell(i));
FreelistEntry* next = nullptr;
for (ssize_t i = cell_count() - 1; i >= 0; i--) {
auto* freelist_entry = init_freelist_entry(i);
freelist_entry->set_live(false); freelist_entry->set_live(false);
if (i == cell_count() - 1) freelist_entry->next = next;
freelist_entry->next = nullptr; next = freelist_entry;
else
freelist_entry->next = static_cast<FreelistEntry*>(cell(i + 1));
} }
m_freelist = static_cast<FreelistEntry*>(cell(0)); m_freelist = next;
} }
Cell* HeapBlock::allocate() Cell* HeapBlock::allocate()
@ -84,7 +85,7 @@ void HeapBlock::deallocate(Cell* cell)
ASSERT(cell->is_live()); ASSERT(cell->is_live());
ASSERT(!cell->is_marked()); ASSERT(!cell->is_marked());
cell->~Cell(); cell->~Cell();
auto* freelist_entry = static_cast<FreelistEntry*>(cell); auto* freelist_entry = new (cell) FreelistEntry();
freelist_entry->set_live(false); freelist_entry->set_live(false);
freelist_entry->next = m_freelist; freelist_entry->next = m_freelist;
m_freelist = freelist_entry; m_freelist = freelist_entry;

View file

@ -42,8 +42,6 @@ public:
size_t cell_size() const { return m_cell_size; } size_t cell_size() const { return m_cell_size; }
size_t cell_count() const { return (block_size - sizeof(HeapBlock)) / m_cell_size; } size_t cell_count() const { return (block_size - sizeof(HeapBlock)) / m_cell_size; }
Cell* cell(size_t index) { return reinterpret_cast<Cell*>(&m_storage[index * cell_size()]); }
Cell* allocate(); Cell* allocate();
void deallocate(Cell*); void deallocate(Cell*);
@ -72,10 +70,22 @@ public:
private: private:
HeapBlock(Heap&, size_t cell_size); HeapBlock(Heap&, size_t cell_size);
struct FreelistEntry : public Cell { struct FreelistEntry final : public Cell {
FreelistEntry* next; FreelistEntry* next { nullptr };
virtual const char* class_name() const override { return "FreelistEntry"; }
}; };
Cell* cell(size_t index)
{
return reinterpret_cast<Cell*>(&m_storage[index * cell_size()]);
}
FreelistEntry* init_freelist_entry(size_t index)
{
return new (&m_storage[index * cell_size()]) FreelistEntry();
}
Heap& m_heap; Heap& m_heap;
size_t m_cell_size { 0 }; size_t m_cell_size { 0 };
FreelistEntry* m_freelist { nullptr }; FreelistEntry* m_freelist { nullptr };