• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

randombit / botan / 5079590438

25 May 2023 12:28PM UTC coverage: 92.228% (+0.5%) from 91.723%
5079590438

Pull #3502

github

Pull Request #3502: Apply clang-format to the codebase

75589 of 81959 relevant lines covered (92.23%)

12139530.51 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

67.48
/src/lib/utils/os_utils.cpp
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_THREADS)
20
   #include <thread>
21
#endif
22

23
#if defined(BOTAN_TARGET_OS_HAS_EXPLICIT_BZERO)
24
   #include <string.h>
25
#endif
26

27
#if defined(BOTAN_TARGET_OS_HAS_POSIX1)
28
   #include <errno.h>
29
   #include <setjmp.h>
30
   #include <signal.h>
31
   #include <stdlib.h>
32
   #include <sys/mman.h>
33
   #include <sys/resource.h>
34
   #include <sys/types.h>
35
   #include <termios.h>
36
   #include <unistd.h>
37
   #undef B0
38
#endif
39

40
#if defined(BOTAN_TARGET_OS_IS_EMSCRIPTEN)
41
   #include <emscripten/emscripten.h>
42
#endif
43

44
#if defined(BOTAN_TARGET_OS_HAS_GETAUXVAL) || defined(BOTAN_TARGET_OS_IS_ANDROID) || \
45
   defined(BOTAN_TARGET_OS_HAS_ELF_AUX_INFO)
46
   #include <sys/auxv.h>
47
#endif
48

49
#if defined(BOTAN_TARGET_OS_HAS_AUXINFO)
50
   #include <dlfcn.h>
51
   #include <elf.h>
52
#endif
53

54
#if defined(BOTAN_TARGET_OS_HAS_WIN32)
55
   #define NOMINMAX 1
56
   #define _WINSOCKAPI_  // stop windows.h including winsock.h
57
   #include <windows.h>
58
#endif
59

60
#if defined(BOTAN_TARGET_OS_IS_ANDROID)
61
   #include <elf.h>
62
extern "C" char** environ;
63
#endif
64

65
#if defined(BOTAN_TARGET_OS_IS_IOS) || defined(BOTAN_TARGET_OS_IS_MACOS)
66
   #include <mach/vm_statistics.h>
67
   #include <sys/sysctl.h>
68
   #include <sys/types.h>
69
#endif
70

71
#if defined(BOTAN_TARGET_OS_HAS_PRCTL)
72
   #include <sys/prctl.h>
73
   #if !defined(PR_SET_VMA)
74
      #define PR_SET_VMA 0x53564d41
75
      #define PR_SET_VMA_ANON_NAME 0
76
   #endif
77
#endif
78

79
namespace Botan {
80

81
// Not defined in OS namespace for historical reasons
82
void secure_scrub_memory(void* ptr, size_t n) {
233,905,320✔
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);
233,905,320✔
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
   static void* (*const volatile memset_ptr)(void*, int, size_t) = std::memset;
101
   (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
}
233,905,320✔
110

111
uint32_t OS::get_process_id() {
221,422✔
112
#if defined(BOTAN_TARGET_OS_HAS_POSIX1)
113
   return ::getpid();
221,422✔
114
#elif defined(BOTAN_TARGET_OS_HAS_WIN32)
115
   return ::GetCurrentProcessId();
116
#elif defined(BOTAN_TARGET_OS_IS_LLVM) || defined(BOTAN_TARGET_OS_IS_NONE)
117
   return 0;  // truly no meaningful value
118
#else
119
   #error "Missing get_process_id"
120
#endif
121
}
122

123
unsigned long OS::get_auxval(unsigned long id) {
22,183✔
124
#if defined(BOTAN_TARGET_OS_HAS_GETAUXVAL)
125
   return ::getauxval(id);
×
126
#elif defined(BOTAN_TARGET_OS_IS_ANDROID) && defined(BOTAN_TARGET_ARCH_IS_ARM32)
127

128
   if(id == 0)
129
      return 0;
130

131
   char** p = environ;
132

133
   while(*p++ != nullptr)
134
      ;
135

136
   Elf32_auxv_t* e = reinterpret_cast<Elf32_auxv_t*>(p);
137

138
   while(e != nullptr) {
139
      if(e->a_type == id)
140
         return e->a_un.a_val;
141
      e++;
142
   }
143

144
   return 0;
145
#elif defined(BOTAN_TARGET_OS_HAS_ELF_AUX_INFO)
146
   unsigned long auxinfo = 0;
147
   ::elf_aux_info(static_cast<int>(id), &auxinfo, sizeof(auxinfo));
148
   return auxinfo;
149
#elif defined(BOTAN_TARGET_OS_HAS_AUXINFO)
150
   for(const AuxInfo* auxinfo = static_cast<AuxInfo*>(::_dlauxinfo()); auxinfo != AT_NULL; ++auxinfo) {
151
      if(id == auxinfo->a_type)
152
         return auxinfo->a_v;
153
   }
154

155
   return 0;
156
#else
157
   BOTAN_UNUSED(id);
158
   return 0;
159
#endif
160
}
161

162
bool OS::running_in_privileged_state() {
22,183✔
163
#if defined(AT_SECURE)
164
   return OS::get_auxval(AT_SECURE) != 0;
×
165
#elif defined(BOTAN_TARGET_OS_HAS_POSIX1)
166
   return (::getuid() != ::geteuid()) || (::getgid() != ::getegid());
167
#else
168
   return false;
169
#endif
170
}
171

172
uint64_t OS::get_cpu_cycle_counter() {
3,214,107✔
173
   uint64_t rtc = 0;
3,214,107✔
174

175
#if defined(BOTAN_TARGET_OS_HAS_WIN32)
176
   LARGE_INTEGER tv;
177
   ::QueryPerformanceCounter(&tv);
178
   rtc = tv.QuadPart;
179

180
#elif defined(BOTAN_USE_GCC_INLINE_ASM)
181

182
   #if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY)
183

184
   if(CPUID::has_rdtsc()) {
3,214,107✔
185
      uint32_t rtc_low = 0, rtc_high = 0;
3,214,107✔
186
      asm volatile("rdtsc" : "=d"(rtc_high), "=a"(rtc_low));
3,214,107✔
187
      rtc = (static_cast<uint64_t>(rtc_high) << 32) | rtc_low;
3,214,107✔
188
   }
189

190
   #elif defined(BOTAN_TARGET_ARCH_IS_PPC64)
191

192
   for(;;) {
193
      uint32_t rtc_low = 0, rtc_high = 0, rtc_high2 = 0;
194
      asm volatile("mftbu %0" : "=r"(rtc_high));
195
      asm volatile("mftb %0" : "=r"(rtc_low));
196
      asm volatile("mftbu %0" : "=r"(rtc_high2));
197

198
      if(rtc_high == rtc_high2) {
199
         rtc = (static_cast<uint64_t>(rtc_high) << 32) | rtc_low;
200
         break;
201
      }
202
   }
203

204
   #elif defined(BOTAN_TARGET_ARCH_IS_ALPHA)
205
   asm volatile("rpcc %0" : "=r"(rtc));
206

207
      // OpenBSD does not trap access to the %tick register
208
   #elif defined(BOTAN_TARGET_ARCH_IS_SPARC64) && !defined(BOTAN_TARGET_OS_IS_OPENBSD)
209
   asm volatile("rd %%tick, %0" : "=r"(rtc));
210

211
   #elif defined(BOTAN_TARGET_ARCH_IS_IA64)
212
   asm volatile("mov %0=ar.itc" : "=r"(rtc));
213

214
   #elif defined(BOTAN_TARGET_ARCH_IS_S390X)
215
   asm volatile("stck 0(%0)" : : "a"(&rtc) : "memory", "cc");
216

217
   #elif defined(BOTAN_TARGET_ARCH_IS_HPPA)
218
   asm volatile("mfctl 16,%0" : "=r"(rtc));  // 64-bit only?
219

220
   #else
221
      //#warning "OS::get_cpu_cycle_counter not implemented"
222
   #endif
223

224
#endif
225

226
   return rtc;
3,214,107✔
227
}
228

229
size_t OS::get_cpu_available() {
751✔
230
#if defined(BOTAN_TARGET_OS_HAS_POSIX1)
231

232
   #if defined(_SC_NPROCESSORS_ONLN)
233
   const long cpu_online = ::sysconf(_SC_NPROCESSORS_ONLN);
751✔
234
   if(cpu_online > 0)
751✔
235
      return static_cast<size_t>(cpu_online);
751✔
236
   #endif
237

238
   #if defined(_SC_NPROCESSORS_CONF)
239
   const long cpu_conf = ::sysconf(_SC_NPROCESSORS_CONF);
×
240
   if(cpu_conf > 0)
×
241
      return static_cast<size_t>(cpu_conf);
×
242
   #endif
243

244
#endif
245

246
#if defined(BOTAN_TARGET_OS_HAS_THREADS)
247
   // hardware_concurrency is allowed to return 0 if the value is not
248
   // well defined or not computable.
249
   const size_t hw_concur = std::thread::hardware_concurrency();
×
250

251
   if(hw_concur > 0)
×
252
      return hw_concur;
253
#endif
254

255
   return 1;
256
}
257

258
uint64_t OS::get_high_resolution_clock() {
4,500✔
259
   if(uint64_t cpu_clock = OS::get_cpu_cycle_counter())
4,500✔
260
      return cpu_clock;
261

262
#if defined(BOTAN_TARGET_OS_IS_EMSCRIPTEN)
263
   return emscripten_get_now();
264
#endif
265

266
   /*
267
   If we got here either we either don't have an asm instruction
268
   above, or (for x86) RDTSC is not available at runtime. Try some
269
   clock_gettimes and return the first one that works, or otherwise
270
   fall back to std::chrono.
271
   */
272

273
#if defined(BOTAN_TARGET_OS_HAS_CLOCK_GETTIME)
274

275
   // The ordering here is somewhat arbitrary...
276
   const clockid_t clock_types[] = {
×
277
   #if defined(CLOCK_MONOTONIC_HR)
278
      CLOCK_MONOTONIC_HR,
279
   #endif
280
   #if defined(CLOCK_MONOTONIC_RAW)
281
      CLOCK_MONOTONIC_RAW,
282
   #endif
283
   #if defined(CLOCK_MONOTONIC)
284
      CLOCK_MONOTONIC,
285
   #endif
286
   #if defined(CLOCK_PROCESS_CPUTIME_ID)
287
      CLOCK_PROCESS_CPUTIME_ID,
288
   #endif
289
   #if defined(CLOCK_THREAD_CPUTIME_ID)
290
      CLOCK_THREAD_CPUTIME_ID,
291
   #endif
292
   };
293

294
   for(clockid_t clock : clock_types) {
×
295
      struct timespec ts;
×
296
      if(::clock_gettime(clock, &ts) == 0) {
×
297
         return (static_cast<uint64_t>(ts.tv_sec) * 1000000000) + static_cast<uint64_t>(ts.tv_nsec);
×
298
      }
299
   }
300
#endif
301

302
   // Plain C++11 fallback
303
   auto now = std::chrono::high_resolution_clock::now().time_since_epoch();
×
304
   return std::chrono::duration_cast<std::chrono::nanoseconds>(now).count();
×
305
}
306

307
uint64_t OS::get_system_timestamp_ns() {
3,210,656✔
308
#if defined(BOTAN_TARGET_OS_HAS_CLOCK_GETTIME)
309
   struct timespec ts;
3,210,656✔
310
   if(::clock_gettime(CLOCK_REALTIME, &ts) == 0) {
3,210,656✔
311
      return (static_cast<uint64_t>(ts.tv_sec) * 1000000000) + static_cast<uint64_t>(ts.tv_nsec);
3,210,656✔
312
   }
313
#endif
314

315
   auto now = std::chrono::system_clock::now().time_since_epoch();
×
316
   return std::chrono::duration_cast<std::chrono::nanoseconds>(now).count();
×
317
}
318

319
size_t OS::system_page_size() {
575,061✔
320
   const size_t default_page_size = 4096;
575,061✔
321

322
#if defined(BOTAN_TARGET_OS_HAS_POSIX1)
323
   long p = ::sysconf(_SC_PAGESIZE);
8,583✔
324
   if(p > 1)
575,061✔
325
      return static_cast<size_t>(p);
575,061✔
326
   else
327
      return default_page_size;
328
#elif defined(BOTAN_TARGET_OS_HAS_VIRTUAL_LOCK)
329
   BOTAN_UNUSED(default_page_size);
330
   SYSTEM_INFO sys_info;
331
   ::GetSystemInfo(&sys_info);
332
   return sys_info.dwPageSize;
333
#else
334
   return default_page_size;
335
#endif
336
}
337

338
size_t OS::get_memory_locking_limit() {
8,583✔
339
   /*
340
   * Linux defaults to only 64 KiB of mlockable memory per process (too small)
341
   * but BSDs offer a small fraction of total RAM (more than we need). Bound the
342
   * total mlock size to 512 KiB which is enough to run the entire test suite
343
   * without spilling to non-mlock memory (and thus presumably also enough for
344
   * many useful programs), but small enough that we should not cause problems
345
   * even if many processes are mlocking on the same machine.
346
   */
347
   const size_t max_locked_kb = 512;
8,583✔
348

349
   /*
350
   * If RLIMIT_MEMLOCK is not defined, likely the OS does not support
351
   * unprivileged mlock calls.
352
   */
353
#if defined(RLIMIT_MEMLOCK) && defined(BOTAN_TARGET_OS_HAS_POSIX1) && defined(BOTAN_TARGET_OS_HAS_POSIX_MLOCK)
354
   const size_t mlock_requested =
8,583✔
355
      std::min<size_t>(read_env_variable_sz("BOTAN_MLOCK_POOL_SIZE", max_locked_kb), max_locked_kb);
8,583✔
356

357
   if(mlock_requested > 0) {
8,583✔
358
      struct ::rlimit limits;
8,583✔
359

360
      ::getrlimit(RLIMIT_MEMLOCK, &limits);
8,583✔
361

362
      if(limits.rlim_cur < limits.rlim_max) {
8,583✔
363
         limits.rlim_cur = limits.rlim_max;
×
364
         ::setrlimit(RLIMIT_MEMLOCK, &limits);
×
365
         ::getrlimit(RLIMIT_MEMLOCK, &limits);
×
366
      }
367

368
      return std::min<size_t>(limits.rlim_cur, mlock_requested * 1024);
8,583✔
369
   }
370

371
#elif defined(BOTAN_TARGET_OS_HAS_VIRTUAL_LOCK)
372
   const size_t mlock_requested =
373
      std::min<size_t>(read_env_variable_sz("BOTAN_MLOCK_POOL_SIZE", max_locked_kb), max_locked_kb);
374

375
   SIZE_T working_min = 0, working_max = 0;
376
   if(!::GetProcessWorkingSetSize(::GetCurrentProcess(), &working_min, &working_max)) {
377
      return 0;
378
   }
379

380
   // According to Microsoft MSDN:
381
   // 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
382
   // In the book "Windows Internals Part 2": the maximum lockable pages are minimum working set size - 8 pages
383
   // But the information in the book seems to be inaccurate/outdated
384
   // I've tested this on Windows 8.1 x64, Windows 10 x64 and Windows 7 x86
385
   // On all three OS the value is 11 instead of 8
386
   const size_t overhead = OS::system_page_size() * 11;
387
   if(working_min > overhead) {
388
      const size_t lockable_bytes = working_min - overhead;
389
      return std::min<size_t>(lockable_bytes, mlock_requested * 1024);
390
   }
391
#else
392
   // Not supported on this platform
393
   BOTAN_UNUSED(max_locked_kb);
394
#endif
395

396
   return 0;
397
}
398

399
bool OS::read_env_variable(std::string& value_out, std::string_view name_view) {
22,183✔
400
   value_out = "";
22,183✔
401

402
   if(running_in_privileged_state())
22,183✔
403
      return false;
404

405
#if defined(BOTAN_TARGET_OS_HAS_WIN32) && defined(BOTAN_BUILD_COMPILER_IS_MSVC)
406
   const std::string name(name_view);
407
   char val[128] = {0};
408
   size_t req_size = 0;
409
   if(getenv_s(&req_size, val, sizeof(val), name.c_str()) == 0) {
410
      value_out = std::string(val, req_size);
411
      return true;
412
   }
413
#else
414
   const std::string name(name_view);
22,183✔
415
   if(const char* val = std::getenv(name.c_str())) {
22,183✔
416
      value_out = val;
22,183✔
417
      return true;
418
   }
419
#endif
420

421
   return false;
422
}
22,183✔
423

424
size_t OS::read_env_variable_sz(std::string_view name, size_t def) {
8,583✔
425
   std::string value;
8,583✔
426
   if(read_env_variable(value, name) && !value.empty()) {
8,583✔
427
      try {
×
428
         const size_t val = std::stoul(value, nullptr);
8,583✔
429
         return val;
430
      } catch(std::exception&) { /* ignore it */
×
431
      }
×
432
   }
433

434
   return def;
435
}
8,583✔
436

437
#if defined(BOTAN_TARGET_OS_HAS_POSIX1) && defined(BOTAN_TARGET_OS_HAS_POSIX_MLOCK)
438

439
namespace {
440

441
int get_locked_fd() {
442
   #if defined(BOTAN_TARGET_OS_IS_IOS) || defined(BOTAN_TARGET_OS_IS_MACOS)
443
   // On Darwin, tagging anonymous pages allows vmmap to track these.
444
   // Allowed from 240 to 255 for userland applications
445
   static constexpr int default_locked_fd = 255;
446
   int locked_fd = default_locked_fd;
447

448
   if(size_t locked_fdl = OS::read_env_variable_sz("BOTAN_LOCKED_FD", default_locked_fd)) {
449
      if(locked_fdl < 240 || locked_fdl > 255) {
450
         locked_fdl = default_locked_fd;
451
      }
452
      locked_fd = static_cast<int>(locked_fdl);
453
   }
454
   return VM_MAKE_TAG(locked_fd);
455
   #else
456
   return -1;
457
   #endif
458
}
459

460
}
461

462
#endif
463

464
std::vector<void*> OS::allocate_locked_pages(size_t count) {
8,583✔
465
   std::vector<void*> result;
8,583✔
466

467
#if(defined(BOTAN_TARGET_OS_HAS_POSIX1) && defined(BOTAN_TARGET_OS_HAS_POSIX_MLOCK)) || \
468
   defined(BOTAN_TARGET_OS_HAS_VIRTUAL_LOCK)
469

470
   result.reserve(count);
8,583✔
471

472
   const size_t page_size = OS::system_page_size();
8,583✔
473

474
   #if defined(BOTAN_TARGET_OS_HAS_POSIX1) && defined(BOTAN_TARGET_OS_HAS_POSIX_MLOCK)
475
   static const int locked_fd = get_locked_fd();
8,583✔
476
   #endif
477

478
   for(size_t i = 0; i != count; ++i) {
145,911✔
479
      void* ptr = nullptr;
137,328✔
480

481
   #if defined(BOTAN_TARGET_OS_HAS_POSIX1) && defined(BOTAN_TARGET_OS_HAS_POSIX_MLOCK)
482

483
      int mmap_flags = MAP_PRIVATE;
137,328✔
484

485
      #if defined(MAP_ANONYMOUS)
486
      mmap_flags |= MAP_ANONYMOUS;
137,328✔
487
      #elif defined(MAP_ANON)
488
      mmap_flags |= MAP_ANON;
489
      #endif
490

491
      #if defined(MAP_CONCEAL)
492
      mmap_flags |= MAP_CONCEAL;
493
      #elif defined(MAP_NOCORE)
494
      mmap_flags |= MAP_NOCORE;
495
      #endif
496

497
      int mmap_prot = PROT_READ | PROT_WRITE;
137,328✔
498

499
      #if defined(PROT_MAX)
500
      mmap_prot |= PROT_MAX(mmap_prot);
501
      #endif
502

503
      ptr = ::mmap(nullptr,
137,328✔
504
                   3 * page_size,
505
                   mmap_prot,
506
                   mmap_flags,
507
                   /*fd=*/locked_fd,
508
                   /*offset=*/0);
509

510
      if(ptr == MAP_FAILED) {
137,328✔
511
         continue;
×
512
      }
513

514
      // lock the data page
515
      if(::mlock(static_cast<uint8_t*>(ptr) + page_size, page_size) != 0) {
137,328✔
516
         ::munmap(ptr, 3 * page_size);
×
517
         continue;
×
518
      }
519

520
      #if defined(MADV_DONTDUMP)
521
      // we ignore errors here, as DONTDUMP is just a bonus
522
      ::madvise(static_cast<uint8_t*>(ptr) + page_size, page_size, MADV_DONTDUMP);
137,328✔
523
      #endif
524

525
   #elif defined(BOTAN_TARGET_OS_HAS_VIRTUAL_LOCK)
526
      ptr = ::VirtualAlloc(nullptr, 3 * page_size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
527

528
      if(ptr == nullptr)
529
         continue;
530

531
      if(::VirtualLock(static_cast<uint8_t*>(ptr) + page_size, page_size) == 0) {
532
         ::VirtualFree(ptr, 0, MEM_RELEASE);
533
         continue;
534
      }
535
   #endif
536

537
      std::memset(ptr, 0, 3 * page_size);  // zero data page and both guard pages
137,328✔
538

539
      // Attempts to name the data page
540
      page_named(ptr, 3 * page_size);
274,656✔
541
      // Make guard page preceeding the data page
542
      page_prohibit_access(static_cast<uint8_t*>(ptr));
137,328✔
543
      // Make guard page following the data page
544
      page_prohibit_access(static_cast<uint8_t*>(ptr) + 2 * page_size);
137,328✔
545

546
      result.push_back(static_cast<uint8_t*>(ptr) + page_size);
137,328✔
547
   }
548
#else
549
   BOTAN_UNUSED(count);
550
#endif
551

552
   return result;
8,583✔
553
}
×
554

555
void OS::page_allow_access(void* page) {
274,656✔
556
#if defined(BOTAN_TARGET_OS_HAS_POSIX1)
557
   const size_t page_size = OS::system_page_size();
274,656✔
558
   ::mprotect(page, page_size, PROT_READ | PROT_WRITE);
274,656✔
559
#elif defined(BOTAN_TARGET_OS_HAS_VIRTUAL_LOCK)
560
   const size_t page_size = OS::system_page_size();
561
   DWORD old_perms = 0;
562
   ::VirtualProtect(page, page_size, PAGE_READWRITE, &old_perms);
563
   BOTAN_UNUSED(old_perms);
564
#else
565
   BOTAN_UNUSED(page);
566
#endif
567
}
274,656✔
568

569
void OS::page_prohibit_access(void* page) {
274,656✔
570
#if defined(BOTAN_TARGET_OS_HAS_POSIX1)
571
   const size_t page_size = OS::system_page_size();
274,656✔
572
   ::mprotect(page, page_size, PROT_NONE);
274,656✔
573
#elif defined(BOTAN_TARGET_OS_HAS_VIRTUAL_LOCK)
574
   const size_t page_size = OS::system_page_size();
575
   DWORD old_perms = 0;
576
   ::VirtualProtect(page, page_size, PAGE_NOACCESS, &old_perms);
577
   BOTAN_UNUSED(old_perms);
578
#else
579
   BOTAN_UNUSED(page);
580
#endif
581
}
274,656✔
582

583
void OS::free_locked_pages(const std::vector<void*>& pages) {
8,583✔
584
   const size_t page_size = OS::system_page_size();
8,583✔
585

586
   for(size_t i = 0; i != pages.size(); ++i) {
145,911✔
587
      void* ptr = pages[i];
137,328✔
588

589
      secure_scrub_memory(ptr, page_size);
137,328✔
590

591
      // ptr points to the data page, guard pages are before and after
592
      page_allow_access(static_cast<uint8_t*>(ptr) - page_size);
137,328✔
593
      page_allow_access(static_cast<uint8_t*>(ptr) + page_size);
137,328✔
594

595
#if defined(BOTAN_TARGET_OS_HAS_POSIX1) && defined(BOTAN_TARGET_OS_HAS_POSIX_MLOCK)
596
      ::munlock(ptr, page_size);
137,328✔
597
      ::munmap(static_cast<uint8_t*>(ptr) - page_size, 3 * page_size);
137,328✔
598
#elif defined(BOTAN_TARGET_OS_HAS_VIRTUAL_LOCK)
599
      ::VirtualUnlock(ptr, page_size);
600
      ::VirtualFree(static_cast<uint8_t*>(ptr) - page_size, 0, MEM_RELEASE);
601
#endif
602
   }
603
}
8,583✔
604

605
void OS::page_named(void* page, size_t size) {
137,328✔
606
#if defined(BOTAN_TARGET_OS_HAS_PRCTL)
607
   static constexpr char name[] = "Botan mlock pool";
137,328✔
608
   int r = prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, reinterpret_cast<uintptr_t>(page), size, name);
137,328✔
609
   BOTAN_UNUSED(r);
137,328✔
610
#else
611
   BOTAN_UNUSED(page, size);
612
#endif
613
}
×
614

615
#if defined(BOTAN_TARGET_OS_HAS_POSIX1) && !defined(BOTAN_TARGET_OS_IS_EMSCRIPTEN)
616

617
namespace {
618

619
::sigjmp_buf g_sigill_jmp_buf;
620

621
void botan_sigill_handler(int /*unused*/) { siglongjmp(g_sigill_jmp_buf, /*non-zero return value*/ 1); }
1✔
622

623
}
624

625
#endif
626

627
int OS::run_cpu_instruction_probe(const std::function<int()>& probe_fn) {
2✔
628
   volatile int probe_result = -3;
2✔
629

630
#if defined(BOTAN_TARGET_OS_HAS_POSIX1) && !defined(BOTAN_TARGET_OS_IS_EMSCRIPTEN)
631
   struct sigaction old_sigaction;
2✔
632
   struct sigaction sigaction;
2✔
633

634
   sigaction.sa_handler = botan_sigill_handler;
2✔
635
   sigemptyset(&sigaction.sa_mask);
2✔
636
   sigaction.sa_flags = 0;
2✔
637

638
   int rc = ::sigaction(SIGILL, &sigaction, &old_sigaction);
2✔
639

640
   if(rc != 0)
2✔
641
      throw System_Error("run_cpu_instruction_probe sigaction failed", errno);
×
642

643
   rc = sigsetjmp(g_sigill_jmp_buf, /*save sigs*/ 1);
3✔
644

645
   if(rc == 0) {
3✔
646
      // first call to sigsetjmp
647
      probe_result = probe_fn();
3✔
648
   } else if(rc == 1) {
1✔
649
      // non-local return from siglongjmp in signal handler: return error
650
      probe_result = -1;
1✔
651
   }
652

653
   // Restore old SIGILL handler, if any
654
   rc = ::sigaction(SIGILL, &old_sigaction, nullptr);
2✔
655
   if(rc != 0)
2✔
656
      throw System_Error("run_cpu_instruction_probe sigaction restore failed", errno);
×
657

658
#else
659
   BOTAN_UNUSED(probe_fn);
660
#endif
661

662
   return probe_result;
2✔
663
}
664

665
std::unique_ptr<OS::Echo_Suppression> OS::suppress_echo_on_terminal() {
×
666
#if defined(BOTAN_TARGET_OS_HAS_POSIX1)
667
   class POSIX_Echo_Suppression : public Echo_Suppression {
×
668
      public:
669
         POSIX_Echo_Suppression() {
×
670
            m_stdin_fd = fileno(stdin);
×
671
            if(::tcgetattr(m_stdin_fd, &m_old_termios) != 0)
×
672
               throw System_Error("Getting terminal status failed", errno);
×
673

674
            struct termios noecho_flags = m_old_termios;
×
675
            noecho_flags.c_lflag &= ~ECHO;
×
676
            noecho_flags.c_lflag |= ECHONL;
×
677

678
            if(::tcsetattr(m_stdin_fd, TCSANOW, &noecho_flags) != 0)
×
679
               throw System_Error("Clearing terminal echo bit failed", errno);
×
680
         }
×
681

682
         void reenable_echo() override {
×
683
            if(m_stdin_fd > 0) {
×
684
               if(::tcsetattr(m_stdin_fd, TCSANOW, &m_old_termios) != 0)
×
685
                  throw System_Error("Restoring terminal echo bit failed", errno);
×
686
               m_stdin_fd = -1;
×
687
            }
688
         }
×
689

690
         ~POSIX_Echo_Suppression() override {
×
691
            try {
×
692
               reenable_echo();
×
693
            } catch(...) {}
×
694
         }
×
695

696
         POSIX_Echo_Suppression(const POSIX_Echo_Suppression& other) = delete;
697
         POSIX_Echo_Suppression(POSIX_Echo_Suppression&& other) = delete;
698
         POSIX_Echo_Suppression& operator=(const POSIX_Echo_Suppression& other) = delete;
699
         POSIX_Echo_Suppression& operator=(POSIX_Echo_Suppression&& other) = delete;
700

701
      private:
702
         int m_stdin_fd;
703
         struct termios m_old_termios;
704
   };
705

706
   return std::make_unique<POSIX_Echo_Suppression>();
×
707

708
#elif defined(BOTAN_TARGET_OS_HAS_WIN32)
709

710
   class Win32_Echo_Suppression : public Echo_Suppression {
711
      public:
712
         Win32_Echo_Suppression() {
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
            if(m_input_handle != INVALID_HANDLE_VALUE) {
724
               if(::SetConsoleMode(m_input_handle, m_console_state) == 0)
725
                  throw System_Error("Setting console mode failed", ::GetLastError());
726
               m_input_handle = INVALID_HANDLE_VALUE;
727
            }
728
         }
729

730
         ~Win32_Echo_Suppression() override {
731
            try {
732
               reenable_echo();
733
            } catch(...) {}
734
         }
735

736
         Win32_Echo_Suppression(const Win32_Echo_Suppression& other) = delete;
737
         Win32_Echo_Suppression(Win32_Echo_Suppression&& other) = delete;
738
         Win32_Echo_Suppression& operator=(const Win32_Echo_Suppression& other) = delete;
739
         Win32_Echo_Suppression& operator=(Win32_Echo_Suppression&& other) = delete;
740

741
      private:
742
         HANDLE m_input_handle;
743
         DWORD m_console_state;
744
   };
745

746
   return std::make_unique<Win32_Echo_Suppression>();
747

748
#else
749

750
   // Not supported on this platform, return null
751
   return nullptr;
752
#endif
753
}
754

755
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc