◇ ::mprotect(::VirtualProtect)とは?
ブロックチェーンでは「鍵」を扱うためSecure ZeroMemory・メモリロック・メモリプロテクト・Secure Allocatorの実装が必須です。このうちここでは、メモリプロテクトを扱います。
◇ メモリプロテクトとは?
メモリに置かれた情報は外部のプログラムから読み書きすることができます。ブロックチェーンも例外ではなく外部からメモリの内容を直接参照し書き換えることができます。そのため、メモリに対するアクセス権を付与できるのがこの「メモリプロテクト」です。注意点としてアクセス権を付与すると自分自身すら読み書きができません。自分自身であっても読み書き前に「読み書き許可」を出す必要があります。
◇ サンプルコード
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); } class manage { manage()=delete; manage(const manage &)=delete; manage(manage &&)=delete; manage &operator=(const manage &)=delete; manage &operator=(manage &&)=delete; public: explicit manage(void *ptrIn, int32_t sizeIn) : ptr(ptrIn), size(sizeIn), fUnlock(false) {} bool readonly() const { if(fUnlock) return true; #if defined(WIN32) DWORD old; fUnlock = ::VirtualProtect(ptr, size, PAGE_READONLY, &old)) ? true: false; #else fUnlock = (::mprotect(ptr, size, PROT_READ)==0) ? true: false; #endif return fUnlock; } bool readwrite() const { if(fUnlock) return true; #if defined(WIN32) DWORD old; fUnlock = ::VirtualProtect(ptr, size, PAGE_READWRITE, &old)) ? true: false; #else fUnlock = ::mprotect(ptr, size, PROT_READ | PROT_WRITE)==0) ? true; false; #endif return fUnlock; } bool noaccess() const { if(! fUnlock) return true; #if defined(WIN32) DWORD old; fUnlock = ::VirtualProtect(ptr, size, PAGE_NOACCESS, &old) ? false: true; #else fUnlock = (::mprotect(ptr, size, PROT_NONE) == 0) ? false: true; #endif return !fUnlock; } ~manage() { noaccess(); } private: void *ptr; int32_t size; mutable bool fUnlock; }; 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(fptr); const int32_t size; { manage mem(fptr, alloc_info_size); if(! mem.readonly()) throw std::runtime_error("secure_free noaccess failure"); const alloc_info *pinfo = reinterpret_cast<const alloc_info *>(fptr); size = pinfo->data.size; if(! mem.noaccess()) throw std::runtime_error("secure_free noaccess failure"); } { manage mem(fptr, size + alloc_info_size); if(! mem.readwrite()) throw std::runtime_error("secure_free noaccess failure"); secure_memzero(fptr, size + alloc_info_size); if(! mem.noaccess()) throw std::runtime_error("secure_free noaccess failure"); } #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 }