in src/mem/localalloc.h [584:674]
SNMALLOC_FAST_PATH void dealloc(void* p_raw)
{
#ifdef SNMALLOC_PASS_THROUGH
external_alloc::free(p_raw);
#else
// Care is needed so that dealloc(nullptr) works before init
// The backend allocator must ensure that a minimal page map exists
// before init, that maps null to a remote_deallocator that will never
// be in thread local state.
# ifdef __CHERI_PURE_CAPABILITY__
/*
* On CHERI platforms, snap the provided pointer to its base, ignoring
* any client-provided offset, which may have taken the pointer out of
* bounds and so appear to designate a different object. The base is
* is guaranteed by monotonicity either...
* * to be within the bounds originally returned by alloc(), or
* * one past the end (in which case, the capability length must be 0).
*
* Setting the offset does not trap on untagged capabilities, so the tag
* might be clear after this, as well.
*
* For a well-behaved client, this is a no-op: the base is already at the
* start of the allocation and so the offset is zero.
*/
p_raw = __builtin_cheri_offset_set(p_raw, 0);
# endif
capptr::AllocWild<void> p_wild = capptr_from_client(p_raw);
/*
* p_tame may be nullptr, even if p_raw/p_wild are not, in the case
* where domestication fails. We exclusively use p_tame below so that
* such failures become no ops; in the nullptr path, which should be
* well off the fast path, we could be slightly more aggressive and test
* that p_raw is also nullptr and Pal::error() if not. (TODO)
*
* We do not rely on the bounds-checking ability of domestication here,
* and just check the address (and, on other architectures, perhaps
* well-formedness) of this pointer. The remainder of the logic will
* deal with the object's extent.
*/
capptr::Alloc<void> p_tame = capptr_domesticate<SharedStateHandle>(
core_alloc->backend_state_ptr(), p_wild);
const MetaEntry& entry = SharedStateHandle::Pagemap::get_metaentry(
core_alloc->backend_state_ptr(), address_cast(p_tame));
if (SNMALLOC_LIKELY(local_cache.remote_allocator == entry.get_remote()))
{
# if defined(__CHERI_PURE_CAPABILITY__) && defined(SNMALLOC_CHECK_CLIENT)
dealloc_cheri_checks(p_tame.unsafe_ptr());
# endif
if (SNMALLOC_LIKELY(CoreAlloc::dealloc_local_object_fast(
entry, p_tame, local_cache.entropy)))
return;
core_alloc->dealloc_local_object_slow(entry);
return;
}
if (SNMALLOC_LIKELY(entry.get_remote() != nullptr))
{
# if defined(__CHERI_PURE_CAPABILITY__) && defined(SNMALLOC_CHECK_CLIENT)
dealloc_cheri_checks(p_tame.unsafe_ptr());
# endif
// Check if we have space for the remote deallocation
if (local_cache.remote_dealloc_cache.reserve_space(entry))
{
local_cache.remote_dealloc_cache.template dealloc<sizeof(CoreAlloc)>(
entry.get_remote()->trunc_id(), p_tame, key_global);
# ifdef SNMALLOC_TRACING
std::cout << "Remote dealloc fast" << p_raw << " size "
<< alloc_size(p_raw) << std::endl;
# endif
return;
}
dealloc_remote_slow(p_tame);
return;
}
// If p_tame is not null, then dealloc has been call on something
// it shouldn't be called on.
// TODO: Should this be tested even in the !CHECK_CLIENT case?
snmalloc_check_client(p_tame == nullptr, "Not allocated by snmalloc.");
# ifdef SNMALLOC_TRACING
std::cout << "nullptr deallocation" << std::endl;
# endif
return;
#endif
}