You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
123 lines
4.1 KiB
123 lines
4.1 KiB
//! Custom allocation framekwork for frozen (RO) and/or secure allocations, shared allocations, aliased allocation, etc. (polymorphic.)
|
|
#ifndef _ALLOC_H
|
|
#define _ALLOC_H
|
|
|
|
#ifdef __cplusplus
|
|
#include <memory>
|
|
#include <pair>
|
|
#endif
|
|
|
|
#include "constraints.hh"
|
|
#include "types.h"
|
|
#include "macros.h"
|
|
|
|
LINK_START(C)
|
|
typedef struct base_allocator alloc_t;
|
|
|
|
//TODO: C API anonlogue for allocator interface below.
|
|
|
|
LINK_END
|
|
|
|
#if $CXX
|
|
extern "C" {
|
|
struct base_allocator {
|
|
//TODO: Allocator interface...
|
|
virtual ~base_allocator();
|
|
}
|
|
}
|
|
namespace alloc {
|
|
class FrozenAllocator : public alloc_t {
|
|
struct _impl;
|
|
protected:
|
|
struct anon_raw_secmem;
|
|
// Untyped deleter
|
|
struct deleter {
|
|
friend class anon_raw_secmem;
|
|
|
|
constexpr deleter(const deleter&) noexcept = default;
|
|
constexpr deleter& operator=(const deleter&) noexcept = default;
|
|
constexpr deleter(deleter&&) noexcept = default;
|
|
constexpr deleter& operator=(deleter&&) noexcept = default;
|
|
|
|
[[gnu::nonnull(2)]]
|
|
constexpr void operator()(void* restrict p) const noexcept { apply_delete(p); }
|
|
|
|
virtual ~deleter();
|
|
protected:
|
|
explicit deleter(std::shared_ptr<anon_raw_secmem>&& p);
|
|
|
|
[[gnu::nonnull(2)]]
|
|
virtual std::pair<void*> apply_delete(void* restrict, bool = true) const noexcept;
|
|
virtual void scramble_memory(std::initializer_list<std::pair<void*>> ptrs) const noexcept;
|
|
inline void scramble_memory(std::pair<void*> ptr) const noexcept { return scramble_memory({ ptr }); }
|
|
|
|
|
|
// To prevent anon_raw_secmem being destroyed while there are still allocated values, the base class for the deleter for those values contains a refcount. e.g: `std::unique_ptr<T, deleter_for<T>>` where: `deleter_for<T> final : public deleter { virtual ~deleter_for(); ... };`, or `std::shared_ptr<T>`, where: `std::shared_ptr<value_with_deleter<T>>` aliases-ctor(`old, old->value_ptr()`) -> `std::shared_ptr<T>`
|
|
std::shared_ptr<anon_raw_secmem> m_manager_ref;
|
|
};
|
|
struct alloc_vt;
|
|
struct alloc_info;
|
|
struct alloc_value;
|
|
|
|
template<typename>
|
|
struct deleter_for;
|
|
template<typename>
|
|
struct deleter_for_value;
|
|
public:
|
|
FrozenAllocator(FrozenAllocator &&) noexcept;
|
|
FrozenAllocator& operator=(FrozenAllocator &&);
|
|
|
|
FrozenAllocator(const FrozenAllocator&) = delete;
|
|
FrozenAllocator& operator=(const FrozenAllocator&) = delete;
|
|
virtual ~FrozenAllocator();
|
|
private:
|
|
std::unique_ptr<_impl> inner_;
|
|
|
|
/// Manages everything about the actual allocations
|
|
//std::shared_ptr<anon_raw_secmem> m_manager;
|
|
/// A map of values inside the allocator. This is destroyed in reverse order, meaning all the living values are destroyed *before* `m_manager`'d destruction deallocates them.
|
|
//std::map<alloc_info, std::unique_ptr<alloc_value, deleter>> m_values;
|
|
};
|
|
template<typename T>
|
|
struct FrozenAllocator::deleter_for
|
|
: virtual deleter
|
|
{
|
|
inline deleter_for(std::shared_ptr<anon_raw_secmem>&& m)
|
|
: deleter(std::move(m)) {}
|
|
virtual ~deleter_for() = default; // This will use deleter's dtor to remove allocations.
|
|
|
|
[[gnu::nonnull(2)]]
|
|
inline void operator()(T* ptr) const noexcept { apply_delete(deleter::erase_type_unsafe(ptr)); }
|
|
|
|
protected:
|
|
inline virtual void apply_delete_typed(T* ptr) const noexcept {
|
|
ptr->~T();
|
|
}
|
|
private:
|
|
[[gnu::nonnull(2)]]
|
|
inline std::pair<void*> apply_delete(void* restrict up) const noexcept override final {
|
|
if constexpr(std::is_trivially_destructible_v<T>) {
|
|
return deleter::apply_delete(up); // If the dtor is trivial, ignore it and use default behaviour.
|
|
} else {
|
|
auto pair = deleter::apply_delete(up, false); // Unlock the memory and remove the allocation from `anon_raw_secmem`, but do *not* bzero it.
|
|
apply_delete_typed(static_cast<T*>(up)); // Apply the destructor for `T` to the alligned pointer `up`.
|
|
deleter::scramble_memory(pair); // *now* bzero the unaligned pointer (full range, including alignment padding.)
|
|
return pair;
|
|
}
|
|
}
|
|
};
|
|
template<typename T>
|
|
struct FrozenAllocator::deleter_for_value final : deleter_for<T> {
|
|
//TODO: Re-work this? Is it useful? Is it needed?
|
|
|
|
inline virtual ~deleter_for_value() {
|
|
deleter_for<T>::apply_delete(m_value_ptr);
|
|
}
|
|
private:
|
|
T* m_value_ptr;
|
|
};
|
|
}
|
|
#endif
|
|
|
|
#endif /* _ALLOC_H */
|