Botan 3.4.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(_LIBCPP_HAS_THREAD_API_PTHREAD)
642 static_cast<void>(pthread_setname_np(thread.native_handle(), name.c_str()));
643 #elif defined(BOTAN_TARGET_OS_HAS_WIN32) && defined(BOTAN_BUILD_COMPILER_IS_MSVC)
644 typedef HRESULT(WINAPI * std_proc)(HANDLE, PCWSTR);
645 HMODULE kern = GetModuleHandleA("KernelBase.dll");
646 std_proc set_thread_name = reinterpret_cast<std_proc>(GetProcAddress(kern, "SetThreadDescription"));
647 if(set_thread_name) {
648 std::wstring w;
649 auto sz = MultiByteToWideChar(CP_UTF8, 0, name.data(), -1, nullptr, 0);
650 if(sz > 0) {
651 w.resize(sz);
652 if(MultiByteToWideChar(CP_UTF8, 0, name.data(), -1, &w[0], sz) > 0) {
653 (void)set_thread_name(thread.native_handle(), w.c_str());
654 }
655 }
656 }
657 #elif defined(BOTAN_TARGET_OS_IF_HAIKU)
658 auto thread_id = get_pthread_thread_id(thread.native_handle());
659 static_cast<void>(rename_thread(thread_id, name.c_str()));
660 #else
661 // TODO other possible oses ?
662 // macOs does not seem to allow to name threads other than the current one.
663 BOTAN_UNUSED(thread, name);
664 #endif
665}
666#endif
667
668#if defined(BOTAN_TARGET_OS_HAS_POSIX1) && !defined(BOTAN_TARGET_OS_IS_EMSCRIPTEN)
669
670namespace {
671
672// NOLINTNEXTLINE(*-avoid-non-const-global-variables)
673::sigjmp_buf g_sigill_jmp_buf;
674
675void botan_sigill_handler(int /*unused*/) {
676 siglongjmp(g_sigill_jmp_buf, /*non-zero return value*/ 1);
677}
678
679} // namespace
680
681#endif
682
683int OS::run_cpu_instruction_probe(const std::function<int()>& probe_fn) {
684 volatile int probe_result = -3;
685
686#if defined(BOTAN_TARGET_OS_HAS_POSIX1) && !defined(BOTAN_TARGET_OS_IS_EMSCRIPTEN)
687 struct sigaction old_sigaction;
688 struct sigaction sigaction;
689
690 sigaction.sa_handler = botan_sigill_handler;
691 sigemptyset(&sigaction.sa_mask);
692 sigaction.sa_flags = 0;
693
694 int rc = ::sigaction(SIGILL, &sigaction, &old_sigaction);
695
696 if(rc != 0) {
697 throw System_Error("run_cpu_instruction_probe sigaction failed", errno);
698 }
699
700 rc = sigsetjmp(g_sigill_jmp_buf, /*save sigs*/ 1);
701
702 if(rc == 0) {
703 // first call to sigsetjmp
704 probe_result = probe_fn();
705 } else if(rc == 1) {
706 // non-local return from siglongjmp in signal handler: return error
707 probe_result = -1;
708 }
709
710 // Restore old SIGILL handler, if any
711 rc = ::sigaction(SIGILL, &old_sigaction, nullptr);
712 if(rc != 0) {
713 throw System_Error("run_cpu_instruction_probe sigaction restore failed", errno);
714 }
715
716#else
717 BOTAN_UNUSED(probe_fn);
718#endif
719
720 return probe_result;
721}
722
723std::unique_ptr<OS::Echo_Suppression> OS::suppress_echo_on_terminal() {
724#if defined(BOTAN_TARGET_OS_HAS_POSIX1)
725 class POSIX_Echo_Suppression : public Echo_Suppression {
726 public:
727 POSIX_Echo_Suppression() {
728 m_stdin_fd = fileno(stdin);
729 if(::tcgetattr(m_stdin_fd, &m_old_termios) != 0) {
730 throw System_Error("Getting terminal status failed", errno);
731 }
732
733 struct termios noecho_flags = m_old_termios;
734 noecho_flags.c_lflag &= ~ECHO;
735 noecho_flags.c_lflag |= ECHONL;
736
737 if(::tcsetattr(m_stdin_fd, TCSANOW, &noecho_flags) != 0) {
738 throw System_Error("Clearing terminal echo bit failed", errno);
739 }
740 }
741
742 void reenable_echo() override {
743 if(m_stdin_fd > 0) {
744 if(::tcsetattr(m_stdin_fd, TCSANOW, &m_old_termios) != 0) {
745 throw System_Error("Restoring terminal echo bit failed", errno);
746 }
747 m_stdin_fd = -1;
748 }
749 }
750
751 ~POSIX_Echo_Suppression() override {
752 try {
753 reenable_echo();
754 } catch(...) {}
755 }
756
757 POSIX_Echo_Suppression(const POSIX_Echo_Suppression& other) = delete;
758 POSIX_Echo_Suppression(POSIX_Echo_Suppression&& other) = delete;
759 POSIX_Echo_Suppression& operator=(const POSIX_Echo_Suppression& other) = delete;
760 POSIX_Echo_Suppression& operator=(POSIX_Echo_Suppression&& other) = delete;
761
762 private:
763 int m_stdin_fd;
764 struct termios m_old_termios;
765 };
766
767 return std::make_unique<POSIX_Echo_Suppression>();
768
769#elif defined(BOTAN_TARGET_OS_HAS_WIN32)
770
771 class Win32_Echo_Suppression : public Echo_Suppression {
772 public:
773 Win32_Echo_Suppression() {
774 m_input_handle = ::GetStdHandle(STD_INPUT_HANDLE);
775 if(::GetConsoleMode(m_input_handle, &m_console_state) == 0)
776 throw System_Error("Getting console mode failed", ::GetLastError());
777
778 DWORD new_mode = ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT;
779 if(::SetConsoleMode(m_input_handle, new_mode) == 0)
780 throw System_Error("Setting console mode failed", ::GetLastError());
781 }
782
783 void reenable_echo() override {
784 if(m_input_handle != INVALID_HANDLE_VALUE) {
785 if(::SetConsoleMode(m_input_handle, m_console_state) == 0)
786 throw System_Error("Setting console mode failed", ::GetLastError());
787 m_input_handle = INVALID_HANDLE_VALUE;
788 }
789 }
790
791 ~Win32_Echo_Suppression() override {
792 try {
793 reenable_echo();
794 } catch(...) {}
795 }
796
797 Win32_Echo_Suppression(const Win32_Echo_Suppression& other) = delete;
798 Win32_Echo_Suppression(Win32_Echo_Suppression&& other) = delete;
799 Win32_Echo_Suppression& operator=(const Win32_Echo_Suppression& other) = delete;
800 Win32_Echo_Suppression& operator=(Win32_Echo_Suppression&& other) = delete;
801
802 private:
803 HANDLE m_input_handle;
804 DWORD m_console_state;
805 };
806
807 return std::make_unique<Win32_Echo_Suppression>();
808
809#else
810
811 // Not supported on this platform, return null
812 return nullptr;
813#endif
814}
815
816} // 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:723
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:683
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