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