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