Coverage Report

Created: 2020-02-14 15:38

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