私はstd::array
がスタックに完全に割り当てられていることを知っていますが、この質問は次の2つを必要とするセキュリティ上の懸念から動機付けられています。
std::array
のデータは、破棄時にゼロ化またはランダム化されますstd::array
のデータは locked となり、クラッシュまたはスワップメモリのどちらでもディスクに移動しません。通常、std::vector
では、解決策は カスタムアロケータ を作成することです これらのことを行う 。ただし、std::array
の場合、これを行う方法がわからないため、この質問が表示されます。
私ができる最善のことはこれです:
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);
}
}
しかし、これには明らかにメモリロックが欠けており、std::array
を最初に使用することで得られるstd::array
のパフォーマンススキームが複雑になります。
より良い解決策はありますか?
std::array
はアロケータを使用できません。ただし、SecureArrayクラスは、カスタムコンストラクター/デコンストラクターを使用して必要なことを実現できるようです。
このようなもの:
#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);
}
};
std::array
がスタックに完全に割り当てられていることを知っています
これは真実ではありません。 std::array
はメモリを割り当てないため、メモリを割り当てる場所によって異なります。
auto* arr = new std::array<int, 100>(); // BUM! it is allocated on the heap
しかし、これには明らかにメモリロックが欠けており、
std::array
を最初に使用することで得られるstd::array
のパフォーマンススキームが複雑になります。
まず、スタック上のメモリをロックすることは問題ではありません。 POSIXの例を参照してください。
#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;
}
}
したがって、mlock
コンストラクターでSecureArray
または任意のポータブルアナログを呼び出すことができます。
次に、どのようなパフォーマンスの向上が見込めますか?メモリの読み取り/書き込み速度は、配列を割り当てる場所、ヒープ、スタックに依存しません。つまり、メモリの割り当てとロックをどれだけ速く行えるかがすべてです。パフォーマンスが重要な場合、メモリがスタックに割り当てられている場合でも、メモリロックが遅すぎて(またはそうでない場合は、誰が知っているか)、SecureArray
コンストラクタで毎回呼び出すことができません。
したがって、カスタムアロケータでstd::vector
を使用する方が便利です。大きなメモリチャンクを事前に割り当てて事前にロックする可能性があるため、割り当て速度はスタックの場合とほぼ同じです。