aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJack Lloyd <[email protected]>2015-12-05 13:10:59 -0500
committerJack Lloyd <[email protected]>2015-12-05 13:10:59 -0500
commitf75ae7463a7f300a0b2a95693062b7129b6cc53d (patch)
treefaec9ed28956bcd2dda6e8d26f4ba094678bb454
parent2a472e9b33617afa62f5f899ec7eba90eb2f7ece (diff)
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
-rw-r--r--src/build-data/buildh.in6
-rw-r--r--src/lib/utils/info.txt1
-rw-r--r--src/lib/utils/locking_allocator/info.txt9
-rw-r--r--src/lib/utils/locking_allocator/locking_allocator.cpp107
-rw-r--r--src/lib/utils/locking_allocator/locking_allocator.h5
-rw-r--r--src/lib/utils/os_utils.cpp131
-rw-r--r--src/lib/utils/os_utils.h40
7 files changed, 197 insertions, 102 deletions
diff --git a/src/build-data/buildh.in b/src/build-data/buildh.in
index 8b950177f..d993a7ee9 100644
--- a/src/build-data/buildh.in
+++ b/src/build-data/buildh.in
@@ -41,6 +41,12 @@
#define BOTAN_MLOCK_ALLOCATOR_MIN_ALLOCATION 16
#define BOTAN_MLOCK_ALLOCATOR_MAX_ALLOCATION 128
+/*
+* Total maximum amount of RAM (in KiB) we will lock into memory, even
+* if the OS would let us lock more
+*/
+#define BOTAN_MLOCK_ALLOCATOR_MAX_LOCKED_KB 512
+
/* Multiplier on a block cipher's native parallelism */
#define BOTAN_BLOCK_CIPHER_PAR_MULT 4
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
-
-<os>
-android
-linux
-freebsd
-netbsd
-openbsd
-solaris
-</os>
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 <botan/locking_allocator.h>
+#include <botan/internal/os_utils.h>
#include <botan/mem_ops.h>
#include <algorithm>
#include <cstdlib>
#include <string>
-
-#include <sys/mman.h>
-#include <sys/resource.h>
+#include <mutex>
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<size_t>(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<byte*>(
- ::mmap(
- nullptr, m_poolsize,
- PROT_READ | PROT_WRITE,
- MAP_ANONYMOUS | MAP_SHARED | MAP_NOCORE,
- -1, 0));
-
- if(m_pool == static_cast<byte*>(MAP_FAILED))
- {
- m_pool = nullptr;
- throw std::runtime_error("Failed to mmap locking_allocator pool");
- }
+ m_pool = static_cast<byte*>(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<std::pair<size_t, size_t>> 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 <botan/internal/os_utils.h>
+#include <botan/exceptn.h>
+#include <botan/mem_ops.h>
+
+//TODO: defined(BOTAN_TARGET_OS_TYPE_IS_POSIX)
+
+#if defined(BOTAN_TARGET_OS_HAS_POSIX_MLOCK)
+ #include <sys/types.h>
+ #include <sys/mman.h>
+ #include <sys/resource.h>
+ #include <unistd.h>
+#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<size_t>(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 <botan/types.h>
+
+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