Botan 3.13.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/parsing.h>
14#include <botan/internal/target_info.h>
15
16#if defined(BOTAN_HAS_CPUID)
17 #include <botan/internal/cpuid.h>
18#endif
19
20#include <algorithm>
21#include <chrono>
22#include <cstdlib>
23#include <iomanip>
24#include <sstream>
25
26#if defined(BOTAN_TARGET_OS_HAS_POSIX1)
27 #include <errno.h>
28 #include <pthread.h>
29 #include <setjmp.h>
30 #include <signal.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 // NOLINTBEGIN(*-no-assembler)
180
181 #if defined(BOTAN_TARGET_ARCH_IS_X86_64)
182
183 uint32_t rtc_low = 0; // NOLINT(*-const-correctness) clang-tidy doesn't understand inline asm
184 uint32_t rtc_high = 0; // NOLINT(*-const-correctness) clang-tidy doesn't understand inline asm
185 asm volatile("rdtsc" : "=d"(rtc_high), "=a"(rtc_low));
186 rtc = (static_cast<uint64_t>(rtc_high) << 32) | rtc_low;
187
188 #elif defined(BOTAN_TARGET_ARCH_IS_X86_FAMILY) && defined(BOTAN_HAS_CPUID)
189
191 uint32_t rtc_low = 0;
192 uint32_t rtc_high = 0;
193 asm volatile("rdtsc" : "=d"(rtc_high), "=a"(rtc_low));
194 rtc = (static_cast<uint64_t>(rtc_high) << 32) | rtc_low;
195 }
196
197 #elif defined(BOTAN_TARGET_ARCH_IS_PPC64)
198
199 for(;;) {
200 uint32_t rtc_low = 0;
201 uint32_t rtc_high = 0;
202 uint32_t rtc_high2 = 0;
203 asm volatile("mftbu %0" : "=r"(rtc_high));
204 asm volatile("mftb %0" : "=r"(rtc_low));
205 asm volatile("mftbu %0" : "=r"(rtc_high2));
206
207 if(rtc_high == rtc_high2) {
208 rtc = (static_cast<uint64_t>(rtc_high) << 32) | rtc_low;
209 break;
210 }
211 }
212
213 #elif defined(BOTAN_TARGET_ARCH_IS_ALPHA)
214 asm volatile("rpcc %0" : "=r"(rtc));
215
216 #elif defined(BOTAN_TARGET_ARCH_IS_SPARC64) && !defined(BOTAN_TARGET_OS_IS_OPENBSD)
217 // OpenBSD does not trap access to the %tick register so we avoid it there
218 asm volatile("rd %%tick, %0" : "=r"(rtc));
219
220 #elif defined(BOTAN_TARGET_ARCH_IS_IA64)
221 asm volatile("mov %0=ar.itc" : "=r"(rtc));
222
223 #elif defined(BOTAN_TARGET_ARCH_IS_S390X)
224 asm volatile("stck 0(%0)" : : "a"(&rtc) : "memory", "cc");
225
226 #elif defined(BOTAN_TARGET_ARCH_IS_HPPA)
227 asm volatile("mfctl 16,%0" : "=r"(rtc)); // 64-bit only?
228
229 #else
230 //#warning "OS::get_cpu_cycle_counter not implemented"
231 #endif
232
233 // NOLINTEND(*-no-assembler)
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(const 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(const clockid_t clock : clock_types) {
310 struct timespec ts {};
311
312 if(::clock_gettime(clock, &ts) == 0) {
313 return (static_cast<uint64_t>(ts.tv_sec) * 1000000000) + static_cast<uint64_t>(ts.tv_nsec);
314 }
315 }
316#endif
317
318#if defined(BOTAN_TARGET_OS_HAS_SYSTEM_CLOCK)
319 // Plain C++11 fallback
320 auto now = std::chrono::high_resolution_clock::now().time_since_epoch();
321 return std::chrono::duration_cast<std::chrono::nanoseconds>(now).count();
322#else
323 return 0;
324#endif
325}
326
328#if defined(BOTAN_TARGET_OS_HAS_CLOCK_GETTIME)
329 struct timespec ts {};
330
331 if(::clock_gettime(CLOCK_REALTIME, &ts) == 0) {
332 return (static_cast<uint64_t>(ts.tv_sec) * 1000000000) + static_cast<uint64_t>(ts.tv_nsec);
333 }
334#endif
335
336#if defined(BOTAN_TARGET_OS_HAS_SYSTEM_CLOCK)
337 auto now = std::chrono::system_clock::now().time_since_epoch();
338 return std::chrono::duration_cast<std::chrono::nanoseconds>(now).count();
339#else
340 throw Not_Implemented("OS::get_system_timestamp_ns this system does not support a clock");
341#endif
342}
343
344std::string OS::format_time(time_t time, const std::string& format) {
345 std::tm tm{};
346
347#if defined(BOTAN_TARGET_OS_HAS_WIN32)
348 if(::localtime_s(&tm, &time) != 0) {
349 throw Encoding_Error("Could not convert time_t to localtime");
350 }
351#elif defined(BOTAN_TARGET_OS_HAS_POSIX1)
352 if(::localtime_r(&time, &tm) == nullptr) {
353 throw Encoding_Error("Could not convert time_t to localtime");
354 }
355#else
356 if(auto tmp = std::localtime(&time)) {
357 tm = *tmp;
358 } else {
359 throw Encoding_Error("Could not convert time_t to localtime");
360 }
361#endif
362
363 std::ostringstream oss;
364 oss << std::put_time(&tm, format.c_str());
365 return oss.str();
366}
367
369 const size_t default_page_size = 4096;
370
371#if defined(BOTAN_TARGET_OS_HAS_POSIX1)
372 const long p = ::sysconf(_SC_PAGESIZE);
373 if(p > 1) {
374 return static_cast<size_t>(p);
375 } else {
376 return default_page_size;
377 }
378#elif defined(BOTAN_TARGET_OS_HAS_VIRTUAL_LOCK)
379 BOTAN_UNUSED(default_page_size);
380 SYSTEM_INFO sys_info;
381 ::GetSystemInfo(&sys_info);
382 return sys_info.dwPageSize;
383#else
384 return default_page_size;
385#endif
386}
387
389 /*
390 * Linux defaults to only 64 KiB of mlockable memory per process (too small)
391 * but BSDs offer a small fraction of total RAM (more than we need). Bound the
392 * total mlock size to 512 KiB which is enough to run the entire test suite
393 * without spilling to non-mlock memory (and thus presumably also enough for
394 * many useful programs), but small enough that we should not cause problems
395 * even if many processes are mlocking on the same machine.
396 */
397 const size_t max_locked_kb = 512;
398
399 /*
400 * If RLIMIT_MEMLOCK is not defined, likely the OS does not support
401 * unprivileged mlock calls.
402 */
403#if defined(RLIMIT_MEMLOCK) && defined(BOTAN_TARGET_OS_HAS_POSIX1) && defined(BOTAN_TARGET_OS_HAS_POSIX_MLOCK)
404 const size_t mlock_requested =
405 std::min<size_t>(read_env_variable_sz("BOTAN_MLOCK_POOL_SIZE", max_locked_kb), max_locked_kb);
406
407 if(mlock_requested > 0) {
408 struct ::rlimit limits {};
409
410 ::getrlimit(RLIMIT_MEMLOCK, &limits);
411
412 if(limits.rlim_cur < limits.rlim_max) {
413 limits.rlim_cur = limits.rlim_max;
414 ::setrlimit(RLIMIT_MEMLOCK, &limits);
415 ::getrlimit(RLIMIT_MEMLOCK, &limits);
416 }
417
418 return std::min<size_t>(limits.rlim_cur, mlock_requested * 1024);
419 }
420
421#elif defined(BOTAN_TARGET_OS_HAS_VIRTUAL_LOCK)
422 const size_t mlock_requested =
423 std::min<size_t>(read_env_variable_sz("BOTAN_MLOCK_POOL_SIZE", max_locked_kb), max_locked_kb);
424
425 SIZE_T working_min = 0, working_max = 0;
426 if(!::GetProcessWorkingSetSize(::GetCurrentProcess(), &working_min, &working_max)) {
427 return 0;
428 }
429
430 // According to Microsoft MSDN:
431 // 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
432 // In the book "Windows Internals Part 2": the maximum lockable pages are minimum working set size - 8 pages
433 // But the information in the book seems to be inaccurate/outdated
434 // I've tested this on Windows 8.1 x64, Windows 10 x64 and Windows 7 x86
435 // On all three OS the value is 11 instead of 8
436 const size_t overhead = OS::system_page_size() * 11;
437 if(working_min > overhead) {
438 const size_t lockable_bytes = working_min - overhead;
439 return std::min<size_t>(lockable_bytes, mlock_requested * 1024);
440 }
441#else
442 // Not supported on this platform
443 BOTAN_UNUSED(max_locked_kb);
444#endif
445
446 return 0;
447}
448
449bool OS::read_env_variable(std::string& value_out, std::string_view name_view) {
450 value_out = "";
451
452 if(running_in_privileged_state()) {
453 return false;
454 }
455
456#if defined(BOTAN_TARGET_OS_HAS_WIN32) && \
457 (defined(BOTAN_BUILD_COMPILER_IS_MSVC) || defined(BOTAN_BUILD_COMPILER_IS_CLANGCL))
458 const std::string name(name_view);
459 char val[128] = {0};
460 size_t req_size = 0;
461 if(getenv_s(&req_size, val, sizeof(val), name.c_str()) == 0) {
462 // Microsoft's implementation always writes a terminating \0,
463 // and includes it in the reported length of the environment variable
464 // if a value exists.
465 if(req_size > 0 && val[req_size - 1] == '\0') {
466 value_out = std::string(val);
467 } else {
468 value_out = std::string(val, req_size);
469 }
470 return true;
471 }
472#else
473 const std::string name(name_view);
474 if(const char* val = std::getenv(name.c_str())) {
475 value_out = val;
476 return true;
477 }
478#endif
479
480 return false;
481}
482
483size_t OS::read_env_variable_sz(std::string_view name, size_t def) {
484 std::string value;
485 if(read_env_variable(value, name) && !value.empty()) {
486 if(const auto sz = parse_sz(value)) {
487 return *sz;
488 }
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 #if defined(BOTAN_TARGET_OS_IS_IOS) || defined(BOTAN_TARGET_OS_IS_MACOS)
500 // On Darwin, tagging anonymous pages allows vmmap to track these.
501 // Allowed from 240 to 255 for userland applications
502 static constexpr int default_locked_fd = 255;
503 int locked_fd = default_locked_fd;
504
505 if(size_t locked_fdl = OS::read_env_variable_sz("BOTAN_LOCKED_FD", default_locked_fd)) {
506 if(locked_fdl < 240 || locked_fdl > 255) {
507 locked_fdl = default_locked_fd;
508 }
509 locked_fd = static_cast<int>(locked_fdl);
510 }
511 return VM_MAKE_TAG(locked_fd);
512 #else
513 return -1;
514 #endif
515}
516
517int mmap_flags() {
518 int flags = MAP_PRIVATE;
519
520 #if defined(MAP_ANONYMOUS)
521 flags |= MAP_ANONYMOUS;
522 #elif defined(MAP_ANON)
523 flags |= MAP_ANON;
524 #endif
525
526 #if defined(MAP_CONCEAL)
527 flags |= MAP_CONCEAL;
528 #elif defined(MAP_NOCORE)
529 flags |= MAP_NOCORE;
530 #endif
531
532 return flags;
533}
534
535int mmap_prot() {
536 int prot = PROT_READ | PROT_WRITE; // NOLINT(*-const-correctness)
537
538 #if defined(PROT_MAX)
539 prot |= PROT_MAX(prot);
540 #endif
541
542 return prot;
543}
544
545} // namespace
546
547#endif
548
549std::vector<void*> OS::allocate_locked_pages(size_t count) {
550 std::vector<void*> result;
551
552#if(defined(BOTAN_TARGET_OS_HAS_POSIX1) && defined(BOTAN_TARGET_OS_HAS_POSIX_MLOCK)) || \
553 defined(BOTAN_TARGET_OS_HAS_VIRTUAL_LOCK)
554
555 result.reserve(count);
556
557 const size_t page_size = OS::system_page_size();
558
559 #if defined(BOTAN_TARGET_OS_HAS_POSIX1) && defined(BOTAN_TARGET_OS_HAS_POSIX_MLOCK)
560 static const int locked_fd = get_locked_fd();
561 #endif
562
563 for(size_t i = 0; i != count; ++i) {
564 void* ptr = nullptr;
565
566 #if defined(BOTAN_TARGET_OS_HAS_POSIX1) && defined(BOTAN_TARGET_OS_HAS_POSIX_MLOCK)
567 ptr = ::mmap(nullptr,
568 3 * page_size,
569 mmap_prot(),
570 mmap_flags(),
571 /*fd=*/locked_fd,
572 /*offset=*/0);
573
574 if(ptr == MAP_FAILED) {
575 continue;
576 }
577
578 // lock the data page
579 if(::mlock(static_cast<uint8_t*>(ptr) + page_size, page_size) != 0) {
580 ::munmap(ptr, 3 * page_size);
581 continue;
582 }
583
584 #if defined(MADV_DONTDUMP)
585 // we ignore errors here, as DONTDUMP is just a bonus
586 ::madvise(static_cast<uint8_t*>(ptr) + page_size, page_size, MADV_DONTDUMP);
587 #endif
588
589 #elif defined(BOTAN_TARGET_OS_HAS_VIRTUAL_LOCK)
590 ptr = ::VirtualAlloc(nullptr, 3 * page_size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
591
592 if(ptr == nullptr)
593 continue;
594
595 if(::VirtualLock(static_cast<uint8_t*>(ptr) + page_size, page_size) == 0) {
596 ::VirtualFree(ptr, 0, MEM_RELEASE);
597 continue;
598 }
599 #endif
600
601 std::memset(ptr, 0, 3 * page_size); // zero data page and both guard pages
602
603 // Attempts to name the data page
604 page_named(ptr, 3 * page_size);
605 // Make guard page preceding the data page
606 page_prohibit_access(static_cast<uint8_t*>(ptr));
607 // Make guard page following the data page
608 page_prohibit_access(static_cast<uint8_t*>(ptr) + 2 * page_size);
609
610 result.push_back(static_cast<uint8_t*>(ptr) + page_size);
611 }
612#else
613 BOTAN_UNUSED(count);
614#endif
615
616 return result;
617}
618
619void OS::page_allow_access(void* page) {
620#if defined(BOTAN_TARGET_OS_HAS_POSIX1)
621 const size_t page_size = OS::system_page_size();
622 ::mprotect(page, page_size, PROT_READ | PROT_WRITE);
623#elif defined(BOTAN_TARGET_OS_HAS_VIRTUAL_LOCK)
624 const size_t page_size = OS::system_page_size();
625 DWORD old_perms = 0;
626 ::VirtualProtect(page, page_size, PAGE_READWRITE, &old_perms);
627 BOTAN_UNUSED(old_perms);
628#else
629 BOTAN_UNUSED(page);
630#endif
631}
632
633void OS::page_prohibit_access(void* page) {
634#if defined(BOTAN_TARGET_OS_HAS_POSIX1)
635 const size_t page_size = OS::system_page_size();
636 ::mprotect(page, page_size, PROT_NONE);
637#elif defined(BOTAN_TARGET_OS_HAS_VIRTUAL_LOCK)
638 const size_t page_size = OS::system_page_size();
639 DWORD old_perms = 0;
640 ::VirtualProtect(page, page_size, PAGE_NOACCESS, &old_perms);
641 BOTAN_UNUSED(old_perms);
642#else
643 BOTAN_UNUSED(page);
644#endif
645}
646
647void OS::free_locked_pages(const std::vector<void*>& pages) {
648 const size_t page_size = OS::system_page_size();
649
650 for(void* ptr : pages) {
651 secure_scrub_memory(ptr, page_size);
652
653 // ptr points to the data page, guard pages are before and after
654 page_allow_access(static_cast<uint8_t*>(ptr) - page_size);
655 page_allow_access(static_cast<uint8_t*>(ptr) + page_size);
656
657#if defined(BOTAN_TARGET_OS_HAS_POSIX1) && defined(BOTAN_TARGET_OS_HAS_POSIX_MLOCK)
658 ::munlock(ptr, page_size);
659 ::munmap(static_cast<uint8_t*>(ptr) - page_size, 3 * page_size);
660#elif defined(BOTAN_TARGET_OS_HAS_VIRTUAL_LOCK)
661 ::VirtualUnlock(ptr, page_size);
662 ::VirtualFree(static_cast<uint8_t*>(ptr) - page_size, 0, MEM_RELEASE);
663#endif
664 }
665}
666
667void OS::page_named(void* page, size_t size) {
668#if defined(BOTAN_TARGET_OS_HAS_PRCTL) && defined(PR_SET_VMA) && defined(PR_SET_VMA_ANON_NAME)
669 static constexpr char name[] = "Botan mlock pool";
670 // NOLINTNEXTLINE(*-vararg)
671 const int r = prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, reinterpret_cast<uintptr_t>(page), size, name);
672 BOTAN_UNUSED(r);
673#else
674 BOTAN_UNUSED(page, size);
675#endif
676}
677
678#if defined(BOTAN_TARGET_OS_HAS_THREADS)
679void OS::set_thread_name(std::thread& thread, const std::string& name) {
680 #if defined(BOTAN_TARGET_OS_IS_LINUX) || defined(BOTAN_TARGET_OS_IS_FREEBSD) || defined(BOTAN_TARGET_OS_IS_DRAGONFLY)
681 static_cast<void>(pthread_setname_np(thread.native_handle(), name.c_str()));
682 #elif defined(BOTAN_TARGET_OS_IS_OPENBSD)
683 static_cast<void>(pthread_set_name_np(thread.native_handle(), name.c_str()));
684 #elif defined(BOTAN_TARGET_OS_IS_NETBSD)
685 static_cast<void>(pthread_setname_np(thread.native_handle(), "%s", const_cast<char*>(name.c_str())));
686 #elif defined(BOTAN_TARGET_OS_HAS_WIN32) && defined(_LIBCPP_HAS_THREAD_API_PTHREAD)
687 static_cast<void>(pthread_setname_np(thread.native_handle(), name.c_str()));
688 #elif defined(BOTAN_TARGET_OS_HAS_WIN32) && defined(BOTAN_BUILD_COMPILER_IS_MSVC)
689 typedef HRESULT(WINAPI * std_proc)(HANDLE, PCWSTR);
690 HMODULE kern = GetModuleHandleA("KernelBase.dll");
691 std_proc set_thread_name = reinterpret_cast<std_proc>(GetProcAddress(kern, "SetThreadDescription"));
692 if(set_thread_name) {
693 std::wstring w;
694 auto sz = MultiByteToWideChar(CP_UTF8, 0, name.data(), -1, nullptr, 0);
695 if(sz > 0) {
696 w.resize(sz);
697 if(MultiByteToWideChar(CP_UTF8, 0, name.data(), -1, &w[0], sz) > 0) {
698 (void)set_thread_name(thread.native_handle(), w.c_str());
699 }
700 }
701 }
702 #elif defined(BOTAN_TARGET_OS_IF_HAIKU)
703 auto thread_id = get_pthread_thread_id(thread.native_handle());
704 static_cast<void>(rename_thread(thread_id, name.c_str()));
705 #else
706 // TODO other possible oses ?
707 // macOs does not seem to allow to name threads other than the current one.
708 BOTAN_UNUSED(thread, name);
709 #endif
710}
711#endif
712
713#if defined(BOTAN_TARGET_OS_HAS_POSIX1) && !defined(BOTAN_TARGET_OS_IS_EMSCRIPTEN)
714
715namespace {
716
717// NOLINTNEXTLINE(*-avoid-non-const-global-variables)
718::sigjmp_buf g_sigill_jmp_buf;
719
720void botan_sigill_handler(int /*unused*/) {
721 siglongjmp(g_sigill_jmp_buf, /*non-zero return value*/ 1);
722}
723
724} // namespace
725
726#endif
727
728int OS::run_cpu_instruction_probe(const std::function<int()>& probe_fn) {
729 volatile int probe_result = -3;
730
731#if defined(BOTAN_TARGET_OS_HAS_POSIX1) && !defined(BOTAN_TARGET_OS_IS_EMSCRIPTEN)
732 struct sigaction old_sigaction {};
733
734 struct sigaction sigaction {};
735
736 sigaction.sa_handler = botan_sigill_handler;
737 sigemptyset(&sigaction.sa_mask);
738 sigaction.sa_flags = 0;
739
740 int rc = ::sigaction(SIGILL, &sigaction, &old_sigaction);
741
742 if(rc != 0) {
743 throw System_Error("run_cpu_instruction_probe sigaction failed", errno);
744 }
745
746 rc = sigsetjmp(g_sigill_jmp_buf, /*save sigs*/ 1);
747
748 if(rc == 0) {
749 // first call to sigsetjmp
750 probe_result = probe_fn();
751 } else if(rc == 1) {
752 // non-local return from siglongjmp in signal handler: return error
753 probe_result = -1;
754 }
755
756 // Restore old SIGILL handler, if any
757 rc = ::sigaction(SIGILL, &old_sigaction, nullptr);
758 if(rc != 0) {
759 throw System_Error("run_cpu_instruction_probe sigaction restore failed", errno);
760 }
761
762#else
763 BOTAN_UNUSED(probe_fn);
764#endif
765
766 return probe_result;
767}
768
769std::unique_ptr<OS::Echo_Suppression> OS::suppress_echo_on_terminal() {
770#if defined(BOTAN_TARGET_OS_HAS_POSIX1)
771 class POSIX_Echo_Suppression final : public Echo_Suppression {
772 public:
773 POSIX_Echo_Suppression() : m_stdin_fd(fileno(stdin)), m_old_termios{} {
774 if(::tcgetattr(m_stdin_fd, &m_old_termios) != 0) {
775 throw System_Error("Getting terminal status failed", errno);
776 }
777
778 struct termios noecho_flags = m_old_termios;
779 noecho_flags.c_lflag &= ~ECHO;
780 noecho_flags.c_lflag |= ECHONL;
781
782 if(::tcsetattr(m_stdin_fd, TCSANOW, &noecho_flags) != 0) {
783 throw System_Error("Clearing terminal echo bit failed", errno);
784 }
785 }
786
787 void reenable_echo() override {
788 if(m_stdin_fd > 0) {
789 if(::tcsetattr(m_stdin_fd, TCSANOW, &m_old_termios) != 0) {
790 throw System_Error("Restoring terminal echo bit failed", errno);
791 }
792 m_stdin_fd = -1;
793 }
794 }
795
796 ~POSIX_Echo_Suppression() override {
797 try {
798 reenable_echo();
799 } catch(...) {}
800 }
801
802 POSIX_Echo_Suppression(const POSIX_Echo_Suppression& other) = delete;
803 POSIX_Echo_Suppression(POSIX_Echo_Suppression&& other) = delete;
804 POSIX_Echo_Suppression& operator=(const POSIX_Echo_Suppression& other) = delete;
805 POSIX_Echo_Suppression& operator=(POSIX_Echo_Suppression&& other) = delete;
806
807 private:
808 int m_stdin_fd;
809 struct termios m_old_termios;
810 };
811
812 return std::make_unique<POSIX_Echo_Suppression>();
813
814#elif defined(BOTAN_TARGET_OS_HAS_WIN32)
815
816 class Win32_Echo_Suppression final : public Echo_Suppression {
817 public:
818 Win32_Echo_Suppression() {
819 m_input_handle = ::GetStdHandle(STD_INPUT_HANDLE);
820 if(::GetConsoleMode(m_input_handle, &m_console_state) == 0)
821 throw System_Error("Getting console mode failed", ::GetLastError());
822
823 DWORD new_mode = ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT;
824 if(::SetConsoleMode(m_input_handle, new_mode) == 0)
825 throw System_Error("Setting console mode failed", ::GetLastError());
826 }
827
828 void reenable_echo() override {
829 if(m_input_handle != INVALID_HANDLE_VALUE) {
830 if(::SetConsoleMode(m_input_handle, m_console_state) == 0)
831 throw System_Error("Setting console mode failed", ::GetLastError());
832 m_input_handle = INVALID_HANDLE_VALUE;
833 }
834 }
835
836 ~Win32_Echo_Suppression() override {
837 try {
838 reenable_echo();
839 } catch(...) {}
840 }
841
842 Win32_Echo_Suppression(const Win32_Echo_Suppression& other) = delete;
843 Win32_Echo_Suppression(Win32_Echo_Suppression&& other) = delete;
844 Win32_Echo_Suppression& operator=(const Win32_Echo_Suppression& other) = delete;
845 Win32_Echo_Suppression& operator=(Win32_Echo_Suppression&& other) = delete;
846
847 private:
848 HANDLE m_input_handle;
849 DWORD m_console_state;
850 };
851
852 return std::make_unique<Win32_Echo_Suppression>();
853
854#else
855
856 // Not supported on this platform, return null
857 return nullptr;
858#endif
859}
860
861} // 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:388
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:769
size_t read_env_variable_sz(std::string_view var_name, size_t def_value=0)
Definition os_utils.cpp:483
void page_allow_access(void *page)
Definition os_utils.cpp:619
std::string BOTAN_TEST_API format_time(time_t time, const std::string &format)
Definition os_utils.cpp:344
bool read_env_variable(std::string &value_out, std::string_view var_name)
Definition os_utils.cpp:449
void page_prohibit_access(void *page)
Definition os_utils.cpp:633
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:728
std::vector< void * > allocate_locked_pages(size_t count)
Definition os_utils.cpp:549
size_t system_page_size()
Definition os_utils.cpp:368
uint64_t BOTAN_TEST_API get_system_timestamp_ns()
Definition os_utils.cpp:327
void free_locked_pages(const std::vector< void * > &pages)
Definition os_utils.cpp:647
void page_named(void *page, size_t size)
Definition os_utils.cpp:667
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
Flags flags(Flag flags)
Definition p11.h:1235
std::optional< size_t > parse_sz(std::string_view input, bool require_canonical)
Definition parsing.cpp:72
void secure_scrub_memory(void *ptr, size_t n)
Definition mem_utils.cpp:25