Coverage Report

Created: 2020-08-01 06:18

/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
97.1M
   {
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
97.1M
   Call memset through a static volatile pointer, which the compiler
80
97.1M
   should not elide. This construct should be safe in conforming
81
97.1M
   compilers, but who knows. I did confirm that on x86-64 GCC 6.1 and
82
97.1M
   Clang 3.8 both create code that saves the memset address in the
83
97.1M
   data segment and unconditionally loads and jumps to that address.
84
97.1M
   */
85
97.1M
   static void* (*const volatile memset_ptr)(void*, int, size_t) = std::memset;
86
97.1M
   (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
97.1M
   }
95
96
uint32_t OS::get_process_id()
97
545k
   {
98
545k
#if defined(BOTAN_TARGET_OS_HAS_POSIX1)
99
545k
   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
545k
   }
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
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
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
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
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
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
0
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
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
0
      // 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
0
      // 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
0
      // Make guard page preceeding the data page
534
0
      page_prohibit_access(static_cast<uint8_t*>(ptr));
535
0
      // 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
0
      // 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
0
      // first call to sigsetjmp
638
0
      probe_result = probe_fn();
639
0
      }
640
0
   else if(rc == 1)
641
0
      {
642
0
      // non-local return from siglongjmp in signal handler: return error
643
0
      probe_result = -1;
644
0
      }
645
0
646
0
   // 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
}