NFC: Introduce pointer domestication backend support

`capptr_domesticate<Backend>(Backend::LocalState*, CapPtr<T, B>)` is the
intended affordance for conversion to covert from a `CapPtr<T, B>` with
`B::wildness` `Wild` to a `CapPtr<>` with `B::with_wildness<Tame>` and thence
plumbed into the rest of the machinery.

David added the SFINAE wrapper so that `Backend`-s now don't need to implement a
domestication callback; instead, if the `Backend` does not provide a
`capptr_domesticate` function, a default, which just does an explicit type cast,
will be used instead.

This is not yet hooked into the rest of the tree.

Co-authored-by: David Chisnall <David.Chisnall@microsoft.com>
This commit is contained in:
Nathaniel Wesley Filardo
2021-08-16 21:03:29 +01:00
committed by Nathaniel Wesley Filardo
parent 7e53a2e82a
commit 1a608e6066
6 changed files with 133 additions and 1 deletions

View File

@@ -294,6 +294,23 @@ namespace snmalloc
UNUSED(ls);
concretePagemap.register_range(p, sz);
}
/**
* Return the bounds of the memory this back-end manages as a pair of
* addresses (start then end). This is available iff this is a
* fixed-range Backend.
*/
template<bool fixed_range_ = fixed_range>
static SNMALLOC_FAST_PATH
std::enable_if_t<fixed_range_, std::pair<address_t, address_t>>
get_bounds(LocalState* local_state)
{
static_assert(
fixed_range_ == fixed_range, "Don't set SFINAE parameter!");
UNUSED(local_state);
return concretePagemap.get_bounds();
}
};
private:

View File

@@ -58,6 +58,24 @@ namespace snmalloc
concept ConceptBackendMetaRange =
ConceptBackendMeta<Meta> && ConceptBackendMeta_Range<Meta>;
/**
* The backend also defines domestication (that is, the difference between
* Tame and Wild CapPtr bounds). It exports the intended affordance for
* testing a Wild pointer and either returning nullptr or the original
* pointer, now Tame.
*/
template<typename Globals>
concept ConceptBackendDomestication =
requires(typename Globals::LocalState* ls,
capptr::AllocWild<void> ptr)
{
{ Globals::capptr_domesticate(ls, ptr) }
-> ConceptSame<capptr::Alloc<void>>;
{ Globals::capptr_domesticate(ls, ptr.template as_static<char>()) }
-> ConceptSame<capptr::Alloc<char>>;
};
class CommonConfig;
struct Flags;
@@ -89,6 +107,7 @@ namespace snmalloc
typename Globals::GlobalPoolState;
{ Globals::pool() } -> ConceptSame<typename Globals::GlobalPoolState &>;
};
/**

View File

@@ -1,7 +1,6 @@
#pragma once
#include "../ds/defines.h"
#include "../mem/remotecache.h"
namespace snmalloc
{
@@ -137,4 +136,54 @@ namespace snmalloc
return true;
}
namespace detail
{
/**
* SFINAE helper, calls capptr_domesticate in the backend if it exists.
*/
template<
SNMALLOC_CONCEPT(ConceptBackendDomestication) Backend,
typename T,
SNMALLOC_CONCEPT(capptr::ConceptBound) B>
SNMALLOC_FAST_PATH_INLINE auto
capptr_domesticate(typename Backend::LocalState* ls, CapPtr<T, B> p, int)
-> decltype(Backend::capptr_domesticate(ls, p))
{
return Backend::capptr_domesticate(ls, p);
}
/**
* SFINAE helper. If the back end does not provide special handling for
* domestication then assume all wild pointers can be domesticated.
*/
template<
SNMALLOC_CONCEPT(ConceptBackendGlobals) Backend,
typename T,
SNMALLOC_CONCEPT(capptr::ConceptBound) B>
SNMALLOC_FAST_PATH_INLINE auto
capptr_domesticate(typename Backend::LocalState*, CapPtr<T, B> p, long)
{
return CapPtr<
T,
typename B::template with_wildness<capptr::dimension::Wildness::Tame>>(
p.unsafe_ptr());
}
} // namespace detail
/**
* Wrapper that calls `Backend::capptr_domesticate` if and only if it is
* implemented. If it is not implemented then this assumes that any wild
* pointer can be domesticated.
*/
template<
SNMALLOC_CONCEPT(ConceptBackendGlobals) Backend,
typename T,
SNMALLOC_CONCEPT(capptr::ConceptBound) B>
SNMALLOC_FAST_PATH_INLINE auto
capptr_domesticate(typename Backend::LocalState* ls, CapPtr<T, B> p)
{
return detail::capptr_domesticate<Backend>(ls, p, 0);
}
} // namespace snmalloc
#include "../mem/remotecache.h"

View File

@@ -51,5 +51,33 @@ namespace snmalloc
{
Backend::init(local_state, base, length);
}
/* Verify that a pointer points into the region managed by this config */
template<typename T, SNMALLOC_CONCEPT(capptr::ConceptBound) B>
static SNMALLOC_FAST_PATH CapPtr<
T,
typename B::template with_wildness<capptr::dimension::Wildness::Tame>>
capptr_domesticate(typename Backend::LocalState* ls, CapPtr<T, B> p)
{
static_assert(B::wildness == capptr::dimension::Wildness::Wild);
static const size_t sz = sizeof(
std::conditional<std::is_same_v<std::remove_cv<T>, void>, void*, T>);
UNUSED(ls);
auto address = address_cast(p);
auto bounds = Backend::Pagemap::get_bounds(nullptr);
if (
(address < bounds.first) || (address > bounds.second) ||
((bounds.second - address) < sz))
{
return nullptr;
}
return CapPtr<
T,
typename B::template with_wildness<capptr::dimension::Wildness::Tame>>(
p.unsafe_ptr());
}
};
}

View File

@@ -205,6 +205,15 @@ namespace snmalloc
body_opt = new_body;
}
template<bool has_bounds_ = has_bounds>
std::enable_if_t<has_bounds_, std::pair<address_t, size_t>> get_bounds()
{
static_assert(
has_bounds_ == has_bounds, "Don't set SFINAE template parameter!");
return {base, size};
}
/**
* Get the number of entries.
*/

View File

@@ -569,6 +569,11 @@ namespace snmalloc
#ifdef SNMALLOC_PASS_THROUGH
return external_alloc::malloc_usable_size(const_cast<void*>(p_raw));
#else
// TODO What's the domestication policy here? At the moment we just probe
// the pagemap with the raw address, without checks. There could be
// implicit domestication through the `SharedStateHandle::Pagemap` or we
// could just leave well enough alone.
// Note that this should return 0 for nullptr.
// Other than nullptr, we know the system will be initialised as it must
// be called with something we have already allocated.
@@ -600,6 +605,11 @@ namespace snmalloc
void* external_pointer(void* p_raw)
{
#ifndef SNMALLOC_PASS_THROUGH
// TODO What's the domestication policy here? At the moment we just probe
// the pagemap with the raw address, without checks. There could be
// implicit domestication through the `SharedStateHandle::Pagemap` or we
// could just leave well enough alone.
// TODO bring back the CHERI bits. Wes to review if required.
MetaEntry entry =
SharedStateHandle::Pagemap::template get_metaentry<true>(