Botan  2.6.0
Crypto and TLS for C++11
os_utils.cpp
Go to the documentation of this file.
1 /*
2 * OS and machine specific utility functions
3 * (C) 2015,2016,2017 Jack Lloyd
4 * (C) 2016 Daniel Neus
5 *
6 * Botan is released under the Simplified BSD License (see license.txt)
7 */
8 
9 #include <botan/internal/os_utils.h>
10 #include <botan/cpuid.h>
11 #include <botan/exceptn.h>
12 #include <botan/mem_ops.h>
13 
14 #include <chrono>
15 #include <cstdlib>
16 
17 #if defined(BOTAN_TARGET_OS_HAS_EXPLICIT_BZERO)
18  #include <string.h>
19 #endif
20 
21 #if defined(BOTAN_TARGET_OS_HAS_POSIX1)
22  #include <sys/types.h>
23  #include <sys/resource.h>
24  #include <sys/mman.h>
25  #include <signal.h>
26  #include <setjmp.h>
27  #include <unistd.h>
28  #include <errno.h>
29 #elif defined(BOTAN_TARGET_OS_HAS_WIN32)
30  #define NOMINMAX 1
31  #include <windows.h>
32 #endif
33 
34 namespace Botan {
35 
36 // Not defined in OS namespace for historical reasons
37 void secure_scrub_memory(void* ptr, size_t n)
38  {
39 #if defined(BOTAN_TARGET_OS_HAS_RTLSECUREZEROMEMORY)
40  ::RtlSecureZeroMemory(ptr, n);
41 
42 #elif defined(BOTAN_TARGET_OS_HAS_EXPLICIT_BZERO)
43  ::explicit_bzero(ptr, n);
44 
45 #elif defined(BOTAN_USE_VOLATILE_MEMSET_FOR_ZERO) && (BOTAN_USE_VOLATILE_MEMSET_FOR_ZERO == 1)
46  /*
47  Call memset through a static volatile pointer, which the compiler
48  should not elide. This construct should be safe in conforming
49  compilers, but who knows. I did confirm that on x86-64 GCC 6.1 and
50  Clang 3.8 both create code that saves the memset address in the
51  data segment and uncondtionally loads and jumps to that address.
52  */
53  static void* (*const volatile memset_ptr)(void*, int, size_t) = std::memset;
54  (memset_ptr)(ptr, 0, n);
55 #else
56 
57  volatile uint8_t* p = reinterpret_cast<volatile uint8_t*>(ptr);
58 
59  for(size_t i = 0; i != n; ++i)
60  p[i] = 0;
61 #endif
62  }
63 
65  {
66 #if defined(BOTAN_TARGET_OS_HAS_POSIX1)
67  return ::getpid();
68 #elif defined(BOTAN_TARGET_OS_HAS_WIN32)
69  return ::GetCurrentProcessId();
70 #elif defined(BOTAN_TARGET_OS_IS_INCLUDEOS) || defined(BOTAN_TARGET_OS_IS_LLVM)
71  return 0; // truly no meaningful value
72 #else
73  #error "Missing get_process_id"
74 #endif
75  }
76 
78  {
79  uint64_t rtc = 0;
80 
81 #if defined(BOTAN_TARGET_OS_HAS_WIN32)
82  LARGE_INTEGER tv;
83  ::QueryPerformanceCounter(&tv);
84  rtc = tv.QuadPart;
85 
86 #elif defined(BOTAN_USE_GCC_INLINE_ASM)
87 
88 #if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY)
89 
90  if(CPUID::has_rdtsc())
91  {
92  uint32_t rtc_low = 0, rtc_high = 0;
93  asm volatile("rdtsc" : "=d" (rtc_high), "=a" (rtc_low));
94  rtc = (static_cast<uint64_t>(rtc_high) << 32) | rtc_low;
95  }
96 
97 #elif defined(BOTAN_TARGET_ARCH_IS_PPC64)
98 
99  for(;;)
100  {
101  uint32_t rtc_low = 0, rtc_high = 0, rtc_high2 = 0;
102  asm volatile("mftbu %0" : "=r" (rtc_high));
103  asm volatile("mftb %0" : "=r" (rtc_low));
104  asm volatile("mftbu %0" : "=r" (rtc_high2));
105 
106  if(rtc_high == rtc_high2)
107  {
108  rtc = (static_cast<uint64_t>(rtc_high) << 32) | rtc_low;
109  break;
110  }
111  }
112 
113 #elif defined(BOTAN_TARGET_ARCH_IS_ALPHA)
114  asm volatile("rpcc %0" : "=r" (rtc));
115 
116  // OpenBSD does not trap access to the %tick register
117 #elif defined(BOTAN_TARGET_ARCH_IS_SPARC64) && !defined(BOTAN_TARGET_OS_IS_OPENBSD)
118  asm volatile("rd %%tick, %0" : "=r" (rtc));
119 
120 #elif defined(BOTAN_TARGET_ARCH_IS_IA64)
121  asm volatile("mov %0=ar.itc" : "=r" (rtc));
122 
123 #elif defined(BOTAN_TARGET_ARCH_IS_S390X)
124  asm volatile("stck 0(%0)" : : "a" (&rtc) : "memory", "cc");
125 
126 #elif defined(BOTAN_TARGET_ARCH_IS_HPPA)
127  asm volatile("mfctl 16,%0" : "=r" (rtc)); // 64-bit only?
128 
129 #else
130  //#warning "OS::get_processor_timestamp not implemented"
131 #endif
132 
133 #endif
134 
135  return rtc;
136  }
137 
139  {
140  if(uint64_t cpu_clock = OS::get_processor_timestamp())
141  return cpu_clock;
142 
143  /*
144  If we got here either we either don't have an asm instruction
145  above, or (for x86) RDTSC is not available at runtime. Try some
146  clock_gettimes and return the first one that works, or otherwise
147  fall back to std::chrono.
148  */
149 
150 #if defined(BOTAN_TARGET_OS_HAS_CLOCK_GETTIME)
151 
152  // The ordering here is somewhat arbitrary...
153  const clockid_t clock_types[] = {
154 #if defined(CLOCK_MONOTONIC_HR)
155  CLOCK_MONOTONIC_HR,
156 #endif
157 #if defined(CLOCK_MONOTONIC_RAW)
158  CLOCK_MONOTONIC_RAW,
159 #endif
160 #if defined(CLOCK_MONOTONIC)
161  CLOCK_MONOTONIC,
162 #endif
163 #if defined(CLOCK_PROCESS_CPUTIME_ID)
164  CLOCK_PROCESS_CPUTIME_ID,
165 #endif
166 #if defined(CLOCK_THREAD_CPUTIME_ID)
167  CLOCK_THREAD_CPUTIME_ID,
168 #endif
169  };
170 
171  for(clockid_t clock : clock_types)
172  {
173  struct timespec ts;
174  if(::clock_gettime(clock, &ts) == 0)
175  {
176  return (static_cast<uint64_t>(ts.tv_sec) * 1000000000) + static_cast<uint64_t>(ts.tv_nsec);
177  }
178  }
179 #endif
180 
181  // Plain C++11 fallback
182  auto now = std::chrono::high_resolution_clock::now().time_since_epoch();
183  return std::chrono::duration_cast<std::chrono::nanoseconds>(now).count();
184  }
185 
187  {
188 #if defined(BOTAN_TARGET_OS_HAS_CLOCK_GETTIME)
189  struct timespec ts;
190  if(::clock_gettime(CLOCK_REALTIME, &ts) == 0)
191  {
192  return (static_cast<uint64_t>(ts.tv_sec) * 1000000000) + static_cast<uint64_t>(ts.tv_nsec);
193  }
194 #endif
195 
196  auto now = std::chrono::system_clock::now().time_since_epoch();
197  return std::chrono::duration_cast<std::chrono::nanoseconds>(now).count();
198  }
199 
201  {
202 #if defined(BOTAN_TARGET_OS_HAS_POSIX1)
203  long p = ::sysconf(_SC_PAGESIZE);
204  if(p > 1)
205  return static_cast<size_t>(p);
206  else
207  return 4096;
208 #elif defined(BOTAN_TARGET_OS_HAS_VIRTUAL_LOCK)
209  SYSTEM_INFO sys_info;
210  ::GetSystemInfo(&sys_info);
211  return sys_info.dwPageSize;
212 #endif
213 
214  // default value
215  return 4096;
216  }
217 
219  {
220 #if defined(BOTAN_TARGET_OS_HAS_POSIX1)
221  /*
222  * Linux defaults to only 64 KiB of mlockable memory per process
223  * (too small) but BSDs offer a small fraction of total RAM (more
224  * than we need). Bound the total mlock size to 512 KiB which is
225  * enough to run the entire test suite without spilling to non-mlock
226  * memory (and thus presumably also enough for many useful
227  * programs), but small enough that we should not cause problems
228  * even if many processes are mlocking on the same machine.
229  */
230  size_t mlock_requested = BOTAN_MLOCK_ALLOCATOR_MAX_LOCKED_KB;
231 
232  /*
233  * Allow override via env variable
234  */
235  if(const char* env = std::getenv("BOTAN_MLOCK_POOL_SIZE"))
236  {
237  try
238  {
239  const size_t user_req = std::stoul(env, nullptr);
240  mlock_requested = std::min(user_req, mlock_requested);
241  }
242  catch(std::exception&) { /* ignore it */ }
243  }
244 
245 #if defined(RLIMIT_MEMLOCK)
246  if(mlock_requested > 0)
247  {
248  struct ::rlimit limits;
249 
250  ::getrlimit(RLIMIT_MEMLOCK, &limits);
251 
252  if(limits.rlim_cur < limits.rlim_max)
253  {
254  limits.rlim_cur = limits.rlim_max;
255  ::setrlimit(RLIMIT_MEMLOCK, &limits);
256  ::getrlimit(RLIMIT_MEMLOCK, &limits);
257  }
258 
259  return std::min<size_t>(limits.rlim_cur, mlock_requested * 1024);
260  }
261 #else
262  /*
263  * If RLIMIT_MEMLOCK is not defined, likely the OS does not support
264  * unprivileged mlock calls.
265  */
266  return 0;
267 #endif
268 
269 #elif defined(BOTAN_TARGET_OS_HAS_VIRTUAL_LOCK)
270  SIZE_T working_min = 0, working_max = 0;
271  if(!::GetProcessWorkingSetSize(::GetCurrentProcess(), &working_min, &working_max))
272  {
273  return 0;
274  }
275 
276  // According to Microsoft MSDN:
277  // The maximum number of pages that a process can lock is equal to the number of pages in its minimum working set minus a small overhead
278  // In the book "Windows Internals Part 2": the maximum lockable pages are minimum working set size - 8 pages
279  // But the information in the book seems to be inaccurate/outdated
280  // I've tested this on Windows 8.1 x64, Windows 10 x64 and Windows 7 x86
281  // On all three OS the value is 11 instead of 8
282  size_t overhead = OS::system_page_size() * 11ULL;
283  if(working_min > overhead)
284  {
285  size_t lockable_bytes = working_min - overhead;
286  if(lockable_bytes < (BOTAN_MLOCK_ALLOCATOR_MAX_LOCKED_KB * 1024ULL))
287  {
288  return lockable_bytes;
289  }
290  else
291  {
292  return BOTAN_MLOCK_ALLOCATOR_MAX_LOCKED_KB * 1024ULL;
293  }
294  }
295 #endif
296 
297  return 0;
298  }
299 
300 void* OS::allocate_locked_pages(size_t length)
301  {
302 #if defined(BOTAN_TARGET_OS_HAS_POSIX1)
303 
304 #if !defined(MAP_NOCORE)
305  #define MAP_NOCORE 0
306 #endif
307 
308 #if !defined(MAP_ANONYMOUS)
309  #define MAP_ANONYMOUS MAP_ANON
310 #endif
311 
312  void* ptr = ::mmap(nullptr,
313  length,
314  PROT_READ | PROT_WRITE,
315  MAP_ANONYMOUS | MAP_SHARED | MAP_NOCORE,
316  /*fd*/-1,
317  /*offset*/0);
318 
319  if(ptr == MAP_FAILED)
320  {
321  return nullptr;
322  }
323 
324 #if defined(MADV_DONTDUMP)
325  ::madvise(ptr, length, MADV_DONTDUMP);
326 #endif
327 
328 #if defined(BOTAN_TARGET_OS_HAS_POSIX_MLOCK)
329  if(::mlock(ptr, length) != 0)
330  {
331  ::munmap(ptr, length);
332  return nullptr; // failed to lock
333  }
334 #endif
335 
336  ::memset(ptr, 0, length);
337 
338  return ptr;
339 #elif defined(BOTAN_TARGET_OS_HAS_VIRTUAL_LOCK)
340  LPVOID ptr = ::VirtualAlloc(nullptr, length, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
341  if(!ptr)
342  {
343  return nullptr;
344  }
345 
346  if(::VirtualLock(ptr, length) == 0)
347  {
348  ::VirtualFree(ptr, 0, MEM_RELEASE);
349  return nullptr; // failed to lock
350  }
351 
352  return ptr;
353 #else
354  BOTAN_UNUSED(length);
355  return nullptr; /* not implemented */
356 #endif
357  }
358 
359 void OS::free_locked_pages(void* ptr, size_t length)
360  {
361  if(ptr == nullptr || length == 0)
362  return;
363 
364 #if defined(BOTAN_TARGET_OS_HAS_POSIX1)
365  secure_scrub_memory(ptr, length);
366 
367 #if defined(BOTAN_TARGET_OS_HAS_POSIX_MLOCK)
368  ::munlock(ptr, length);
369 #endif
370 
371  ::munmap(ptr, length);
372 #elif defined(BOTAN_TARGET_OS_HAS_VIRTUAL_LOCK)
373  secure_scrub_memory(ptr, length);
374  ::VirtualUnlock(ptr, length);
375  ::VirtualFree(ptr, 0, MEM_RELEASE);
376 #else
377  // Invalid argument because no way this pointer was allocated by us
378  throw Invalid_Argument("Invalid ptr to free_locked_pages");
379 #endif
380  }
381 
382 #if defined(BOTAN_TARGET_OS_HAS_POSIX1)
383 namespace {
384 
385 static ::sigjmp_buf g_sigill_jmp_buf;
386 
387 void botan_sigill_handler(int)
388  {
389  siglongjmp(g_sigill_jmp_buf, /*non-zero return value*/1);
390  }
391 
392 }
393 #endif
394 
395 int OS::run_cpu_instruction_probe(std::function<int ()> probe_fn)
396  {
397  volatile int probe_result = -3;
398 
399 #if defined(BOTAN_TARGET_OS_HAS_POSIX1)
400  struct sigaction old_sigaction;
401  struct sigaction sigaction;
402 
403  sigaction.sa_handler = botan_sigill_handler;
404  sigemptyset(&sigaction.sa_mask);
405  sigaction.sa_flags = 0;
406 
407  int rc = ::sigaction(SIGILL, &sigaction, &old_sigaction);
408 
409  if(rc != 0)
410  throw Exception("run_cpu_instruction_probe sigaction failed");
411 
412  rc = sigsetjmp(g_sigill_jmp_buf, /*save sigs*/1);
413 
414  if(rc == 0)
415  {
416  // first call to sigsetjmp
417  probe_result = probe_fn();
418  }
419  else if(rc == 1)
420  {
421  // non-local return from siglongjmp in signal handler: return error
422  probe_result = -1;
423  }
424 
425  // Restore old SIGILL handler, if any
426  rc = ::sigaction(SIGILL, &old_sigaction, nullptr);
427  if(rc != 0)
428  throw Exception("run_cpu_instruction_probe sigaction restore failed");
429 
430 #elif defined(BOTAN_TARGET_OS_IS_WINDOWS) && defined(BOTAN_TARGET_COMPILER_IS_MSVC)
431 
432  // Windows SEH
433  __try
434  {
435  probe_result = probe_fn();
436  }
437  __except(::GetExceptionCode() == EXCEPTION_ILLEGAL_INSTRUCTION ?
438  EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
439  {
440  probe_result = -1;
441  }
442 
443 #endif
444 
445  return probe_result;
446  }
447 
448 }
int BOTAN_TEST_API run_cpu_instruction_probe(std::function< int()> probe_fn)
Definition: os_utils.cpp:395
uint32_t BOTAN_TEST_API get_process_id()
Definition: os_utils.cpp:64
void * allocate_locked_pages(size_t length)
Definition: os_utils.cpp:300
size_t get_memory_locking_limit()
Definition: os_utils.cpp:218
uint64_t BOTAN_TEST_API get_processor_timestamp()
Definition: os_utils.cpp:77
uint64_t BOTAN_TEST_API get_system_timestamp_ns()
Definition: os_utils.cpp:186
Definition: alg_id.cpp:13
#define BOTAN_UNUSED(...)
Definition: assert.h:117
void secure_scrub_memory(void *ptr, size_t n)
Definition: os_utils.cpp:37
size_t system_page_size()
Definition: os_utils.cpp:200
void free_locked_pages(void *ptr, size_t length)
Definition: os_utils.cpp:359
uint64_t BOTAN_TEST_API get_high_resolution_clock()
Definition: os_utils.cpp:138