Coverage Report

Created: 2022-06-23 06:44

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