Towards heap walk (#569)

* Implement tracking full slabs and large allocations

This adds an additional SeqSet that is used to track all the fully
used slabs and large allocations.  This gives more chances to
detect memory leaks, and additionally catch some more UAF failures
where the object is not recycled.

* Make slabmeta track a slab interior pointer

Use the head of the free list builder to track an interior pointer to
the slab. This is unused unless the list contains something.
Hence, we can use this to represent an interior pointer to the slab and
report more accurate leaks.

* clangformat

* clangtidy

* clangtidy

* Clang tidy again.

* Fixing provenance.

* Clangformat

* Clang tidy.

* Add assert for sanity

* Make reinterpret_cast more descriptive.

Add an operation to get a tag free pointer from an address_t, and use it

* Clangformat

* CR

* Fix calculation of number of allocations.

* Fix calculation of number of allocations.

* Fix test
This commit is contained in:
Matthew Parkinson
2022-12-20 13:36:10 +00:00
committed by GitHub
parent 704843d5ff
commit 4e88b42621
8 changed files with 199 additions and 51 deletions

View File

@@ -1,16 +1,27 @@
#include <snmalloc/snmalloc.h>
#ifdef SNMALLOC_PASS_THROUGH // This test depends on snmalloc internals
int main()
{
#ifndef SNMALLOC_PASS_THROUGH // This test depends on snmalloc internals
return 0;
}
#else
# include <iostream>
# include <snmalloc/snmalloc.h>
# include <vector>
template<size_t size>
void debug_check_empty_1()
{
std::cout << "debug_check_empty_1 " << size << std::endl;
snmalloc::Alloc& a = snmalloc::ThreadAlloc::get();
bool result;
auto r = a.alloc(16);
auto r = a.alloc(size);
snmalloc::debug_check_empty<snmalloc::StandardConfig>(&result);
if (result != false)
{
std::cout << "debug_check_empty failed to detect leaked memory:" << size
<< std::endl;
abort();
}
@@ -19,14 +30,17 @@ int main()
snmalloc::debug_check_empty<snmalloc::StandardConfig>(&result);
if (result != true)
{
std::cout << "debug_check_empty failed to say empty:" << size << std::endl;
abort();
}
r = a.alloc(16);
r = a.alloc(size);
snmalloc::debug_check_empty<snmalloc::StandardConfig>(&result);
if (result != false)
{
std::cout << "debug_check_empty failed to detect leaked memory:" << size
<< std::endl;
abort();
}
@@ -35,7 +49,70 @@ int main()
snmalloc::debug_check_empty<snmalloc::StandardConfig>(&result);
if (result != true)
{
std::cout << "debug_check_empty failed to say empty:" << size << std::endl;
abort();
}
#endif
}
template<size_t size>
void debug_check_empty_2()
{
std::cout << "debug_check_empty_2 " << size << std::endl;
snmalloc::Alloc& a = snmalloc::ThreadAlloc::get();
bool result;
std::vector<void*> allocs;
// 1GB of allocations
size_t count = snmalloc::bits::min<size_t>(2048, 1024 * 1024 * 1024 / size);
for (size_t i = 0; i < count; i++)
{
if (i % (count / 16) == 0)
{
std::cout << "." << std::flush;
}
auto r = a.alloc(size);
allocs.push_back(r);
snmalloc::debug_check_empty<snmalloc::StandardConfig>(&result);
if (result != false)
{
std::cout << "False empty after " << i << " allocations of " << size
<< std::endl;
abort();
}
}
std::cout << std::endl;
for (size_t i = 0; i < count; i++)
{
if (i % (count / 16) == 0)
{
std::cout << "." << std::flush;
}
snmalloc::debug_check_empty<snmalloc::StandardConfig>(&result);
if (result != false)
{
std::cout << "False empty after " << i << " deallocations of " << size
<< std::endl;
abort();
}
a.dealloc(allocs[i]);
}
std::cout << std::endl;
snmalloc::debug_check_empty<snmalloc::StandardConfig>();
}
int main()
{
debug_check_empty_1<16>();
debug_check_empty_1<16384>();
debug_check_empty_1<65536>();
debug_check_empty_1<1024 * 1024 * 32>();
debug_check_empty_2<32>();
debug_check_empty_2<16384>();
debug_check_empty_2<65535>();
debug_check_empty_2<1024 * 1024 * 32>();
return 0;
}
#endif