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