Coverage Report

Created: 2020-09-16 07:52

/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
   Call memset through a static volatile pointer, which the compiler
80
   should not elide. This construct should be safe in conforming
81
   compilers, but who knows. I did confirm that on x86-64 GCC 6.1 and
82
   Clang 3.8 both create code that saves the memset address in the
83
   data segment and unconditionally loads and jumps to that address.
84
   */
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
100M
   }
95
96
uint32_t OS::get_process_id()
97
553k
   {
98
553k
#if defined(BOTAN_TARGET_OS_HAS_POSIX1)
99
553k
   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) || defined(BOTAN_TARGET_OS_IS_NONE)
103
   return 0; // truly no meaningful value
104
#else
105
   #error "Missing get_process_id"
106
#endif
107
553k
   }
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
0
   }
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
0
   }
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
0
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
0
   }
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
0
250
   /*
251
   If we got here either we either don't have an asm instruction
252
   above, or (for x86) RDTSC is not available at runtime. Try some
253
   clock_gettimes and return the first one that works, or otherwise
254
   fall back to std::chrono.
255
   */
256
0
257
0
#if defined(BOTAN_TARGET_OS_HAS_CLOCK_GETTIME)
258
0
259
   // 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
0
#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
   // 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
0
   }
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
   /*
331
   * If RLIMIT_MEMLOCK is not defined, likely the OS does not support
332
   * unprivileged mlock calls.
333
   *
334
   * Linux defaults to only 64 KiB of mlockable memory per process
335
   * (too small) but BSDs offer a small fraction of total RAM (more
336
   * than we need). Bound the total mlock size to 512 KiB which is
337
   * enough to run the entire test suite without spilling to non-mlock
338
   * memory (and thus presumably also enough for many useful
339
   * programs), but small enough that we should not cause problems
340
   * even if many processes are mlocking on the same machine.
341
   */
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
0
383
   // 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
0
   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
   std::vector<void*> result;
462
0
463
0
#if (defined(BOTAN_TARGET_OS_HAS_POSIX1) && defined(BOTAN_TARGET_OS_HAS_POSIX_MLOCK)) || defined(BOTAN_TARGET_OS_HAS_VIRTUAL_LOCK)
464
0
465
0
   result.reserve(count);
466
0
467
0
   const size_t page_size = OS::system_page_size();
468
0
469
0
#if defined(BOTAN_TARGET_OS_HAS_POSIX1) && defined(BOTAN_TARGET_OS_HAS_POSIX_MLOCK)
470
0
   static const int locked_fd = get_locked_fd();
471
0
#endif
472
0
473
0
   for(size_t i = 0; i != count; ++i)
474
0
      {
475
0
      void* ptr = nullptr;
476
0
477
0
#if defined(BOTAN_TARGET_OS_HAS_POSIX1) && defined(BOTAN_TARGET_OS_HAS_POSIX_MLOCK)
478
0
479
#if !defined(MAP_ANONYMOUS)
480
   #define MAP_ANONYMOUS MAP_ANON
481
#endif
482
0
483
0
#if !defined(MAP_NOCORE)
484
#if defined(MAP_CONCEAL)
485
   #define MAP_NOCORE MAP_CONCEAL
486
#else
487
0
   #define MAP_NOCORE 0
488
0
#endif
489
0
#endif
490
0
491
0
#if !defined(PROT_MAX)
492
0
   #define PROT_MAX(p) 0
493
0
#endif
494
0
      const int pflags = PROT_READ | PROT_WRITE;
495
0
496
0
      ptr = ::mmap(nullptr, 3*page_size,
497
0
                   pflags | PROT_MAX(pflags),
498
0
                   MAP_ANONYMOUS | MAP_PRIVATE | MAP_NOCORE,
499
0
                   /*fd=*/locked_fd, /*offset=*/0);
500
0
501
0
      if(ptr == MAP_FAILED)
502
0
         {
503
0
         continue;
504
0
         }
505
0
506
      // lock the data page
507
0
      if(::mlock(static_cast<uint8_t*>(ptr) + page_size, page_size) != 0)
508
0
         {
509
0
         ::munmap(ptr, 3*page_size);
510
0
         continue;
511
0
         }
512
0
513
0
#if defined(MADV_DONTDUMP)
514
      // we ignore errors here, as DONTDUMP is just a bonus
515
0
      ::madvise(static_cast<uint8_t*>(ptr) + page_size, page_size, MADV_DONTDUMP);
516
0
#endif
517
0
518
#elif defined(BOTAN_TARGET_OS_HAS_VIRTUAL_LOCK)
519
      ptr = ::VirtualAlloc(nullptr, 3*page_size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
520
521
      if(ptr == nullptr)
522
         continue;
523
524
      if(::VirtualLock(static_cast<uint8_t*>(ptr) + page_size, page_size) == 0)
525
         {
526
         ::VirtualFree(ptr, 0, MEM_RELEASE);
527
         continue;
528
         }
529
#endif
530
0
531
0
      std::memset(ptr, 0, 3*page_size); // zero data page and both guard pages
532
0
533
      // Make guard page preceeding the data page
534
0
      page_prohibit_access(static_cast<uint8_t*>(ptr));
535
      // Make guard page following the data page
536
0
      page_prohibit_access(static_cast<uint8_t*>(ptr) + 2*page_size);
537
0
538
0
      result.push_back(static_cast<uint8_t*>(ptr) + page_size);
539
0
      }
540
#else
541
   BOTAN_UNUSED(count);
542
#endif
543
0
544
0
   return result;
545
0
   }
546
547
void OS::page_allow_access(void* page)
548
0
   {
549
0
#if defined(BOTAN_TARGET_OS_HAS_POSIX1)
550
0
   const size_t page_size = OS::system_page_size();
551
0
   ::mprotect(page, page_size, PROT_READ | PROT_WRITE);
552
#elif defined(BOTAN_TARGET_OS_HAS_VIRTUAL_LOCK)
553
   const size_t page_size = OS::system_page_size();
554
   DWORD old_perms = 0;
555
   ::VirtualProtect(page, page_size, PAGE_READWRITE, &old_perms);
556
   BOTAN_UNUSED(old_perms);
557
#else
558
   BOTAN_UNUSED(page);
559
#endif
560
0
   }
561
562
void OS::page_prohibit_access(void* page)
563
0
   {
564
0
#if defined(BOTAN_TARGET_OS_HAS_POSIX1)
565
0
   const size_t page_size = OS::system_page_size();
566
0
   ::mprotect(page, page_size, PROT_NONE);
567
#elif defined(BOTAN_TARGET_OS_HAS_VIRTUAL_LOCK)
568
   const size_t page_size = OS::system_page_size();
569
   DWORD old_perms = 0;
570
   ::VirtualProtect(page, page_size, PAGE_NOACCESS, &old_perms);
571
   BOTAN_UNUSED(old_perms);
572
#else
573
   BOTAN_UNUSED(page);
574
#endif
575
0
   }
576
577
void OS::free_locked_pages(const std::vector<void*>& pages)
578
0
   {
579
0
   const size_t page_size = OS::system_page_size();
580
0
581
0
   for(size_t i = 0; i != pages.size(); ++i)
582
0
      {
583
0
      void* ptr = pages[i];
584
0
585
0
      secure_scrub_memory(ptr, page_size);
586
0
587
      // ptr points to the data page, guard pages are before and after
588
0
      page_allow_access(static_cast<uint8_t*>(ptr) - page_size);
589
0
      page_allow_access(static_cast<uint8_t*>(ptr) + page_size);
590
0
591
0
#if defined(BOTAN_TARGET_OS_HAS_POSIX1) && defined(BOTAN_TARGET_OS_HAS_POSIX_MLOCK)
592
0
      ::munlock(ptr, page_size);
593
0
      ::munmap(static_cast<uint8_t*>(ptr) - page_size, 3*page_size);
594
#elif defined(BOTAN_TARGET_OS_HAS_VIRTUAL_LOCK)
595
      ::VirtualUnlock(ptr, page_size);
596
      ::VirtualFree(static_cast<uint8_t*>(ptr) - page_size, 0, MEM_RELEASE);
597
#endif
598
0
      }
599
0
   }
600
601
#if defined(BOTAN_TARGET_OS_HAS_POSIX1) && !defined(BOTAN_TARGET_OS_IS_EMSCRIPTEN)
602
603
namespace {
604
605
static ::sigjmp_buf g_sigill_jmp_buf;
606
607
void botan_sigill_handler(int)
608
0
   {
609
0
   siglongjmp(g_sigill_jmp_buf, /*non-zero return value*/1);
610
0
   }
611
612
}
613
614
#endif
615
616
int OS::run_cpu_instruction_probe(std::function<int ()> probe_fn)
617
0
   {
618
0
   volatile int probe_result = -3;
619
0
620
0
#if defined(BOTAN_TARGET_OS_HAS_POSIX1) && !defined(BOTAN_TARGET_OS_IS_EMSCRIPTEN)
621
0
   struct sigaction old_sigaction;
622
0
   struct sigaction sigaction;
623
0
624
0
   sigaction.sa_handler = botan_sigill_handler;
625
0
   sigemptyset(&sigaction.sa_mask);
626
0
   sigaction.sa_flags = 0;
627
0
628
0
   int rc = ::sigaction(SIGILL, &sigaction, &old_sigaction);
629
0
630
0
   if(rc != 0)
631
0
      throw System_Error("run_cpu_instruction_probe sigaction failed", errno);
632
0
633
0
   rc = sigsetjmp(g_sigill_jmp_buf, /*save sigs*/1);
634
0
635
0
   if(rc == 0)
636
0
      {
637
      // first call to sigsetjmp
638
0
      probe_result = probe_fn();
639
0
      }
640
0
   else if(rc == 1)
641
0
      {
642
      // non-local return from siglongjmp in signal handler: return error
643
0
      probe_result = -1;
644
0
      }
645
0
646
   // Restore old SIGILL handler, if any
647
0
   rc = ::sigaction(SIGILL, &old_sigaction, nullptr);
648
0
   if(rc != 0)
649
0
      throw System_Error("run_cpu_instruction_probe sigaction restore failed", errno);
650
0
651
#else
652
   BOTAN_UNUSED(probe_fn);
653
#endif
654
0
655
0
   return probe_result;
656
0
   }
657
658
std::unique_ptr<OS::Echo_Suppression> OS::suppress_echo_on_terminal()
659
0
   {
660
0
#if defined(BOTAN_TARGET_OS_HAS_POSIX1)
661
0
   class POSIX_Echo_Suppression : public Echo_Suppression
662
0
      {
663
0
      public:
664
0
         POSIX_Echo_Suppression()
665
0
            {
666
0
            m_stdin_fd = fileno(stdin);
667
0
            if(::tcgetattr(m_stdin_fd, &m_old_termios) != 0)
668
0
               throw System_Error("Getting terminal status failed", errno);
669
0
670
0
            struct termios noecho_flags = m_old_termios;
671
0
            noecho_flags.c_lflag &= ~ECHO;
672
0
            noecho_flags.c_lflag |= ECHONL;
673
0
674
0
            if(::tcsetattr(m_stdin_fd, TCSANOW, &noecho_flags) != 0)
675
0
               throw System_Error("Clearing terminal echo bit failed", errno);
676
0
            }
677
0
678
0
         void reenable_echo() override
679
0
            {
680
0
            if(m_stdin_fd > 0)
681
0
               {
682
0
               if(::tcsetattr(m_stdin_fd, TCSANOW, &m_old_termios) != 0)
683
0
                  throw System_Error("Restoring terminal echo bit failed", errno);
684
0
               m_stdin_fd = -1;
685
0
               }
686
0
            }
687
0
688
0
         ~POSIX_Echo_Suppression()
689
0
            {
690
0
            try
691
0
               {
692
0
               reenable_echo();
693
0
               }
694
0
            catch(...)
695
0
               {
696
0
               }
697
0
            }
698
0
699
0
      private:
700
0
         int m_stdin_fd;
701
0
         struct termios m_old_termios;
702
0
      };
703
0
704
0
   return std::unique_ptr<Echo_Suppression>(new POSIX_Echo_Suppression);
705
0
706
#elif defined(BOTAN_TARGET_OS_HAS_WIN32)
707
708
   class Win32_Echo_Suppression : public Echo_Suppression
709
      {
710
      public:
711
         Win32_Echo_Suppression()
712
            {
713
            m_input_handle = ::GetStdHandle(STD_INPUT_HANDLE);
714
            if(::GetConsoleMode(m_input_handle, &m_console_state) == 0)
715
               throw System_Error("Getting console mode failed", ::GetLastError());
716
717
            DWORD new_mode = ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT;
718
            if(::SetConsoleMode(m_input_handle, new_mode) == 0)
719
               throw System_Error("Setting console mode failed", ::GetLastError());
720
            }
721
722
         void reenable_echo() override
723
            {
724
            if(m_input_handle != INVALID_HANDLE_VALUE)
725
               {
726
               if(::SetConsoleMode(m_input_handle, m_console_state) == 0)
727
                  throw System_Error("Setting console mode failed", ::GetLastError());
728
               m_input_handle = INVALID_HANDLE_VALUE;
729
               }
730
            }
731
732
         ~Win32_Echo_Suppression()
733
            {
734
            try
735
               {
736
               reenable_echo();
737
               }
738
            catch(...)
739
               {
740
               }
741
            }
742
743
      private:
744
         HANDLE m_input_handle;
745
         DWORD m_console_state;
746
      };
747
748
   return std::unique_ptr<Echo_Suppression>(new Win32_Echo_Suppression);
749
750
#else
751
752
   // Not supported on this platform, return null
753
   return std::unique_ptr<Echo_Suppression>();
754
#endif
755
0
   }
756
757
}