Botan 3.11.0
Crypto and TLS for C&
Botan::OS Namespace Reference

Classes

class  Echo_Suppression
class  Socket
class  SocketUDP

Functions

std::vector< void * > allocate_locked_pages (size_t count)
std::string BOTAN_TEST_API format_time (time_t time, const std::string &format)
void free_locked_pages (const std::vector< void * > &pages)
std::optional< std::pair< unsigned long, unsigned long > > get_auxval_hwcap ()
size_t BOTAN_TEST_API get_cpu_available ()
uint64_t BOTAN_TEST_API get_cpu_cycle_counter ()
uint64_t BOTAN_TEST_API get_high_resolution_clock ()
size_t get_memory_locking_limit ()
uint32_t BOTAN_TEST_API get_process_id ()
uint64_t BOTAN_TEST_API get_system_timestamp_ns ()
std::unique_ptr< Socket > BOTAN_TEST_API open_socket (std::string_view hostname, std::string_view service, std::chrono::milliseconds timeout)
std::unique_ptr< SocketUDP > BOTAN_TEST_API open_socket_udp (std::string_view hostname, std::string_view service, std::chrono::microseconds timeout)
std::unique_ptr< SocketUDP > BOTAN_TEST_API open_socket_udp (std::string_view uri, std::chrono::microseconds timeout)
void page_allow_access (void *page)
void page_named (void *page, size_t size)
void page_prohibit_access (void *page)
bool read_env_variable (std::string &value_out, std::string_view var_name)
size_t read_env_variable_sz (std::string_view var_name, size_t def_value=0)
int BOTAN_TEST_API run_cpu_instruction_probe (const std::function< int()> &probe_fn)
std::unique_ptr< Echo_Suppression > BOTAN_UNSTABLE_API suppress_echo_on_terminal ()
size_t system_page_size ()

Function Documentation

◆ allocate_locked_pages()

std::vector< void * > Botan::OS::allocate_locked_pages ( size_t count)

Request count pages of RAM which are locked into memory using mlock, VirtualLock, or some similar OS specific API. Free it with free_locked_pages.

Returns an empty list on failure. This function is allowed to return fewer than count pages.

The contents of the allocated pages are undefined.

Each page is preceded by and followed by a page which is marked as noaccess, such that accessing it will cause a crash. This turns out of bound reads/writes into crash events.

Parameters
countrequested number of locked pages

Definition at line 522 of file os_utils.cpp.

522 {
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}
#define BOTAN_UNUSED
Definition assert.h:144
void page_prohibit_access(void *page)
Definition os_utils.cpp:627
size_t system_page_size()
Definition os_utils.cpp:367
void page_named(void *page, size_t size)
Definition os_utils.cpp:661

References BOTAN_UNUSED, page_named(), page_prohibit_access(), and system_page_size().

Referenced by Botan::mlock_allocator::mlock_allocator().

◆ format_time()

std::string Botan::OS::format_time ( time_t time,
const std::string & format )

Format a time

Converts the time_t to a local time representation, then invokes std::put_time with the specified format.

Definition at line 343 of file os_utils.cpp.

343 {
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}

◆ free_locked_pages()

void Botan::OS::free_locked_pages ( const std::vector< void * > & pages)

Free memory allocated by allocate_locked_pages

Parameters
pagesa list of pages returned by allocate_locked_pages

Definition at line 641 of file os_utils.cpp.

641 {
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}
void page_allow_access(void *page)
Definition os_utils.cpp:613
void secure_scrub_memory(void *ptr, size_t n)
Definition mem_utils.cpp:25

References page_allow_access(), Botan::secure_scrub_memory(), and system_page_size().

Referenced by Botan::mlock_allocator::~mlock_allocator().

◆ get_auxval_hwcap()

std::optional< std::pair< unsigned long, unsigned long > > Botan::OS::get_auxval_hwcap ( )

If this system supports getauxval (or an equivalent interface, like FreeBSD's elf_aux_info) queries AT_HWCAP and AT_HWCAP2 and returns both.

Otherwise returns nullopt.

Definition at line 135 of file os_utils.cpp.

135 {
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}

◆ get_cpu_available()

size_t Botan::OS::get_cpu_available ( )

Definition at line 239 of file os_utils.cpp.

239 {
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}

◆ get_cpu_cycle_counter()

uint64_t Botan::OS::get_cpu_cycle_counter ( )
Returns
CPU processor clock, if available

On Windows, calls QueryPerformanceCounter.

Under GCC or Clang on supported platforms the hardware cycle counter is queried. Currently supported processors are x86, PPC, Alpha, SPARC, IA-64, S/390x, and HP-PA. If no CPU cycle counter is available on this system, returns zero.

Definition at line 168 of file os_utils.cpp.

168 {
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}
static bool has(CPUID::Feature feat)
Definition cpuid.h:94

References Botan::CPUID::has(), and Botan::CPUFeature::RDTSC.

Referenced by get_high_resolution_clock().

◆ get_high_resolution_clock()

uint64_t Botan::OS::get_high_resolution_clock ( )

Definition at line 271 of file os_utils.cpp.

271 {
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}
uint64_t BOTAN_TEST_API get_cpu_cycle_counter()
Definition os_utils.cpp:168

References get_cpu_cycle_counter().

Referenced by Botan::RandomNumberGenerator::randomize_with_ts_input().

◆ get_memory_locking_limit()

size_t Botan::OS::get_memory_locking_limit ( )
Returns
maximum amount of memory (in bytes) Botan could/should hypothetically allocate for the memory poool. Reads environment variable "BOTAN_MLOCK_POOL_SIZE", set to "0" to disable pool.

Definition at line 387 of file os_utils.cpp.

387 {
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}
size_t read_env_variable_sz(std::string_view var_name, size_t def_value=0)
Definition os_utils.cpp:482

References BOTAN_UNUSED, read_env_variable_sz(), and system_page_size().

Referenced by Botan::mlock_allocator::mlock_allocator().

◆ get_process_id()

uint32_t Botan::OS::get_process_id ( )
Returns
process ID assigned by the operating system.

On Unix and Windows systems, this always returns a result

On systems where there is no processes to speak of (for example on baremetal systems or within a unikernel), this function returns zero.

Definition at line 76 of file os_utils.cpp.

76 {
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}

Referenced by Botan::RandomNumberGenerator::randomize_with_ts_input(), and Botan::Stateful_RNG::reseed_check().

◆ get_system_timestamp_ns()

uint64_t Botan::OS::get_system_timestamp_ns ( )
Returns
system clock (reflecting wall clock) with best resolution available, normalized to nanoseconds resolution, using Unix epoch.

If the system does not have a real time clock this function will throw Not_Implemented

Definition at line 326 of file os_utils.cpp.

326 {
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}

Referenced by Botan::measure_cost().

◆ open_socket()

std::unique_ptr< OS::Socket > Botan::OS::open_socket ( std::string_view hostname,
std::string_view service,
std::chrono::milliseconds timeout )

Open up a socket. Will throw on error. Returns null if sockets are not available on this platform.

Definition at line 359 of file socket.cpp.

361 {
362#if defined(BOTAN_HAS_BOOST_ASIO)
363 return std::make_unique<Asio_Socket>(hostname, service, timeout);
364
365#elif defined(BOTAN_TARGET_OS_HAS_SOCKETS) || defined(BOTAN_TARGET_OS_HAS_WINSOCK2)
366 return std::make_unique<BSD_Socket>(hostname, service, timeout);
367
368#else
369 BOTAN_UNUSED(hostname, service, timeout);
370 // No sockets for you
371 return std::unique_ptr<Socket>();
372#endif
373}

References BOTAN_UNUSED.

◆ open_socket_udp() [1/2]

std::unique_ptr< OS::SocketUDP > Botan::OS::open_socket_udp ( std::string_view hostname,
std::string_view service,
std::chrono::microseconds timeout )

Open up a socket. Will throw on error. Returns null if sockets are not available on this platform.

Definition at line 323 of file socket_udp.cpp.

325 {
326#if defined(BOTAN_HAS_BOOST_ASIO)
327 return std::make_unique<Asio_SocketUDP>(hostname, service, timeout);
328#elif defined(BOTAN_TARGET_OS_HAS_SOCKETS) || defined(BOTAN_TARGET_OS_HAS_WINSOCK2)
329 return std::make_unique<BSD_SocketUDP>(hostname, service, timeout);
330#else
331 BOTAN_UNUSED(hostname);
332 BOTAN_UNUSED(service);
333 BOTAN_UNUSED(timeout);
334 return std::unique_ptr<OS::SocketUDP>();
335#endif
336}

References BOTAN_UNUSED.

Referenced by Botan::Roughtime::online_request(), and open_socket_udp().

◆ open_socket_udp() [2/2]

std::unique_ptr< OS::SocketUDP > Botan::OS::open_socket_udp ( std::string_view uri,
std::chrono::microseconds timeout )

Open up a socket. Will throw on error. Returns null if sockets are not available on this platform.

Definition at line 338 of file socket_udp.cpp.

338 {
339 const auto uri = URI::from_any(uri_string);
340 if(uri.port() == 0) {
341 throw Invalid_Argument("UDP port not specified");
342 }
343 return open_socket_udp(uri.host(), std::to_string(uri.port()), timeout);
344}
static URI from_any(std::string_view uri)
Definition uri.cpp:175
std::unique_ptr< SocketUDP > BOTAN_TEST_API open_socket_udp(std::string_view hostname, std::string_view service, std::chrono::microseconds timeout)

References Botan::URI::from_any(), and open_socket_udp().

◆ page_allow_access()

void Botan::OS::page_allow_access ( void * page)

Set the MMU to allow R/W access to this page

Definition at line 613 of file os_utils.cpp.

613 {
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}

References BOTAN_UNUSED, and system_page_size().

Referenced by Botan::Memory_Pool::allocate(), free_locked_pages(), Botan::Sodium::sodium_mprotect_readwrite(), and Botan::Memory_Pool::~Memory_Pool().

◆ page_named()

void Botan::OS::page_named ( void * page,
size_t size )

Set a ID to a page's range expressed by size bytes

Definition at line 661 of file os_utils.cpp.

661 {
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}

References BOTAN_UNUSED.

Referenced by allocate_locked_pages().

◆ page_prohibit_access()

void Botan::OS::page_prohibit_access ( void * page)

Set the MMU to prohibit access to this page

Definition at line 627 of file os_utils.cpp.

627 {
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}

References BOTAN_UNUSED, and system_page_size().

Referenced by allocate_locked_pages(), Botan::Memory_Pool::deallocate(), Botan::Memory_Pool::Memory_Pool(), and Botan::Sodium::sodium_mprotect_noaccess().

◆ read_env_variable()

bool Botan::OS::read_env_variable ( std::string & value_out,
std::string_view var_name )

Read the value of an environment variable, setting it to value_out if it exists. Returns false and sets value_out to empty string if no such variable is set. If the process seems to be running in a privileged state (such as setuid) then always returns false and does not examine the environment.

Definition at line 448 of file os_utils.cpp.

448 {
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}

Referenced by Botan_FFI::ffi_error_exception_thrown(), and read_env_variable_sz().

◆ read_env_variable_sz()

size_t Botan::OS::read_env_variable_sz ( std::string_view var_name,
size_t def_value = 0 )

Read the value of an environment variable and convert it to an integer. If not set or conversion fails, returns the default value.

If the process seems to be running in a privileged state (such as setuid) then always returns nullptr, similar to glibc's secure_getenv.

Definition at line 482 of file os_utils.cpp.

482 {
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}
bool read_env_variable(std::string &value_out, std::string_view var_name)
Definition os_utils.cpp:448

References read_env_variable().

Referenced by get_memory_locking_limit().

◆ run_cpu_instruction_probe()

int Botan::OS::run_cpu_instruction_probe ( const std::function< int()> & probe_fn)

Run a probe instruction to test for support for a CPU instruction. Runs in system-specific env that catches illegal instructions; this function always fails if the OS doesn't provide this. Returns value of probe_fn, if it could run. If error occurs, returns negative number. This allows probe_fn to indicate errors of its own, if it wants. For example the instruction might not only be only available on some CPUs, but also buggy on some subset of these - the probe function can test to make sure the instruction works properly before indicating that the instruction is available.

Warning
on Unix systems uses signal handling in a way that is not thread safe. It should only be called in a single-threaded context (ie, at static init time).

If probe_fn throws an exception the result is undefined.

Return codes: -1 illegal instruction detected

Definition at line 722 of file os_utils.cpp.

722 {
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}

References BOTAN_UNUSED.

◆ suppress_echo_on_terminal()

std::unique_ptr< OS::Echo_Suppression > Botan::OS::suppress_echo_on_terminal ( )

Suppress echo on the terminal Returns null if this operation is not supported on the current system.

Definition at line 763 of file os_utils.cpp.

763 {
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 {
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}
virtual void reenable_echo()=0

◆ system_page_size()

size_t Botan::OS::system_page_size ( )

Return the size of a memory page, if that can be derived on the current system. Otherwise returns some default value (eg 4096)

Definition at line 367 of file os_utils.cpp.

367 {
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}

References BOTAN_UNUSED.

Referenced by allocate_locked_pages(), free_locked_pages(), get_memory_locking_limit(), Botan::mlock_allocator::mlock_allocator(), page_allow_access(), and page_prohibit_access().