Botan  2.4.0
Crypto and TLS for C++11
locking_allocator.cpp
Go to the documentation of this file.
1 /*
2 * Mlock Allocator
3 * (C) 2012,2014,2015 Jack Lloyd
4 *
5 * Botan is released under the Simplified BSD License (see license.txt)
6 */
7 
8 #include <botan/locking_allocator.h>
9 #include <botan/internal/os_utils.h>
10 #include <botan/mem_ops.h>
11 #include <algorithm>
12 #include <cstdlib>
13 #include <string>
14 #include <botan/mutex.h>
15 
16 namespace Botan {
17 
18 namespace {
19 
20 bool ptr_in_pool(const void* pool_ptr, size_t poolsize,
21  const void* buf_ptr, size_t bufsize)
22  {
23  const uintptr_t pool = reinterpret_cast<uintptr_t>(pool_ptr);
24  const uintptr_t buf = reinterpret_cast<uintptr_t>(buf_ptr);
25  return (buf >= pool) && (buf + bufsize <= pool + poolsize);
26  }
27 
28 size_t padding_for_alignment(size_t offset, size_t desired_alignment)
29  {
30  size_t mod = offset % desired_alignment;
31  if(mod == 0)
32  return 0; // already right on
33  return desired_alignment - mod;
34  }
35 
36 }
37 
38 void* mlock_allocator::allocate(size_t num_elems, size_t elem_size)
39  {
40  if(!m_pool)
41  return nullptr;
42 
43  const size_t n = num_elems * elem_size;
44  const size_t alignment = 16;
45 
46  if(n / elem_size != num_elems)
47  return nullptr; // overflow!
48 
49  if(n > m_poolsize)
50  return nullptr;
51  if(n < BOTAN_MLOCK_ALLOCATOR_MIN_ALLOCATION || n > BOTAN_MLOCK_ALLOCATOR_MAX_ALLOCATION)
52  return nullptr;
53 
54  lock_guard_type<mutex_type> lock(m_mutex);
55 
56  auto best_fit = m_freelist.end();
57 
58  for(auto i = m_freelist.begin(); i != m_freelist.end(); ++i)
59  {
60  // If we have a perfect fit, use it immediately
61  if(i->second == n && (i->first % alignment) == 0)
62  {
63  const size_t offset = i->first;
64  m_freelist.erase(i);
65  clear_mem(m_pool + offset, n);
66 
67  BOTAN_ASSERT((reinterpret_cast<uintptr_t>(m_pool) + offset) % alignment == 0,
68  "Returning correctly aligned pointer");
69 
70  return m_pool + offset;
71  }
72 
73  if((i->second >= (n + padding_for_alignment(i->first, alignment)) &&
74  ((best_fit == m_freelist.end()) || (best_fit->second > i->second))))
75  {
76  best_fit = i;
77  }
78  }
79 
80  if(best_fit != m_freelist.end())
81  {
82  const size_t offset = best_fit->first;
83 
84  const size_t alignment_padding = padding_for_alignment(offset, alignment);
85 
86  best_fit->first += n + alignment_padding;
87  best_fit->second -= n + alignment_padding;
88 
89  // Need to realign, split the block
90  if(alignment_padding)
91  {
92  /*
93  If we used the entire block except for small piece used for
94  alignment at the beginning, so just update the entry already
95  in place (as it is in the correct location), rather than
96  deleting the empty range and inserting the new one in the
97  same location.
98  */
99  if(best_fit->second == 0)
100  {
101  best_fit->first = offset;
102  best_fit->second = alignment_padding;
103  }
104  else
105  m_freelist.insert(best_fit, std::make_pair(offset, alignment_padding));
106  }
107 
108  clear_mem(m_pool + offset + alignment_padding, n);
109 
110  BOTAN_ASSERT((reinterpret_cast<uintptr_t>(m_pool) + offset + alignment_padding) % alignment == 0,
111  "Returning correctly aligned pointer");
112 
113  return m_pool + offset + alignment_padding;
114  }
115 
116  return nullptr;
117  }
118 
119 bool mlock_allocator::deallocate(void* p, size_t num_elems, size_t elem_size) BOTAN_NOEXCEPT
120  {
121  if(!m_pool)
122  return false;
123 
124  size_t n = num_elems * elem_size;
125 
126  /*
127  We return nullptr in allocate if there was an overflow, so if an
128  overflow occurs here we know the pointer was not allocated by this pool.
129  */
130  if(n / elem_size != num_elems)
131  return false;
132 
133  if(!ptr_in_pool(m_pool, m_poolsize, p, n))
134  return false;
135 
136  std::memset(p, 0, n);
137 
138  lock_guard_type<mutex_type> lock(m_mutex);
139 
140  const size_t start = static_cast<uint8_t*>(p) - m_pool;
141 
142  auto comp = [](std::pair<size_t, size_t> x, std::pair<size_t, size_t> y){ return x.first < y.first; };
143 
144  auto i = std::lower_bound(m_freelist.begin(), m_freelist.end(),
145  std::make_pair(start, 0), comp);
146 
147  // try to merge with later block
148  if(i != m_freelist.end() && start + n == i->first)
149  {
150  i->first = start;
151  i->second += n;
152  n = 0;
153  }
154 
155  // try to merge with previous block
156  if(i != m_freelist.begin())
157  {
158  auto prev = std::prev(i);
159 
160  if(prev->first + prev->second == start)
161  {
162  if(n)
163  {
164  prev->second += n;
165  n = 0;
166  }
167  else
168  {
169  // merge adjoining
170  prev->second += i->second;
171  m_freelist.erase(i);
172  }
173  }
174  }
175 
176  if(n != 0) // no merge possible?
177  m_freelist.insert(i, std::make_pair(start, n));
178 
179  return true;
180  }
181 
182 mlock_allocator::mlock_allocator()
183  {
184  const size_t mem_to_lock = OS::get_memory_locking_limit();
185 
186  /*
187  TODO: split into multiple single page allocations to
188  help ASLR and guard pages to help reduce the damage of
189  a wild reads or write by the application.
190  */
191 
192  if(mem_to_lock)
193  {
194  m_pool = static_cast<uint8_t*>(OS::allocate_locked_pages(mem_to_lock));
195 
196  if(m_pool != nullptr)
197  {
198  m_poolsize = mem_to_lock;
199  m_freelist.push_back(std::make_pair(0, m_poolsize));
200  }
201  }
202  }
203 
204 mlock_allocator::~mlock_allocator()
205  {
206  if(m_pool)
207  {
208  secure_scrub_memory(m_pool, m_poolsize);
209  OS::free_locked_pages(m_pool, m_poolsize);
210  m_pool = nullptr;
211  }
212  }
213 
215  {
216  static mlock_allocator mlock;
217  return mlock;
218  }
219 
220 }
static mlock_allocator & instance()
void clear_mem(T *ptr, size_t n)
Definition: mem_ops.h:86
void * allocate_locked_pages(size_t length)
Definition: os_utils.cpp:283
#define BOTAN_NOEXCEPT
Definition: compiler.h:154
void * allocate(size_t num_elems, size_t elem_size)
size_t get_memory_locking_limit()
Definition: os_utils.cpp:197
#define BOTAN_ASSERT(expr, assertion_made)
Definition: assert.h:29
bool deallocate(void *p, size_t num_elems, size_t elem_size) BOTAN_NOEXCEPT
Definition: alg_id.cpp:13
void secure_scrub_memory(void *ptr, size_t n)
Definition: os_utils.cpp:37
void free_locked_pages(void *ptr, size_t length)
Definition: os_utils.cpp:340