Botan  2.9.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,2018 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 <stdlib.h>
27  #include <setjmp.h>
28  #include <unistd.h>
29  #include <errno.h>
30  #include <termios.h>
31  #undef B0
32 #endif
33 
34 #if defined(BOTAN_TARGET_OS_IS_EMSCRIPTEN)
35  #include <emscripten/emscripten.h>
36 #endif
37 
38 #if defined(BOTAN_TARGET_OS_HAS_GETAUXVAL)
39  #include <sys/auxv.h>
40 #endif
41 
42 #if defined(BOTAN_TARGET_OS_HAS_WIN32)
43  #define NOMINMAX 1
44  #include <windows.h>
45 #endif
46 
47 namespace Botan {
48 
49 // Not defined in OS namespace for historical reasons
50 void secure_scrub_memory(void* ptr, size_t n)
51  {
52 #if defined(BOTAN_TARGET_OS_HAS_RTLSECUREZEROMEMORY)
53  ::RtlSecureZeroMemory(ptr, n);
54 
55 #elif defined(BOTAN_TARGET_OS_HAS_EXPLICIT_BZERO)
56  ::explicit_bzero(ptr, n);
57 
58 #elif defined(BOTAN_TARGET_OS_HAS_EXPLICIT_MEMSET)
59  (void)::explicit_memset(ptr, 0, n);
60 
61 #elif defined(BOTAN_USE_VOLATILE_MEMSET_FOR_ZERO) && (BOTAN_USE_VOLATILE_MEMSET_FOR_ZERO == 1)
62  /*
63  Call memset through a static volatile pointer, which the compiler
64  should not elide. This construct should be safe in conforming
65  compilers, but who knows. I did confirm that on x86-64 GCC 6.1 and
66  Clang 3.8 both create code that saves the memset address in the
67  data segment and unconditionally loads and jumps to that address.
68  */
69  static void* (*const volatile memset_ptr)(void*, int, size_t) = std::memset;
70  (memset_ptr)(ptr, 0, n);
71 #else
72 
73  volatile uint8_t* p = reinterpret_cast<volatile uint8_t*>(ptr);
74 
75  for(size_t i = 0; i != n; ++i)
76  p[i] = 0;
77 #endif
78  }
79 
81  {
82 #if defined(BOTAN_TARGET_OS_HAS_POSIX1)
83  return ::getpid();
84 #elif defined(BOTAN_TARGET_OS_HAS_WIN32)
85  return ::GetCurrentProcessId();
86 #elif defined(BOTAN_TARGET_OS_IS_INCLUDEOS) || defined(BOTAN_TARGET_OS_IS_LLVM)
87  return 0; // truly no meaningful value
88 #else
89  #error "Missing get_process_id"
90 #endif
91  }
92 
94  {
95 #if defined(BOTAN_TARGET_OS_HAS_GETAUXVAL) && defined(AT_SECURE)
96  return ::getauxval(AT_SECURE) != 0;
97 #elif defined(BOTAN_TARGET_OS_HAS_POSIX1)
98  return (::getuid() != ::geteuid()) || (::getgid() != ::getegid());
99 #else
100  return false;
101 #endif
102  }
103 
105  {
106  uint64_t rtc = 0;
107 
108 #if defined(BOTAN_TARGET_OS_HAS_WIN32)
109  LARGE_INTEGER tv;
110  ::QueryPerformanceCounter(&tv);
111  rtc = tv.QuadPart;
112 
113 #elif defined(BOTAN_USE_GCC_INLINE_ASM)
114 
115 #if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY)
116 
117  if(CPUID::has_rdtsc())
118  {
119  uint32_t rtc_low = 0, rtc_high = 0;
120  asm volatile("rdtsc" : "=d" (rtc_high), "=a" (rtc_low));
121  rtc = (static_cast<uint64_t>(rtc_high) << 32) | rtc_low;
122  }
123 
124 #elif defined(BOTAN_TARGET_ARCH_IS_PPC64)
125 
126  for(;;)
127  {
128  uint32_t rtc_low = 0, rtc_high = 0, rtc_high2 = 0;
129  asm volatile("mftbu %0" : "=r" (rtc_high));
130  asm volatile("mftb %0" : "=r" (rtc_low));
131  asm volatile("mftbu %0" : "=r" (rtc_high2));
132 
133  if(rtc_high == rtc_high2)
134  {
135  rtc = (static_cast<uint64_t>(rtc_high) << 32) | rtc_low;
136  break;
137  }
138  }
139 
140 #elif defined(BOTAN_TARGET_ARCH_IS_ALPHA)
141  asm volatile("rpcc %0" : "=r" (rtc));
142 
143  // OpenBSD does not trap access to the %tick register
144 #elif defined(BOTAN_TARGET_ARCH_IS_SPARC64) && !defined(BOTAN_TARGET_OS_IS_OPENBSD)
145  asm volatile("rd %%tick, %0" : "=r" (rtc));
146 
147 #elif defined(BOTAN_TARGET_ARCH_IS_IA64)
148  asm volatile("mov %0=ar.itc" : "=r" (rtc));
149 
150 #elif defined(BOTAN_TARGET_ARCH_IS_S390X)
151  asm volatile("stck 0(%0)" : : "a" (&rtc) : "memory", "cc");
152 
153 #elif defined(BOTAN_TARGET_ARCH_IS_HPPA)
154  asm volatile("mfctl 16,%0" : "=r" (rtc)); // 64-bit only?
155 
156 #else
157  //#warning "OS::get_cpu_cycle_counter not implemented"
158 #endif
159 
160 #endif
161 
162  return rtc;
163  }
164 
166  {
167  if(uint64_t cpu_clock = OS::get_cpu_cycle_counter())
168  return cpu_clock;
169 
170 #if defined(BOTAN_TARGET_OS_IS_EMSCRIPTEN)
171  return emscripten_get_now();
172 #endif
173 
174  /*
175  If we got here either we either don't have an asm instruction
176  above, or (for x86) RDTSC is not available at runtime. Try some
177  clock_gettimes and return the first one that works, or otherwise
178  fall back to std::chrono.
179  */
180 
181 #if defined(BOTAN_TARGET_OS_HAS_CLOCK_GETTIME)
182 
183  // The ordering here is somewhat arbitrary...
184  const clockid_t clock_types[] = {
185 #if defined(CLOCK_MONOTONIC_HR)
186  CLOCK_MONOTONIC_HR,
187 #endif
188 #if defined(CLOCK_MONOTONIC_RAW)
189  CLOCK_MONOTONIC_RAW,
190 #endif
191 #if defined(CLOCK_MONOTONIC)
192  CLOCK_MONOTONIC,
193 #endif
194 #if defined(CLOCK_PROCESS_CPUTIME_ID)
195  CLOCK_PROCESS_CPUTIME_ID,
196 #endif
197 #if defined(CLOCK_THREAD_CPUTIME_ID)
198  CLOCK_THREAD_CPUTIME_ID,
199 #endif
200  };
201 
202  for(clockid_t clock : clock_types)
203  {
204  struct timespec ts;
205  if(::clock_gettime(clock, &ts) == 0)
206  {
207  return (static_cast<uint64_t>(ts.tv_sec) * 1000000000) + static_cast<uint64_t>(ts.tv_nsec);
208  }
209  }
210 #endif
211 
212  // Plain C++11 fallback
213  auto now = std::chrono::high_resolution_clock::now().time_since_epoch();
214  return std::chrono::duration_cast<std::chrono::nanoseconds>(now).count();
215  }
216 
218  {
219 #if defined(BOTAN_TARGET_OS_HAS_CLOCK_GETTIME)
220  struct timespec ts;
221  if(::clock_gettime(CLOCK_REALTIME, &ts) == 0)
222  {
223  return (static_cast<uint64_t>(ts.tv_sec) * 1000000000) + static_cast<uint64_t>(ts.tv_nsec);
224  }
225 #endif
226 
227  auto now = std::chrono::system_clock::now().time_since_epoch();
228  return std::chrono::duration_cast<std::chrono::nanoseconds>(now).count();
229  }
230 
232  {
233  const size_t default_page_size = 4096;
234 
235 #if defined(BOTAN_TARGET_OS_HAS_POSIX1)
236  long p = ::sysconf(_SC_PAGESIZE);
237  if(p > 1)
238  return static_cast<size_t>(p);
239  else
240  return default_page_size;
241 #elif defined(BOTAN_TARGET_OS_HAS_VIRTUAL_LOCK)
242  SYSTEM_INFO sys_info;
243  ::GetSystemInfo(&sys_info);
244  return sys_info.dwPageSize;
245 #else
246  return default_page_size;
247 #endif
248  }
249 
251  {
252 #if defined(BOTAN_TARGET_OS_HAS_POSIX1) && defined(BOTAN_TARGET_OS_HAS_POSIX_MLOCK) && defined(RLIMIT_MEMLOCK)
253  /*
254  * If RLIMIT_MEMLOCK is not defined, likely the OS does not support
255  * unprivileged mlock calls.
256  *
257  * Linux defaults to only 64 KiB of mlockable memory per process
258  * (too small) but BSDs offer a small fraction of total RAM (more
259  * than we need). Bound the total mlock size to 512 KiB which is
260  * enough to run the entire test suite without spilling to non-mlock
261  * memory (and thus presumably also enough for many useful
262  * programs), but small enough that we should not cause problems
263  * even if many processes are mlocking on the same machine.
264  */
265  size_t mlock_requested = BOTAN_MLOCK_ALLOCATOR_MAX_LOCKED_KB;
266 
267  /*
268  * Allow override via env variable
269  */
270  if(const char* env = read_env_variable("BOTAN_MLOCK_POOL_SIZE"))
271  {
272  try
273  {
274  const size_t user_req = std::stoul(env, nullptr);
275  mlock_requested = std::min(user_req, mlock_requested);
276  }
277  catch(std::exception&) { /* ignore it */ }
278  }
279 
280  if(mlock_requested > 0)
281  {
282  struct ::rlimit limits;
283 
284  ::getrlimit(RLIMIT_MEMLOCK, &limits);
285 
286  if(limits.rlim_cur < limits.rlim_max)
287  {
288  limits.rlim_cur = limits.rlim_max;
289  ::setrlimit(RLIMIT_MEMLOCK, &limits);
290  ::getrlimit(RLIMIT_MEMLOCK, &limits);
291  }
292 
293  return std::min<size_t>(limits.rlim_cur, mlock_requested * 1024);
294  }
295 
296 #elif defined(BOTAN_TARGET_OS_HAS_VIRTUAL_LOCK)
297  SIZE_T working_min = 0, working_max = 0;
298  if(!::GetProcessWorkingSetSize(::GetCurrentProcess(), &working_min, &working_max))
299  {
300  return 0;
301  }
302 
303  // According to Microsoft MSDN:
304  // 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
305  // In the book "Windows Internals Part 2": the maximum lockable pages are minimum working set size - 8 pages
306  // But the information in the book seems to be inaccurate/outdated
307  // I've tested this on Windows 8.1 x64, Windows 10 x64 and Windows 7 x86
308  // On all three OS the value is 11 instead of 8
309  size_t overhead = OS::system_page_size() * 11ULL;
310  if(working_min > overhead)
311  {
312  size_t lockable_bytes = working_min - overhead;
313  if(lockable_bytes < (BOTAN_MLOCK_ALLOCATOR_MAX_LOCKED_KB * 1024ULL))
314  {
315  return lockable_bytes;
316  }
317  else
318  {
319  return BOTAN_MLOCK_ALLOCATOR_MAX_LOCKED_KB * 1024ULL;
320  }
321  }
322 #endif
323 
324  // Not supported on this platform
325  return 0;
326  }
327 
328 const char* OS::read_env_variable(const std::string& name)
329  {
331  return nullptr;
332 
333  return std::getenv(name.c_str());
334  }
335 
336 void* OS::allocate_locked_pages(size_t length)
337  {
338 #if defined(BOTAN_TARGET_OS_HAS_POSIX1) && defined(BOTAN_TARGET_OS_HAS_POSIX_MLOCK)
339 
340  const size_t page_size = OS::system_page_size();
341 
342  if(length % page_size != 0)
343  return nullptr;
344 
345  void* ptr = nullptr;
346  int rc = ::posix_memalign(&ptr, page_size, length);
347 
348  if(rc != 0 || ptr == nullptr)
349  return nullptr;
350 
351 #if defined(MADV_DONTDUMP)
352  ::madvise(ptr, length, MADV_DONTDUMP);
353 #endif
354 
355  if(::mlock(ptr, length) != 0)
356  {
357  std::free(ptr);
358  return nullptr; // failed to lock
359  }
360 
361  ::memset(ptr, 0, length);
362 
363  return ptr;
364 #elif defined(BOTAN_TARGET_OS_HAS_VIRTUAL_LOCK)
365  LPVOID ptr = ::VirtualAlloc(nullptr, length, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
366  if(!ptr)
367  {
368  return nullptr;
369  }
370 
371  if(::VirtualLock(ptr, length) == 0)
372  {
373  ::VirtualFree(ptr, 0, MEM_RELEASE);
374  return nullptr; // failed to lock
375  }
376 
377  return ptr;
378 #else
379  BOTAN_UNUSED(length);
380  return nullptr; /* not implemented */
381 #endif
382  }
383 
384 void OS::free_locked_pages(void* ptr, size_t length)
385  {
386  if(ptr == nullptr || length == 0)
387  return;
388 
389 #if defined(BOTAN_TARGET_OS_HAS_POSIX1) && defined(BOTAN_TARGET_OS_HAS_POSIX_MLOCK)
390  secure_scrub_memory(ptr, length);
391  ::munlock(ptr, length);
392  std::free(ptr);
393 
394 #elif defined(BOTAN_TARGET_OS_HAS_VIRTUAL_LOCK)
395  secure_scrub_memory(ptr, length);
396  ::VirtualUnlock(ptr, length);
397  ::VirtualFree(ptr, 0, MEM_RELEASE);
398 
399 #else
400  // Invalid argument because no way this pointer was allocated by us
401  throw Invalid_Argument("Invalid ptr to free_locked_pages");
402 #endif
403  }
404 
405 #if defined(BOTAN_TARGET_OS_HAS_POSIX1) && !defined(BOTAN_TARGET_OS_IS_EMSCRIPTEN)
406 
407 namespace {
408 
409 static ::sigjmp_buf g_sigill_jmp_buf;
410 
411 void botan_sigill_handler(int)
412  {
413  siglongjmp(g_sigill_jmp_buf, /*non-zero return value*/1);
414  }
415 
416 }
417 
418 #endif
419 
420 int OS::run_cpu_instruction_probe(std::function<int ()> probe_fn)
421  {
422  volatile int probe_result = -3;
423 
424 #if defined(BOTAN_TARGET_OS_HAS_POSIX1) && !defined(BOTAN_TARGET_OS_IS_EMSCRIPTEN)
425  struct sigaction old_sigaction;
426  struct sigaction sigaction;
427 
428  sigaction.sa_handler = botan_sigill_handler;
429  sigemptyset(&sigaction.sa_mask);
430  sigaction.sa_flags = 0;
431 
432  int rc = ::sigaction(SIGILL, &sigaction, &old_sigaction);
433 
434  if(rc != 0)
435  throw System_Error("run_cpu_instruction_probe sigaction failed", errno);
436 
437  rc = sigsetjmp(g_sigill_jmp_buf, /*save sigs*/1);
438 
439  if(rc == 0)
440  {
441  // first call to sigsetjmp
442  probe_result = probe_fn();
443  }
444  else if(rc == 1)
445  {
446  // non-local return from siglongjmp in signal handler: return error
447  probe_result = -1;
448  }
449 
450  // Restore old SIGILL handler, if any
451  rc = ::sigaction(SIGILL, &old_sigaction, nullptr);
452  if(rc != 0)
453  throw System_Error("run_cpu_instruction_probe sigaction restore failed", errno);
454 
455 #elif defined(BOTAN_TARGET_OS_IS_WINDOWS) && defined(BOTAN_TARGET_COMPILER_IS_MSVC)
456 
457  // Windows SEH
458  __try
459  {
460  probe_result = probe_fn();
461  }
462  __except(::GetExceptionCode() == EXCEPTION_ILLEGAL_INSTRUCTION ?
463  EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
464  {
465  probe_result = -1;
466  }
467 
468 #else
469  BOTAN_UNUSED(probe_fn);
470 #endif
471 
472  return probe_result;
473  }
474 
475 std::unique_ptr<OS::Echo_Suppression> OS::suppress_echo_on_terminal()
476  {
477 #if defined(BOTAN_TARGET_OS_HAS_POSIX1)
478  class POSIX_Echo_Suppression : public Echo_Suppression
479  {
480  public:
481  POSIX_Echo_Suppression()
482  {
483  m_stdin_fd = fileno(stdin);
484  if(::tcgetattr(m_stdin_fd, &m_old_termios) != 0)
485  throw System_Error("Getting terminal status failed", errno);
486 
487  struct termios noecho_flags = m_old_termios;
488  noecho_flags.c_lflag &= ~ECHO;
489  noecho_flags.c_lflag |= ECHONL;
490 
491  if(::tcsetattr(m_stdin_fd, TCSANOW, &noecho_flags) != 0)
492  throw System_Error("Clearing terminal echo bit failed", errno);
493  }
494 
495  void reenable_echo() override
496  {
497  if(m_stdin_fd > 0)
498  {
499  if(::tcsetattr(m_stdin_fd, TCSANOW, &m_old_termios) != 0)
500  throw System_Error("Restoring terminal echo bit failed", errno);
501  m_stdin_fd = -1;
502  }
503  }
504 
505  ~POSIX_Echo_Suppression()
506  {
507  try
508  {
509  reenable_echo();
510  }
511  catch(...)
512  {
513  }
514  }
515 
516  private:
517  int m_stdin_fd;
518  struct termios m_old_termios;
519  };
520 
521  return std::unique_ptr<Echo_Suppression>(new POSIX_Echo_Suppression);
522 
523 #elif defined(BOTAN_TARGET_OS_HAS_WIN32)
524 
525  class Win32_Echo_Suppression : public Echo_Suppression
526  {
527  public:
528  Win32_Echo_Suppression()
529  {
530  m_input_handle = ::GetStdHandle(STD_INPUT_HANDLE);
531  if(::GetConsoleMode(m_input_handle, &m_console_state) == 0)
532  throw System_Error("Getting console mode failed", ::GetLastError());
533 
534  DWORD new_mode = ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT;
535  if(::SetConsoleMode(m_input_handle, new_mode) == 0)
536  throw System_Error("Setting console mode failed", ::GetLastError());
537  }
538 
539  void reenable_echo() override
540  {
541  if(m_input_handle != INVALID_HANDLE_VALUE)
542  {
543  if(::SetConsoleMode(m_input_handle, m_console_state) == 0)
544  throw System_Error("Setting console mode failed", ::GetLastError());
545  m_input_handle = INVALID_HANDLE_VALUE;
546  }
547  }
548 
549  ~Win32_Echo_Suppression()
550  {
551  try
552  {
553  reenable_echo();
554  }
555  catch(...)
556  {
557  }
558  }
559 
560  private:
561  HANDLE m_input_handle;
562  DWORD m_console_state;
563  };
564 
565  return std::unique_ptr<Echo_Suppression>(new Win32_Echo_Suppression);
566 
567 #else
568 
569  // Not supported on this platform, return null
570  return std::unique_ptr<Echo_Suppression>();
571 #endif
572  }
573 
574 }
int BOTAN_TEST_API run_cpu_instruction_probe(std::function< int()> probe_fn)
Definition: os_utils.cpp:420
uint64_t BOTAN_TEST_API get_cpu_cycle_counter()
Definition: os_utils.cpp:104
uint32_t BOTAN_TEST_API get_process_id()
Definition: os_utils.cpp:80
void * allocate_locked_pages(size_t length)
Definition: os_utils.cpp:336
size_t get_memory_locking_limit()
Definition: os_utils.cpp:250
bool running_in_privileged_state()
Definition: os_utils.cpp:93
std::string name
uint64_t BOTAN_TEST_API get_system_timestamp_ns()
Definition: os_utils.cpp:217
Definition: alg_id.cpp:13
#define BOTAN_UNUSED(...)
Definition: assert.h:142
void secure_scrub_memory(void *ptr, size_t n)
Definition: os_utils.cpp:50
std::unique_ptr< Echo_Suppression > BOTAN_UNSTABLE_API suppress_echo_on_terminal()
Definition: os_utils.cpp:475
size_t system_page_size()
Definition: os_utils.cpp:231
const char * read_env_variable(const std::string &var_name)
Definition: os_utils.cpp:328
void free_locked_pages(void *ptr, size_t length)
Definition: os_utils.cpp:384
uint64_t BOTAN_TEST_API get_high_resolution_clock()
Definition: os_utils.cpp:165