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