Coverage Report

Created: 2024-02-25 06:16

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