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