From f75ae7463a7f300a0b2a95693062b7129b6cc53d Mon Sep 17 00:00:00 2001 From: Jack Lloyd Date: Sat, 5 Dec 2015 13:10:59 -0500 Subject: Add OS utility header Provide abstractions for the locking allocator (allocate and free locked pages) to decouple it from the platform dependent code. Should make it easy to write a Windows version using VirtualAlloc+VirtualLock. Exposes max mlock limit as a build.h toggle --- src/lib/utils/info.txt | 1 + src/lib/utils/locking_allocator/info.txt | 9 -- .../utils/locking_allocator/locking_allocator.cpp | 107 +++-------------- .../utils/locking_allocator/locking_allocator.h | 5 +- src/lib/utils/os_utils.cpp | 131 +++++++++++++++++++++ src/lib/utils/os_utils.h | 40 +++++++ 6 files changed, 191 insertions(+), 102 deletions(-) create mode 100644 src/lib/utils/os_utils.cpp create mode 100644 src/lib/utils/os_utils.h (limited to 'src/lib') diff --git a/src/lib/utils/info.txt b/src/lib/utils/info.txt index 228fccd82..348992ddf 100644 --- a/src/lib/utils/info.txt +++ b/src/lib/utils/info.txt @@ -25,6 +25,7 @@ bit_ops.h ct_utils.h donna128.h filesystem.h +os_utils.h prefetch.h rounding.h semaphore.h diff --git a/src/lib/utils/locking_allocator/info.txt b/src/lib/utils/locking_allocator/info.txt index d3b5e86f8..c815bdbcc 100644 --- a/src/lib/utils/locking_allocator/info.txt +++ b/src/lib/utils/locking_allocator/info.txt @@ -1,10 +1 @@ define LOCKING_ALLOCATOR 20131128 - - -android -linux -freebsd -netbsd -openbsd -solaris - diff --git a/src/lib/utils/locking_allocator/locking_allocator.cpp b/src/lib/utils/locking_allocator/locking_allocator.cpp index c145cfd7f..c6181d4c0 100644 --- a/src/lib/utils/locking_allocator/locking_allocator.cpp +++ b/src/lib/utils/locking_allocator/locking_allocator.cpp @@ -1,72 +1,22 @@ /* * Mlock Allocator -* (C) 2012,2014 Jack Lloyd +* (C) 2012,2014,2015 Jack Lloyd * * Botan is released under the Simplified BSD License (see license.txt) */ #include +#include #include #include #include #include - -#include -#include +#include namespace Botan { namespace { -size_t reset_mlock_limit(size_t max_req) - { -#if defined(RLIMIT_MEMLOCK) - struct rlimit limits; - - ::getrlimit(RLIMIT_MEMLOCK, &limits); - - if(limits.rlim_cur < limits.rlim_max) - { - limits.rlim_cur = limits.rlim_max; - ::setrlimit(RLIMIT_MEMLOCK, &limits); - ::getrlimit(RLIMIT_MEMLOCK, &limits); - } - - return std::min(limits.rlim_cur, max_req); -#endif - - return 0; - } - -size_t mlock_limit() - { - /* - * Linux defaults to only 64 KiB of mlockable memory per process - * (too small) but BSDs offer a small fraction of total RAM (more - * than we need). Bound the total mlock size to 512 KiB which is - * enough to run the entire test suite without spilling to non-mlock - * memory (and thus presumably also enough for many useful - * programs), but small enough that we should not cause problems - * even if many processes are mlocking on the same machine. - */ - size_t mlock_requested = 512; - - /* - * Allow override via env variable - */ - if(const char* env = ::getenv("BOTAN_MLOCK_POOL_SIZE")) - { - try - { - const size_t user_req = std::stoul(env, nullptr); - mlock_requested = std::min(user_req, mlock_requested); - } - catch(std::exception&) { /* ignore it */ } - } - - return reset_mlock_limit(mlock_requested*1024); - } - bool ptr_in_pool(const void* pool_ptr, size_t poolsize, const void* buf_ptr, size_t bufsize) { @@ -240,47 +190,25 @@ bool mlock_allocator::deallocate(void* p, size_t num_elems, size_t elem_size) return true; } -mlock_allocator::mlock_allocator() : - m_poolsize(mlock_limit()), - m_pool(nullptr) +mlock_allocator::mlock_allocator() { -#if !defined(MAP_NOCORE) - #define MAP_NOCORE 0 -#endif + const size_t mem_to_lock = OS::get_memory_locking_limit(); -#if !defined(MAP_ANONYMOUS) - #define MAP_ANONYMOUS MAP_ANON -#endif + /* + TODO: split into multiple single page allocations to + help ASLR and guard pages to help reduce the damage of + a wild reads or write by the application. + */ - if(m_poolsize) + if(mem_to_lock) { - m_pool = static_cast( - ::mmap( - nullptr, m_poolsize, - PROT_READ | PROT_WRITE, - MAP_ANONYMOUS | MAP_SHARED | MAP_NOCORE, - -1, 0)); - - if(m_pool == static_cast(MAP_FAILED)) - { - m_pool = nullptr; - throw std::runtime_error("Failed to mmap locking_allocator pool"); - } + m_pool = static_cast(OS::allocate_locked_pages(mem_to_lock)); - clear_mem(m_pool, m_poolsize); - - if(::mlock(m_pool, m_poolsize) != 0) + if(m_pool != nullptr) { - ::munmap(m_pool, m_poolsize); - m_pool = nullptr; - throw std::runtime_error("Could not mlock " + std::to_string(m_poolsize) + " bytes"); + m_poolsize = mem_to_lock; + m_freelist.push_back(std::make_pair(0, m_poolsize)); } - -#if defined(MADV_DONTDUMP) - ::madvise(m_pool, m_poolsize, MADV_DONTDUMP); -#endif - - m_freelist.push_back(std::make_pair(0, m_poolsize)); } } @@ -288,9 +216,8 @@ mlock_allocator::~mlock_allocator() { if(m_pool) { - clear_mem(m_pool, m_poolsize); - ::munlock(m_pool, m_poolsize); - ::munmap(m_pool, m_poolsize); + zero_mem(m_pool, m_poolsize); + OS::free_locked_pages(m_pool, m_poolsize); m_pool = nullptr; } } diff --git a/src/lib/utils/locking_allocator/locking_allocator.h b/src/lib/utils/locking_allocator/locking_allocator.h index 2aca2dfa9..db75bc02a 100644 --- a/src/lib/utils/locking_allocator/locking_allocator.h +++ b/src/lib/utils/locking_allocator/locking_allocator.h @@ -32,11 +32,10 @@ class BOTAN_DLL mlock_allocator ~mlock_allocator(); - const size_t m_poolsize; - std::mutex m_mutex; std::vector> m_freelist; - byte* m_pool; + byte* m_pool = nullptr; + size_t m_poolsize = 0; }; } diff --git a/src/lib/utils/os_utils.cpp b/src/lib/utils/os_utils.cpp new file mode 100644 index 000000000..ae93d58d7 --- /dev/null +++ b/src/lib/utils/os_utils.cpp @@ -0,0 +1,131 @@ +/* +* OS and machine specific utility functions +* (C) 2015 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include +#include +#include + +//TODO: defined(BOTAN_TARGET_OS_TYPE_IS_POSIX) + +#if defined(BOTAN_TARGET_OS_HAS_POSIX_MLOCK) + #include + #include + #include + #include +#endif + +namespace Botan { + +namespace OS { + +size_t get_memory_locking_limit() + { +#if defined(BOTAN_TARGET_OS_HAS_POSIX_MLOCK) + /* + * Linux defaults to only 64 KiB of mlockable memory per process + * (too small) but BSDs offer a small fraction of total RAM (more + * than we need). Bound the total mlock size to 512 KiB which is + * enough to run the entire test suite without spilling to non-mlock + * memory (and thus presumably also enough for many useful + * programs), but small enough that we should not cause problems + * even if many processes are mlocking on the same machine. + */ + size_t mlock_requested = BOTAN_MLOCK_ALLOCATOR_MAX_LOCKED_KB; + + /* + * Allow override via env variable + */ + if(const char* env = ::getenv("BOTAN_MLOCK_POOL_SIZE")) + { + try + { + const size_t user_req = std::stoul(env, nullptr); + mlock_requested = std::min(user_req, mlock_requested); + } + catch(std::exception&) { /* ignore it */ } + } + + if(mlock_requested > 0) + { + struct ::rlimit limits; + + ::getrlimit(RLIMIT_MEMLOCK, &limits); + + if(limits.rlim_cur < limits.rlim_max) + { + limits.rlim_cur = limits.rlim_max; + ::setrlimit(RLIMIT_MEMLOCK, &limits); + ::getrlimit(RLIMIT_MEMLOCK, &limits); + } + + return std::min(limits.rlim_cur, mlock_requested * 1024); + } +#endif + + return 0; + } + +void* allocate_locked_pages(size_t length) + { +#if defined(BOTAN_TARGET_OS_HAS_POSIX_MLOCK) + +#if !defined(MAP_NOCORE) + #define MAP_NOCORE 0 +#endif + +#if !defined(MAP_ANONYMOUS) + #define MAP_ANONYMOUS MAP_ANON +#endif + + void* ptr = ::mmap(nullptr, + length, + PROT_READ | PROT_WRITE, + MAP_ANONYMOUS | MAP_SHARED | MAP_NOCORE, + /*fd*/-1, + /*offset*/0); + + if(ptr == MAP_FAILED) + { + return nullptr; + } + +#if defined(MADV_DONTDUMP) + ::madvise(ptr, length, MADV_DONTDUMP); +#endif + + if(::mlock(ptr, length) != 0) + { + ::munmap(ptr, length); + return nullptr; // failed to lock + } + + ::memset(ptr, 0, length); + + return ptr; +#else + return nullptr; /* not implemented */ +#endif + } + +void free_locked_pages(void* ptr, size_t length) + { + if(ptr == nullptr || length == 0) + return; + +#if defined(BOTAN_TARGET_OS_HAS_POSIX_MLOCK) + zero_mem(ptr, length); + ::munlock(ptr, length); + ::munmap(ptr, length); +#else + // Invalid argument because no way this pointer was allocated by us + throw Invalid_Argument("Invalid ptr to free_locked_pages"); +#endif + } + +} + +} diff --git a/src/lib/utils/os_utils.h b/src/lib/utils/os_utils.h new file mode 100644 index 000000000..0030f88c9 --- /dev/null +++ b/src/lib/utils/os_utils.h @@ -0,0 +1,40 @@ +/* +* OS specific utility functions +* (C) 2015 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_OS_UTILS_H__ +#define BOTAN_OS_UTILS_H__ + +#include + +namespace Botan { + +namespace OS { + +/* +* Returns the maximum amount of memory (in bytes) we could/should +* hyptothetically allocate. Reads "BOTAN_MLOCK_POOL_SIZE" from +* environment which can be set to zero. +*/ +size_t get_memory_locking_limit(); + +/* +* Request so many bytes of page-aligned RAM locked into memory OS +* calls (mlock, VirtualLock, or similar). Returns null on failure. The +* memory returned is zeroed. Free it with free_locked_pages. +*/ +void* allocate_locked_pages(size_t length); + +/* +* Free memory allocated by allocate_locked_pages +*/ +void free_locked_pages(void* ptr, size_t length); + +} + +} + +#endif -- cgit v1.2.3