
◇ ::mlock(::VirtualLock)とは?
ブロックチェーンでは鍵を扱うため
Secure ZeroMemory・メモリロック・
メモリプロテクト・Secure Allocatorの実装が必須です。
このうちここではメモリロックを扱います。
◇ メモリロックとは?
搭載以上のメモリを実現する仕組みとして仮想メモリがございます。
この仮想メモリの保管先してドライブにスワップファイルというものが書き出されます。
このファイルにはメモリの内容が書き込まれており、
この仮想メモリによって
搭載されたメモリ容量以上のメモリを扱うことができます。
ここで……ブロックチェーンの「鍵」ですね。
この「鍵」はメモリ上で処理されるものですが、
その過程でメモリが不足するとスワップ行きになります。
そして、鍵の内容をスワップファイルに書き出されて残されると都合が悪いです。
そこで::mlockを使い動的確保したメモリをロックします。
注意点として普通に確保されたメモリをロックすることはできません。
※ ::mallocや::HeapAllocで確保された
「通常のヒープ」が普通に確保されたメモリです。
※ new演算子がメモリ確保に使用する関数も
::mallocや::HeapAllocですのでロック不可です。
::mmapや::VirtualAllocに専用のフラグを付与したメモリを確保する必要があります。
◇ Windowsで大丈夫?
Windows10以降なら心配ご無用です。
気になるバグがあったのはWindows7以前のOSでした。
この部分のコードに気になるコメント文がありました。
「Windowsの::VirtualLockは、稀に作用しない場合があります。まあ、大丈夫さ!」です。
実は……たしかにWindows7では稀に作用しない点を実際に確認いたしました。
気にはなりますが、OSレベルの問題となるのでなすすべがないです。
※ Windows10以降ならば心配ご無用です。
しっかり直してありました。
◇ サンプルコード
typedef std::uint8_t byte;
constexpr size_t alloc_info_size = sizeof(alloc_info);
inline void secure_memzero(void *v, size_t n)
{
static void *(*const volatile memset_v)(void *, int, size_t) = &memset;
memset_v(v, 0, n);
}
enum secure_type
{
LOCK_UNLOCK,
LOCK_UNLOCK_DUMMY
};
typedef _tag_alloc_info
{
secure_type type;
int32_t size;
} alloc_info;
void *secure_malloc(int32_t sizeIn)
{
#if defined(WIN32)
void *ptr = ::VirtualAlloc(nullptr, sizeIn + alloc_info_size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
#else
void *ptr = nullptr;
if ((ptr = ::mmap(nullptr, sizeIn + alloc_info_size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE | MAP_NOCORE, -1, 0)) == MAP_FAILED) {
ptr = nullptr;
}
#endif
if (! ptr)
throw std::runtime_error("secure_alloc out of memory");
bool lock_ret = false;
#if defined(WIN32)
lock_ret = (::VirtualLock(ptr, sizeIn + alloc_info_size) != FALSE) ? true : false;
#else
lock_ret = (::mlock(ptr, sizeIn + alloc_info_size) == 0) ? true : false;
#endif
if (! lock_ret) {
throw std::runtime_error("secure_alloc virtual lock failure");
}
alloc_info *pinfo = reinterpret_cast<alloc_info *>(ptr);
pinfo->data.type = LOCK_UNLOCK;
pinfo->data.size = sizeIn;
return reinterpret_cast<void *>((byte *)ptr + alloc_info_size);
}
void secure_free(void *ptr)
{
void *fptr = reinterpret_cast<void *>((byte *)ptr - alloc_info_size);
const alloc_info *pinfo = reinterpret_cast<const alloc_info *>(fptr);
const int32_t size = pinfo->data.size;
#if defined(WIN32)
if(! ::VirtualUnlock(fptr, size + alloc_info_size))
throw std::runtime_error("secure_free VirtualUnlock failure");
#else
if(::munlock(fptr, size + alloc_info_size)!=0)
throw std::runtime_error("secure_free munlock failure");
#endif
secure_memzero(fptr, size + alloc_info_size);
#if defined(WIN32)
if(! ::VirtualFree(fptr, 0U, MEM_RELEASE))
throw std::runtime_error("secure_free VirtualFree failure");
#else
if(::munmap(fptr, size + alloc_info_size)!=0)
throw std::runtime_error("secure_free munmap failure");
#endif
}
