Add validate to freelist::Builder

The free list builder in a checked build will only validate entries when
they are removed.  This commit adds a validate method, so they can be
checked during teardown.  This means that programs that leak memory
will still fail if the free list has become corrupt.
This commit is contained in:
Matthew Parkinson
2021-10-18 12:06:26 +01:00
committed by Matthew Parkinson
parent 93bb037c64
commit 6db9a2f0e2
2 changed files with 61 additions and 1 deletions

View File

@@ -330,12 +330,31 @@ namespace snmalloc
return chunk_record;
}
template<bool check_slabs = false>
SNMALLOC_SLOW_PATH void dealloc_local_slabs(sizeclass_t sizeclass)
{
// Return unused slabs of sizeclass_t back to global allocator
alloc_classes[sizeclass].queue.filter([this, sizeclass](Metaslab* meta) {
auto domesticate =
[this](freelist::QueuePtr p) SNMALLOC_FAST_PATH_LAMBDA {
auto res =
capptr_domesticate<SharedStateHandle>(backend_state_ptr(), p);
#ifdef SNMALLOC_TRACING
if (res.unsafe_ptr() != p.unsafe_ptr())
printf(
"Domesticated %p to %p!\n", p.unsafe_ptr(), res.unsafe_ptr());
#endif
return res;
};
if (meta->needed() != 0)
{
if (check_slabs)
{
meta->free_queue.validate(entropy.get_free_list_key(), domesticate);
}
return false;
}
alloc_classes[sizeclass].length--;
alloc_classes[sizeclass].unused--;
@@ -789,7 +808,7 @@ namespace snmalloc
// We may now have unused slabs, return to the global allocator.
for (sizeclass_t sizeclass = 0; sizeclass < NUM_SIZECLASSES; sizeclass++)
{
dealloc_local_slabs(sizeclass);
dealloc_local_slabs<true>(sizeclass);
}
return posted;

View File

@@ -116,6 +116,13 @@ namespace snmalloc
template<SNMALLOC_CONCEPT(capptr::ConceptBound) BQueue>
class T
{
template<
bool,
bool,
SNMALLOC_CONCEPT(capptr::ConceptBound),
SNMALLOC_CONCEPT(capptr::ConceptBound)>
friend class Builder;
friend class Object;
union
@@ -699,6 +706,40 @@ namespace snmalloc
init();
return {first, last};
}
template<typename Domesticator>
SNMALLOC_FAST_PATH void
validate(const FreeListKey& key, Domesticator domesticate)
{
#ifdef SNMALLOC_CHECK_CLIENT
for (uint32_t i = 0; i < LENGTH; i++)
{
if (&head[i] == end[i])
{
SNMALLOC_ASSERT(length[i] == 0);
continue;
}
size_t count = 1;
auto curr = read_head(i, key);
auto prev = get_fake_signed_prev(i, key);
while (true)
{
curr->check_prev(prev);
if (address_cast(&(curr->next_object)) == address_cast(end[i]))
break;
count++;
auto next = curr->read_next(key, domesticate);
prev = signed_prev(address_cast(curr), address_cast(next), key);
curr = next;
}
SNMALLOC_ASSERT(count == length[i]);
}
#else
UNUSED(key);
UNUSED(domesticate);
#endif
}
};
} // namespace freelist
} // namespace snmalloc