Botan 3.10.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 // NOLINTBEGIN(*-no-assembler)
180
181 #if defined(BOTAN_TARGET_ARCH_IS_X86_64)
182
183 uint32_t rtc_low = 0;
184 uint32_t rtc_high = 0;
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(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
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 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 try {
487 const size_t val = std::stoul(value, nullptr);
488 return val;
489 } catch(std::exception&) { /* ignore it */
490 }
491 }
492
493 return def;
494}
495
496#if defined(BOTAN_TARGET_OS_HAS_POSIX1) && defined(BOTAN_TARGET_OS_HAS_POSIX_MLOCK)
497
498namespace {
499
500int get_locked_fd() {
501 #if defined(BOTAN_TARGET_OS_IS_IOS) || defined(BOTAN_TARGET_OS_IS_MACOS)
502 // On Darwin, tagging anonymous pages allows vmmap to track these.
503 // Allowed from 240 to 255 for userland applications
504 static constexpr int default_locked_fd = 255;
505 int locked_fd = default_locked_fd;
506
507 if(size_t locked_fdl = OS::read_env_variable_sz("BOTAN_LOCKED_FD", default_locked_fd)) {
508 if(locked_fdl < 240 || locked_fdl > 255) {
509 locked_fdl = default_locked_fd;
510 }
511 locked_fd = static_cast<int>(locked_fdl);
512 }
513 return VM_MAKE_TAG(locked_fd);
514 #else
515 return -1;
516 #endif
517}
518
519} // namespace
520
521#endif
522
523std::vector<void*> OS::allocate_locked_pages(size_t count) {
524 std::vector<void*> result;
525
526#if(defined(BOTAN_TARGET_OS_HAS_POSIX1) && defined(BOTAN_TARGET_OS_HAS_POSIX_MLOCK)) || \
527 defined(BOTAN_TARGET_OS_HAS_VIRTUAL_LOCK)
528
529 result.reserve(count);
530
531 const size_t page_size = OS::system_page_size();
532
533 #if defined(BOTAN_TARGET_OS_HAS_POSIX1) && defined(BOTAN_TARGET_OS_HAS_POSIX_MLOCK)
534 static const int locked_fd = get_locked_fd();
535 #endif
536
537 for(size_t i = 0; i != count; ++i) {
538 void* ptr = nullptr;
539
540 #if defined(BOTAN_TARGET_OS_HAS_POSIX1) && defined(BOTAN_TARGET_OS_HAS_POSIX_MLOCK)
541
542 int mmap_flags = MAP_PRIVATE;
543
544 #if defined(MAP_ANONYMOUS)
545 mmap_flags |= MAP_ANONYMOUS;
546 #elif defined(MAP_ANON)
547 mmap_flags |= MAP_ANON;
548 #endif
549
550 #if defined(MAP_CONCEAL)
551 mmap_flags |= MAP_CONCEAL;
552 #elif defined(MAP_NOCORE)
553 mmap_flags |= MAP_NOCORE;
554 #endif
555
556 int mmap_prot = PROT_READ | PROT_WRITE;
557
558 #if defined(PROT_MAX)
559 mmap_prot |= PROT_MAX(mmap_prot);
560 #endif
561
562 ptr = ::mmap(nullptr,
563 3 * page_size,
564 mmap_prot,
565 mmap_flags,
566 /*fd=*/locked_fd,
567 /*offset=*/0);
568
569 if(ptr == MAP_FAILED) {
570 continue;
571 }
572
573 // lock the data page
574 if(::mlock(static_cast<uint8_t*>(ptr) + page_size, page_size) != 0) {
575 ::munmap(ptr, 3 * page_size);
576 continue;
577 }
578
579 #if defined(MADV_DONTDUMP)
580 // we ignore errors here, as DONTDUMP is just a bonus
581 ::madvise(static_cast<uint8_t*>(ptr) + page_size, page_size, MADV_DONTDUMP);
582 #endif
583
584 #elif defined(BOTAN_TARGET_OS_HAS_VIRTUAL_LOCK)
585 ptr = ::VirtualAlloc(nullptr, 3 * page_size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
586
587 if(ptr == nullptr)
588 continue;
589
590 if(::VirtualLock(static_cast<uint8_t*>(ptr) + page_size, page_size) == 0) {
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 preceding 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#if defined(BOTAN_TARGET_OS_HAS_POSIX1)
616 const size_t page_size = OS::system_page_size();
617 ::mprotect(page, page_size, PROT_READ | PROT_WRITE);
618#elif defined(BOTAN_TARGET_OS_HAS_VIRTUAL_LOCK)
619 const size_t page_size = OS::system_page_size();
620 DWORD old_perms = 0;
621 ::VirtualProtect(page, page_size, PAGE_READWRITE, &old_perms);
622 BOTAN_UNUSED(old_perms);
623#else
624 BOTAN_UNUSED(page);
625#endif
626}
627
628void OS::page_prohibit_access(void* page) {
629#if defined(BOTAN_TARGET_OS_HAS_POSIX1)
630 const size_t page_size = OS::system_page_size();
631 ::mprotect(page, page_size, PROT_NONE);
632#elif defined(BOTAN_TARGET_OS_HAS_VIRTUAL_LOCK)
633 const size_t page_size = OS::system_page_size();
634 DWORD old_perms = 0;
635 ::VirtualProtect(page, page_size, PAGE_NOACCESS, &old_perms);
636 BOTAN_UNUSED(old_perms);
637#else
638 BOTAN_UNUSED(page);
639#endif
640}
641
642void OS::free_locked_pages(const std::vector<void*>& pages) {
643 const size_t page_size = OS::system_page_size();
644
645 for(void* ptr : pages) {
646 secure_scrub_memory(ptr, page_size);
647
648 // ptr points to the data page, guard pages are before and after
649 page_allow_access(static_cast<uint8_t*>(ptr) - page_size);
650 page_allow_access(static_cast<uint8_t*>(ptr) + page_size);
651
652#if defined(BOTAN_TARGET_OS_HAS_POSIX1) && defined(BOTAN_TARGET_OS_HAS_POSIX_MLOCK)
653 ::munlock(ptr, page_size);
654 ::munmap(static_cast<uint8_t*>(ptr) - page_size, 3 * page_size);
655#elif defined(BOTAN_TARGET_OS_HAS_VIRTUAL_LOCK)
656 ::VirtualUnlock(ptr, page_size);
657 ::VirtualFree(static_cast<uint8_t*>(ptr) - page_size, 0, MEM_RELEASE);
658#endif
659 }
660}
661
662void OS::page_named(void* page, size_t size) {
663#if defined(BOTAN_TARGET_OS_HAS_PRCTL) && defined(PR_SET_VMA) && defined(PR_SET_VMA_ANON_NAME)
664 static constexpr char name[] = "Botan mlock pool";
665 // NOLINTNEXTLINE(*-vararg)
666 int r = prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, reinterpret_cast<uintptr_t>(page), size, name);
667 BOTAN_UNUSED(r);
668#else
669 BOTAN_UNUSED(page, size);
670#endif
671}
672
673#if defined(BOTAN_TARGET_OS_HAS_THREADS)
674void OS::set_thread_name(std::thread& thread, const std::string& name) {
675 #if defined(BOTAN_TARGET_OS_IS_LINUX) || defined(BOTAN_TARGET_OS_IS_FREEBSD) || defined(BOTAN_TARGET_OS_IS_DRAGONFLY)
676 static_cast<void>(pthread_setname_np(thread.native_handle(), name.c_str()));
677 #elif defined(BOTAN_TARGET_OS_IS_OPENBSD)
678 static_cast<void>(pthread_set_name_np(thread.native_handle(), name.c_str()));
679 #elif defined(BOTAN_TARGET_OS_IS_NETBSD)
680 static_cast<void>(pthread_setname_np(thread.native_handle(), "%s", const_cast<char*>(name.c_str())));
681 #elif defined(BOTAN_TARGET_OS_HAS_WIN32) && defined(_LIBCPP_HAS_THREAD_API_PTHREAD)
682 static_cast<void>(pthread_setname_np(thread.native_handle(), name.c_str()));
683 #elif defined(BOTAN_TARGET_OS_HAS_WIN32) && defined(BOTAN_BUILD_COMPILER_IS_MSVC)
684 typedef HRESULT(WINAPI * std_proc)(HANDLE, PCWSTR);
685 HMODULE kern = GetModuleHandleA("KernelBase.dll");
686 std_proc set_thread_name = reinterpret_cast<std_proc>(GetProcAddress(kern, "SetThreadDescription"));
687 if(set_thread_name) {
688 std::wstring w;
689 auto sz = MultiByteToWideChar(CP_UTF8, 0, name.data(), -1, nullptr, 0);
690 if(sz > 0) {
691 w.resize(sz);
692 if(MultiByteToWideChar(CP_UTF8, 0, name.data(), -1, &w[0], sz) > 0) {
693 (void)set_thread_name(thread.native_handle(), w.c_str());
694 }
695 }
696 }
697 #elif defined(BOTAN_TARGET_OS_IF_HAIKU)
698 auto thread_id = get_pthread_thread_id(thread.native_handle());
699 static_cast<void>(rename_thread(thread_id, name.c_str()));
700 #else
701 // TODO other possible oses ?
702 // macOs does not seem to allow to name threads other than the current one.
703 BOTAN_UNUSED(thread, name);
704 #endif
705}
706#endif
707
708#if defined(BOTAN_TARGET_OS_HAS_POSIX1) && !defined(BOTAN_TARGET_OS_IS_EMSCRIPTEN)
709
710namespace {
711
712// NOLINTNEXTLINE(*-avoid-non-const-global-variables)
713::sigjmp_buf g_sigill_jmp_buf;
714
715void botan_sigill_handler(int /*unused*/) {
716 siglongjmp(g_sigill_jmp_buf, /*non-zero return value*/ 1);
717}
718
719} // namespace
720
721#endif
722
723int OS::run_cpu_instruction_probe(const std::function<int()>& probe_fn) {
724 volatile int probe_result = -3;
725
726#if defined(BOTAN_TARGET_OS_HAS_POSIX1) && !defined(BOTAN_TARGET_OS_IS_EMSCRIPTEN)
727 struct sigaction old_sigaction {};
728
729 struct sigaction sigaction {};
730
731 sigaction.sa_handler = botan_sigill_handler;
732 sigemptyset(&sigaction.sa_mask);
733 sigaction.sa_flags = 0;
734
735 int rc = ::sigaction(SIGILL, &sigaction, &old_sigaction);
736
737 if(rc != 0) {
738 throw System_Error("run_cpu_instruction_probe sigaction failed", errno);
739 }
740
741 rc = sigsetjmp(g_sigill_jmp_buf, /*save sigs*/ 1);
742
743 if(rc == 0) {
744 // first call to sigsetjmp
745 probe_result = probe_fn();
746 } else if(rc == 1) {
747 // non-local return from siglongjmp in signal handler: return error
748 probe_result = -1;
749 }
750
751 // Restore old SIGILL handler, if any
752 rc = ::sigaction(SIGILL, &old_sigaction, nullptr);
753 if(rc != 0) {
754 throw System_Error("run_cpu_instruction_probe sigaction restore failed", errno);
755 }
756
757#else
758 BOTAN_UNUSED(probe_fn);
759#endif
760
761 return probe_result;
762}
763
764std::unique_ptr<OS::Echo_Suppression> OS::suppress_echo_on_terminal() {
765#if defined(BOTAN_TARGET_OS_HAS_POSIX1)
766 class POSIX_Echo_Suppression : public Echo_Suppression {
767 public:
768 POSIX_Echo_Suppression() : m_stdin_fd(fileno(stdin)), m_old_termios{} {
769 if(::tcgetattr(m_stdin_fd, &m_old_termios) != 0) {
770 throw System_Error("Getting terminal status failed", errno);
771 }
772
773 struct termios noecho_flags = m_old_termios;
774 noecho_flags.c_lflag &= ~ECHO;
775 noecho_flags.c_lflag |= ECHONL;
776
777 if(::tcsetattr(m_stdin_fd, TCSANOW, &noecho_flags) != 0) {
778 throw System_Error("Clearing terminal echo bit failed", errno);
779 }
780 }
781
782 void reenable_echo() override {
783 if(m_stdin_fd > 0) {
784 if(::tcsetattr(m_stdin_fd, TCSANOW, &m_old_termios) != 0) {
785 throw System_Error("Restoring terminal echo bit failed", errno);
786 }
787 m_stdin_fd = -1;
788 }
789 }
790
791 ~POSIX_Echo_Suppression() override {
792 try {
793 reenable_echo();
794 } catch(...) {}
795 }
796
797 POSIX_Echo_Suppression(const POSIX_Echo_Suppression& other) = delete;
798 POSIX_Echo_Suppression(POSIX_Echo_Suppression&& other) = delete;
799 POSIX_Echo_Suppression& operator=(const POSIX_Echo_Suppression& other) = delete;
800 POSIX_Echo_Suppression& operator=(POSIX_Echo_Suppression&& other) = delete;
801
802 private:
803 int m_stdin_fd;
804 struct termios m_old_termios;
805 };
806
807 return std::make_unique<POSIX_Echo_Suppression>();
808
809#elif defined(BOTAN_TARGET_OS_HAS_WIN32)
810
811 class Win32_Echo_Suppression : public Echo_Suppression {
812 public:
813 Win32_Echo_Suppression() {
814 m_input_handle = ::GetStdHandle(STD_INPUT_HANDLE);
815 if(::GetConsoleMode(m_input_handle, &m_console_state) == 0)
816 throw System_Error("Getting console mode failed", ::GetLastError());
817
818 DWORD new_mode = ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT;
819 if(::SetConsoleMode(m_input_handle, new_mode) == 0)
820 throw System_Error("Setting console mode failed", ::GetLastError());
821 }
822
823 void reenable_echo() override {
824 if(m_input_handle != INVALID_HANDLE_VALUE) {
825 if(::SetConsoleMode(m_input_handle, m_console_state) == 0)
826 throw System_Error("Setting console mode failed", ::GetLastError());
827 m_input_handle = INVALID_HANDLE_VALUE;
828 }
829 }
830
831 ~Win32_Echo_Suppression() override {
832 try {
833 reenable_echo();
834 } catch(...) {}
835 }
836
837 Win32_Echo_Suppression(const Win32_Echo_Suppression& other) = delete;
838 Win32_Echo_Suppression(Win32_Echo_Suppression&& other) = delete;
839 Win32_Echo_Suppression& operator=(const Win32_Echo_Suppression& other) = delete;
840 Win32_Echo_Suppression& operator=(Win32_Echo_Suppression&& other) = delete;
841
842 private:
843 HANDLE m_input_handle;
844 DWORD m_console_state;
845 };
846
847 return std::make_unique<Win32_Echo_Suppression>();
848
849#else
850
851 // Not supported on this platform, return null
852 return nullptr;
853#endif
854}
855
856} // 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:764
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:614
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:628
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:723
std::vector< void * > allocate_locked_pages(size_t count)
Definition os_utils.cpp:523
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:642
void page_named(void *page, size_t size)
Definition os_utils.cpp:662
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