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