Clear freelist pointers on allocation for CHERI or CHECK_CLIENT builds.
This is especially important on CHERI to avoid leaking capabilities to the freelist. In the CHERI case we also zero in clear_slab (see comment). Also add a check in the malloc functional test that there are no valid capabilities in the returned allocation.
This commit is contained in:
committed by
Matthew Parkinson
parent
baf35cc80e
commit
af8ab2daf6
@@ -339,6 +339,17 @@ namespace snmalloc
|
||||
address_cast(start_of_slab) ==
|
||||
address_cast(chunk_record->meta_common.chunk));
|
||||
|
||||
#if defined(__CHERI_PURE_CAPABILITY__) && !defined(SNMALLOC_CHECK_CLIENT)
|
||||
// Zero the whole slab. For CHERI we at least need to clear the freelist
|
||||
// pointers to avoid leaking capabilities but we do not need to do it in
|
||||
// the freelist order as for SNMALLOC_CHECK_CLIENT. Zeroing the whole slab
|
||||
// may be more friendly to hw because it does not involve pointer chasing
|
||||
// and is amenable to prefetching.
|
||||
SharedStateHandle::Pal::zero(
|
||||
chunk_record->meta_common.chunk.unsafe_ptr(),
|
||||
snmalloc::sizeclass_to_slab_size(sizeclass));
|
||||
#endif
|
||||
|
||||
#ifdef SNMALLOC_TRACING
|
||||
std::cout << "Slab " << start_of_slab.unsafe_ptr()
|
||||
<< " is unused, Object sizeclass " << sizeclass << std::endl;
|
||||
|
||||
@@ -185,6 +185,21 @@ namespace snmalloc
|
||||
signed_prev == this->prev_encoded,
|
||||
"Heap corruption - free list corrupted!");
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean up this object when removing it from the list. This is
|
||||
* important on CHERI to avoid leaking capabilities. On CHECK_CLIENT
|
||||
* builds it might increase the difficulty to bypass the checks.
|
||||
*/
|
||||
void cleanup()
|
||||
{
|
||||
#if defined(__CHERI_PURE_CAPABILITY__) || defined(SNMALLOC_CHECK_CLIENT)
|
||||
this->next_object = nullptr;
|
||||
# ifdef SNMALLOC_CHECK_CLIENT
|
||||
this->prev_encoded = 0;
|
||||
# endif
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
// Note the inverted template argument order, since BView is inferable.
|
||||
@@ -467,7 +482,7 @@ namespace snmalloc
|
||||
#else
|
||||
UNUSED(key);
|
||||
#endif
|
||||
|
||||
c->cleanup();
|
||||
return c;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -60,6 +60,24 @@ void check_result(size_t size, size_t align, void* p, int err, bool null)
|
||||
"Cheri size is %zu, but required to be %zu.\n", cheri_size, alloc_size);
|
||||
failed = true;
|
||||
}
|
||||
if (p != nullptr)
|
||||
{
|
||||
/*
|
||||
* Scan the allocation for any tagged capabilities. Since this test doesn't
|
||||
* use the allocated memory if there is a valid cap it must have leaked from
|
||||
* the allocator, which is bad.
|
||||
*/
|
||||
void** vp = static_cast<void**>(p);
|
||||
for (size_t n = 0; n < alloc_size / sizeof(*vp); vp++, n++)
|
||||
{
|
||||
void* c = *vp;
|
||||
if (__builtin_cheri_tag_get(c))
|
||||
{
|
||||
printf("Found cap tag set in alloc: %#p at %#p\n", c, vp);
|
||||
failed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (exact_size && (alloc_size != expected_size) && (size != 0))
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user