
◇ ::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 }