Botan 3.0.0-alpha0
Crypto and TLS for C&
os_utils.cpp
Go to the documentation of this file.
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 #define PR_SET_VMA 0x53564d41
74 #define PR_SET_VMA_ANON_NAME 0
75 #endif
76#endif
77
78namespace Botan {
79
80// Not defined in OS namespace for historical reasons
81void secure_scrub_memory(void* ptr, size_t n)
82 {
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 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 }
110
112 {
113#if defined(BOTAN_TARGET_OS_HAS_POSIX1)
114 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 }
123
125 {
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#if defined(BOTAN_TARGET_OS_HAS_POSIX1) && defined(_SC_LEVEL1_DCACHE_LINESIZE)
134 const long res = ::sysconf(_SC_LEVEL1_DCACHE_LINESIZE);
135 if(res > 0)
136 return static_cast<size_t>(res);
137#endif
138
139#if defined(BOTAN_TARGET_OS_HAS_GETAUXVAL)
140
141 #if defined(AT_L1D_CACHEGEOMETRY)
142 // Cache line size is bottom 16 bits
143 const unsigned long dcache_info = OS::get_auxval(AT_L1D_CACHEGEOMETRY);
144 if(dcache_info != 0)
145 return static_cast<size_t>(dcache_info & 0xFFFF);
146 #endif
147
148 #if defined(AT_DCACHEBSIZE)
149 const unsigned long dcache_bsize = OS::get_auxval(AT_DCACHEBSIZE);
150 if(dcache_bsize != 0)
151 return static_cast<size_t>(dcache_bsize);
152 #endif
153
154#endif
155
156 // TODO: on Windows this is returned by GetLogicalProcessorInformation
157
158 // not available on this platform
159 return 0;
160 }
161
162unsigned long OS::get_auxval(unsigned long id)
163 {
164#if defined(BOTAN_TARGET_OS_HAS_GETAUXVAL)
165 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 }
203
205 {
206#if defined(AT_SECURE)
207 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 }
214
216 {
217 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#if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY)
227
228 if(CPUID::has_rdtsc())
229 {
230 uint32_t rtc_low = 0, rtc_high = 0;
231 asm volatile("rdtsc" : "=d" (rtc_high), "=a" (rtc_low));
232 rtc = (static_cast<uint64_t>(rtc_high) << 32) | rtc_low;
233 }
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#endif
272
273 return rtc;
274 }
275
277 {
278#if defined(BOTAN_TARGET_OS_HAS_POSIX1)
279
280#if defined(_SC_NPROCESSORS_ONLN)
281 const long cpu_online = ::sysconf(_SC_NPROCESSORS_ONLN);
282 if(cpu_online > 0)
283 return static_cast<size_t>(cpu_online);
284#endif
285
286#if defined(_SC_NPROCESSORS_CONF)
287 const long cpu_conf = ::sysconf(_SC_NPROCESSORS_CONF);
288 if(cpu_conf > 0)
289 return static_cast<size_t>(cpu_conf);
290#endif
291
292#endif
293
294#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 const size_t hw_concur = std::thread::hardware_concurrency();
298
299 if(hw_concur > 0)
300 return hw_concur;
301#endif
302
303 return 1;
304 }
305
307 {
308 if(uint64_t cpu_clock = OS::get_cpu_cycle_counter())
309 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#if defined(BOTAN_TARGET_OS_HAS_CLOCK_GETTIME)
323
324 // The ordering here is somewhat arbitrary...
325 const clockid_t clock_types[] = {
326#if defined(CLOCK_MONOTONIC_HR)
327 CLOCK_MONOTONIC_HR,
328#endif
329#if defined(CLOCK_MONOTONIC_RAW)
330 CLOCK_MONOTONIC_RAW,
331#endif
332#if defined(CLOCK_MONOTONIC)
333 CLOCK_MONOTONIC,
334#endif
335#if defined(CLOCK_PROCESS_CPUTIME_ID)
336 CLOCK_PROCESS_CPUTIME_ID,
337#endif
338#if defined(CLOCK_THREAD_CPUTIME_ID)
339 CLOCK_THREAD_CPUTIME_ID,
340#endif
341 };
342
343 for(clockid_t clock : clock_types)
344 {
345 struct timespec ts;
346 if(::clock_gettime(clock, &ts) == 0)
347 {
348 return (static_cast<uint64_t>(ts.tv_sec) * 1000000000) + static_cast<uint64_t>(ts.tv_nsec);
349 }
350 }
351#endif
352
353 // Plain C++11 fallback
354 auto now = std::chrono::high_resolution_clock::now().time_since_epoch();
355 return std::chrono::duration_cast<std::chrono::nanoseconds>(now).count();
356 }
357
359 {
360#if defined(BOTAN_TARGET_OS_HAS_CLOCK_GETTIME)
361 struct timespec ts;
362 if(::clock_gettime(CLOCK_REALTIME, &ts) == 0)
363 {
364 return (static_cast<uint64_t>(ts.tv_sec) * 1000000000) + static_cast<uint64_t>(ts.tv_nsec);
365 }
366#endif
367
368 auto now = std::chrono::system_clock::now().time_since_epoch();
369 return std::chrono::duration_cast<std::chrono::nanoseconds>(now).count();
370 }
371
373 {
374 const size_t default_page_size = 4096;
375
376#if defined(BOTAN_TARGET_OS_HAS_POSIX1)
377 long p = ::sysconf(_SC_PAGESIZE);
378 if(p > 1)
379 return static_cast<size_t>(p);
380 else
381 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 }
391
393 {
394#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 const size_t user_req = read_env_variable_sz("BOTAN_MLOCK_POOL_SIZE", BOTAN_MLOCK_ALLOCATOR_MAX_LOCKED_KB);
408
409 const size_t mlock_requested = std::min<size_t>(user_req, BOTAN_MLOCK_ALLOCATOR_MAX_LOCKED_KB);
410
411 if(mlock_requested > 0)
412 {
413 struct ::rlimit limits;
414
415 ::getrlimit(RLIMIT_MEMLOCK, &limits);
416
417 if(limits.rlim_cur < limits.rlim_max)
418 {
419 limits.rlim_cur = limits.rlim_max;
420 ::setrlimit(RLIMIT_MEMLOCK, &limits);
421 ::getrlimit(RLIMIT_MEMLOCK, &limits);
422 }
423
424 return std::min<size_t>(limits.rlim_cur, mlock_requested * 1024);
425 }
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 return 0;
450 }
451
452bool OS::read_env_variable(std::string& value_out, const std::string& name)
453 {
454 value_out = "";
455
457 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 if(const char* val = std::getenv(name.c_str()))
469 {
470 value_out = val;
471 return true;
472 }
473#endif
474
475 return false;
476 }
477
478size_t OS::read_env_variable_sz(const std::string& name, size_t def)
479 {
480 std::string value;
481 if(read_env_variable(value, name))
482 {
483 try
484 {
485 const size_t val = std::stoul(value, nullptr);
486 return val;
487 }
488 catch(std::exception&) { /* ignore it */ }
489 }
490
491 return def;
492 }
493
494#if defined(BOTAN_TARGET_OS_HAS_POSIX1) && defined(BOTAN_TARGET_OS_HAS_POSIX_MLOCK)
495
496namespace {
497
498int get_locked_fd()
499 {
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 return -1;
517#endif
518 }
519
520}
521
522#endif
523
524std::vector<void*> OS::allocate_locked_pages(size_t count)
525 {
526 std::vector<void*> result;
527
528#if (defined(BOTAN_TARGET_OS_HAS_POSIX1) && defined(BOTAN_TARGET_OS_HAS_POSIX_MLOCK)) || defined(BOTAN_TARGET_OS_HAS_VIRTUAL_LOCK)
529
530 result.reserve(count);
531
532 const size_t page_size = OS::system_page_size();
533
534#if defined(BOTAN_TARGET_OS_HAS_POSIX1) && defined(BOTAN_TARGET_OS_HAS_POSIX_MLOCK)
535 static const int locked_fd = get_locked_fd();
536#endif
537
538 for(size_t i = 0; i != count; ++i)
539 {
540 void* ptr = nullptr;
541
542#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#if !defined(MAP_NOCORE)
549#if defined(MAP_CONCEAL)
550 #define MAP_NOCORE MAP_CONCEAL
551#else
552 #define MAP_NOCORE 0
553#endif
554#endif
555
556#if !defined(PROT_MAX)
557 #define PROT_MAX(p) 0
558#endif
559 const int pflags = PROT_READ | PROT_WRITE;
560
561 ptr = ::mmap(nullptr, 3*page_size,
562 pflags | PROT_MAX(pflags),
563 MAP_ANONYMOUS | MAP_PRIVATE | MAP_NOCORE,
564 /*fd=*/locked_fd, /*offset=*/0);
565
566 if(ptr == MAP_FAILED)
567 {
568 continue;
569 }
570
571 // lock the data page
572 if(::mlock(static_cast<uint8_t*>(ptr) + page_size, page_size) != 0)
573 {
574 ::munmap(ptr, 3*page_size);
575 continue;
576 }
577
578#if defined(MADV_DONTDUMP)
579 // we ignore errors here, as DONTDUMP is just a bonus
580 ::madvise(static_cast<uint8_t*>(ptr) + page_size, page_size, MADV_DONTDUMP);
581#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 std::memset(ptr, 0, 3*page_size); // zero data page and both guard pages
597
598 // Attempts to name the data page
599 page_named(ptr, 3*page_size);
600 // Make guard page preceeding the data page
601 page_prohibit_access(static_cast<uint8_t*>(ptr));
602 // Make guard page following the data page
603 page_prohibit_access(static_cast<uint8_t*>(ptr) + 2*page_size);
604
605 result.push_back(static_cast<uint8_t*>(ptr) + page_size);
606 }
607#else
608 BOTAN_UNUSED(count);
609#endif
610
611 return result;
612 }
613
614void OS::page_allow_access(void* page)
615 {
616#if defined(BOTAN_TARGET_OS_HAS_POSIX1)
617 const size_t page_size = OS::system_page_size();
618 ::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 }
628
630 {
631#if defined(BOTAN_TARGET_OS_HAS_POSIX1)
632 const size_t page_size = OS::system_page_size();
633 ::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 }
643
644void OS::free_locked_pages(const std::vector<void*>& pages)
645 {
646 const size_t page_size = OS::system_page_size();
647
648 for(size_t i = 0; i != pages.size(); ++i)
649 {
650 void* ptr = pages[i];
651
652 secure_scrub_memory(ptr, page_size);
653
654 // ptr points to the data page, guard pages are before and after
655 page_allow_access(static_cast<uint8_t*>(ptr) - page_size);
656 page_allow_access(static_cast<uint8_t*>(ptr) + page_size);
657
658#if defined(BOTAN_TARGET_OS_HAS_POSIX1) && defined(BOTAN_TARGET_OS_HAS_POSIX_MLOCK)
659 ::munlock(ptr, page_size);
660 ::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 }
666 }
667
668void OS::page_named(void* page, size_t size)
669 {
670#if defined(BOTAN_TARGET_OS_HAS_PRCTL)
671 static constexpr char name[] = "Botan mlock pool";
672 int r = prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, reinterpret_cast<uintptr_t>(page), size, name);
673 BOTAN_UNUSED(r);
674#else
675 BOTAN_UNUSED(page, size);
676#endif
677 }
678
679#if defined(BOTAN_TARGET_OS_HAS_POSIX1) && !defined(BOTAN_TARGET_OS_IS_EMSCRIPTEN)
680
681namespace {
682
683::sigjmp_buf g_sigill_jmp_buf;
684
685void botan_sigill_handler(int /*unused*/)
686 {
687 siglongjmp(g_sigill_jmp_buf, /*non-zero return value*/1);
688 }
689
690}
691
692#endif
693
694int OS::run_cpu_instruction_probe(const std::function<int ()>& probe_fn)
695 {
696 volatile int probe_result = -3;
697
698#if defined(BOTAN_TARGET_OS_HAS_POSIX1) && !defined(BOTAN_TARGET_OS_IS_EMSCRIPTEN)
699 struct sigaction old_sigaction;
700 struct sigaction sigaction;
701
702 sigaction.sa_handler = botan_sigill_handler;
703 sigemptyset(&sigaction.sa_mask);
704 sigaction.sa_flags = 0;
705
706 int rc = ::sigaction(SIGILL, &sigaction, &old_sigaction);
707
708 if(rc != 0)
709 throw System_Error("run_cpu_instruction_probe sigaction failed", errno);
710
711 rc = sigsetjmp(g_sigill_jmp_buf, /*save sigs*/1);
712
713 if(rc == 0)
714 {
715 // first call to sigsetjmp
716 probe_result = probe_fn();
717 }
718 else if(rc == 1)
719 {
720 // non-local return from siglongjmp in signal handler: return error
721 probe_result = -1;
722 }
723
724 // Restore old SIGILL handler, if any
725 rc = ::sigaction(SIGILL, &old_sigaction, nullptr);
726 if(rc != 0)
727 throw System_Error("run_cpu_instruction_probe sigaction restore failed", errno);
728
729#else
730 BOTAN_UNUSED(probe_fn);
731#endif
732
733 return probe_result;
734 }
735
736std::unique_ptr<OS::Echo_Suppression> OS::suppress_echo_on_terminal()
737 {
738#if defined(BOTAN_TARGET_OS_HAS_POSIX1)
739 class POSIX_Echo_Suppression : public Echo_Suppression
740 {
741 public:
742 POSIX_Echo_Suppression()
743 {
744 m_stdin_fd = fileno(stdin);
745 if(::tcgetattr(m_stdin_fd, &m_old_termios) != 0)
746 throw System_Error("Getting terminal status failed", errno);
747
748 struct termios noecho_flags = m_old_termios;
749 noecho_flags.c_lflag &= ~ECHO;
750 noecho_flags.c_lflag |= ECHONL;
751
752 if(::tcsetattr(m_stdin_fd, TCSANOW, &noecho_flags) != 0)
753 throw System_Error("Clearing terminal echo bit failed", errno);
754 }
755
756 void reenable_echo() override
757 {
758 if(m_stdin_fd > 0)
759 {
760 if(::tcsetattr(m_stdin_fd, TCSANOW, &m_old_termios) != 0)
761 throw System_Error("Restoring terminal echo bit failed", errno);
762 m_stdin_fd = -1;
763 }
764 }
765
766 ~POSIX_Echo_Suppression() override
767 {
768 try
769 {
770 reenable_echo();
771 }
772 catch(...)
773 {
774 }
775 }
776
777 POSIX_Echo_Suppression(const POSIX_Echo_Suppression& other) = delete;
778 POSIX_Echo_Suppression(POSIX_Echo_Suppression&& other) = delete;
779 POSIX_Echo_Suppression& operator=(const POSIX_Echo_Suppression& other) = delete;
780 POSIX_Echo_Suppression& operator=(POSIX_Echo_Suppression&& other) = delete;
781
782 private:
783 int m_stdin_fd;
784 struct termios m_old_termios;
785 };
786
787 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 }
844
845}
#define BOTAN_UNUSED(...)
Definition: assert.h:141
std::string name
#define BOTAN_MLOCK_ALLOCATOR_MAX_LOCKED_KB
Definition: build.h:367
bool running_in_privileged_state()
Definition: os_utils.cpp:204
size_t get_memory_locking_limit()
Definition: os_utils.cpp:392
uint64_t BOTAN_TEST_API get_high_resolution_clock()
Definition: os_utils.cpp:306
size_t BOTAN_TEST_API get_cpu_available()
Definition: os_utils.cpp:276
std::unique_ptr< Echo_Suppression > BOTAN_UNSTABLE_API suppress_echo_on_terminal()
Definition: os_utils.cpp:736
size_t get_cache_line_size()
Definition: os_utils.cpp:124
void page_allow_access(void *page)
Definition: os_utils.cpp:614
void page_prohibit_access(void *page)
Definition: os_utils.cpp:629
size_t read_env_variable_sz(const std::string &var_name, size_t def_value=0)
Definition: os_utils.cpp:478
bool read_env_variable(std::string &value_out, const std::string &var_name)
Definition: os_utils.cpp:452
int BOTAN_TEST_API run_cpu_instruction_probe(const std::function< int()> &probe_fn)
Definition: os_utils.cpp:694
std::vector< void * > allocate_locked_pages(size_t count)
Definition: os_utils.cpp:524
size_t system_page_size()
Definition: os_utils.cpp:372
uint64_t BOTAN_TEST_API get_system_timestamp_ns()
Definition: os_utils.cpp:358
unsigned long get_auxval(unsigned long id)
Definition: os_utils.cpp:162
void free_locked_pages(const std::vector< void * > &pages)
Definition: os_utils.cpp:644
void page_named(void *page, size_t size)
Definition: os_utils.cpp:668
uint32_t BOTAN_TEST_API get_process_id()
Definition: os_utils.cpp:111
uint64_t BOTAN_TEST_API get_cpu_cycle_counter()
Definition: os_utils.cpp:215
Definition: alg_id.cpp:13
void secure_scrub_memory(void *ptr, size_t n)
Definition: os_utils.cpp:81