I know std::array
is completely allocated in the stack, but this question is motivated by security concerns that require two things:
std::array
will be zerod or randomized on destructionstd::array
will be locked, such that it never goes to disk neither on crash or on swap memoryUsually, with std::vector
, the solution is to create a custom allocator that does these things. However, for std::array
, I don't see how to do this, and hence this question.
The best I could do is this:
template <typename T, std::size_t Size>
struct SecureArray : public std::array<T, Size>
{
static_assert(std::is_pod<T>::value, "Only POD types allowed")
static_assert(sizeof(T) == 1, "Only 1-byte types allowed")
virtual ~SecureArray()
{
std::vector<uint8_t> d = RandomBytes(Size); // generates Size random bytes
std::memcpy(this->data(), d.data(), Size);
}
}
But this obviously lacks memory locking and complicates the performance scheme of std::array
that is to be gained by using std::array
in the first place.
Is there any better solution?
std::allocator is used when you want to separate allocation and do construction in two steps. It is also used when separate destruction and deallocation is done in two steps. All the STL containers in C++ have a type parameter Allocator that is by default std::allocator.
Allocators handle all the requests for allocation and deallocation of memory for a given container. The C++ Standard Library provides general-purpose allocators that are used by default, however, custom allocators may also be supplied by the programmer.
std::array
cannot use an allocator; however, it seems like your SecureArray class can achieve what you want through a custom constructor/deconstructor.
Something like this:
#include <sys/mman.h>
template<class T, std::size_t Size>
struct SecureArray : public std::array<T, Size>
{
// Your static_asserts...
SecureArray(void) {
mlock(std::array<T, Size>::data(), sizeof(T) * Size);
}
~SecureArray(void) {
char *bytes = reinterpret_cast<char *>(std::array<T, Size>::data());
for (std::size_t i = 0; i < sizeof(T) * Size; i++)
bytes[i] = 0;
munlock(bytes, sizeof(T) * N);
}
};
I know
std::array
is completely allocated in the stack
This is not quite true. std::array
doesn't allocate any memory, so it depends on where you allocate it.
auto* arr = new std::array<int, 100>(); // BUM! it is allocated on the heap
But this obviously lacks memory locking and complicates the performance scheme of
std::array
that is to be gained by usingstd::array
in the first place.
Firstly, it is not a problem to lock memory on the stack. See POSIX example:
#include <iostream>
#include <sys/mman.h>
#include <array>
int main()
{
std::array<int, 3> a = {1, 2, 3}; // std::array allocated on the stack
if (mlock(a.data(), sizeof(a)) == 0)
{
std::cout << "LOCKED" << std::endl;
}
}
So, you can just call mlock
or any portable analog in SecureArray
constructor.
Secondly, what performance gain do you expect to get? Memory reading/writing speed doesn't depend on where you allocate your array, on the heap or on the stack. So, it is all about how fast you can allocate and lock the memory. If performance is critical, memory locking may be too slow (or not, who knows?) to call it every time in SecureArray
constructor even if memory is allocated on the stack.
So, it is more handy to use std::vector
with custom allocator. It may preallocate and prelock big memory chunks, so allocation speed will be almost as fast as on the stack.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With