Botan 3.9.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 518 of file os_utils.cpp.

518 {
519 std::vector<void*> result;
520
521#if(defined(BOTAN_TARGET_OS_HAS_POSIX1) && defined(BOTAN_TARGET_OS_HAS_POSIX_MLOCK)) || \
522 defined(BOTAN_TARGET_OS_HAS_VIRTUAL_LOCK)
523
524 result.reserve(count);
525
526 const size_t page_size = OS::system_page_size();
527
528 #if defined(BOTAN_TARGET_OS_HAS_POSIX1) && defined(BOTAN_TARGET_OS_HAS_POSIX_MLOCK)
529 static const int locked_fd = get_locked_fd();
530 #endif
531
532 for(size_t i = 0; i != count; ++i) {
533 void* ptr = nullptr;
534
535 #if defined(BOTAN_TARGET_OS_HAS_POSIX1) && defined(BOTAN_TARGET_OS_HAS_POSIX_MLOCK)
536
537 int mmap_flags = MAP_PRIVATE;
538
539 #if defined(MAP_ANONYMOUS)
540 mmap_flags |= MAP_ANONYMOUS;
541 #elif defined(MAP_ANON)
542 mmap_flags |= MAP_ANON;
543 #endif
544
545 #if defined(MAP_CONCEAL)
546 mmap_flags |= MAP_CONCEAL;
547 #elif defined(MAP_NOCORE)
548 mmap_flags |= MAP_NOCORE;
549 #endif
550
551 int mmap_prot = PROT_READ | PROT_WRITE;
552
553 #if defined(PROT_MAX)
554 mmap_prot |= PROT_MAX(mmap_prot);
555 #endif
556
557 ptr = ::mmap(nullptr,
558 3 * page_size,
559 mmap_prot,
560 mmap_flags,
561 /*fd=*/locked_fd,
562 /*offset=*/0);
563
564 if(ptr == MAP_FAILED) {
565 continue;
566 }
567
568 // lock the data page
569 if(::mlock(static_cast<uint8_t*>(ptr) + page_size, page_size) != 0) {
570 ::munmap(ptr, 3 * page_size);
571 continue;
572 }
573
574 #if defined(MADV_DONTDUMP)
575 // we ignore errors here, as DONTDUMP is just a bonus
576 ::madvise(static_cast<uint8_t*>(ptr) + page_size, page_size, MADV_DONTDUMP);
577 #endif
578
579 #elif defined(BOTAN_TARGET_OS_HAS_VIRTUAL_LOCK)
580 ptr = ::VirtualAlloc(nullptr, 3 * page_size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
581
582 if(ptr == nullptr)
583 continue;
584
585 if(::VirtualLock(static_cast<uint8_t*>(ptr) + page_size, page_size) == 0) {
586 ::VirtualFree(ptr, 0, MEM_RELEASE);
587 continue;
588 }
589 #endif
590
591 std::memset(ptr, 0, 3 * page_size); // zero data page and both guard pages
592
593 // Attempts to name the data page
594 page_named(ptr, 3 * page_size);
595 // Make guard page preceeding the data page
596 page_prohibit_access(static_cast<uint8_t*>(ptr));
597 // Make guard page following the data page
598 page_prohibit_access(static_cast<uint8_t*>(ptr) + 2 * page_size);
599
600 result.push_back(static_cast<uint8_t*>(ptr) + page_size);
601 }
602#else
603 BOTAN_UNUSED(count);
604#endif
605
606 return result;
607}
#define BOTAN_UNUSED
Definition assert.h:144
void page_prohibit_access(void *page)
Definition os_utils.cpp:623
size_t system_page_size()
Definition os_utils.cpp:364
void page_named(void *page, size_t size)
Definition os_utils.cpp:657

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 340 of file os_utils.cpp.

340 {
341 std::tm tm{};
342
343#if defined(BOTAN_TARGET_OS_HAS_WIN32)
344 if(::localtime_s(&tm, &time) != 0) {
345 throw Encoding_Error("Could not convert time_t to localtime");
346 }
347#elif defined(BOTAN_TARGET_OS_HAS_POSIX1)
348 if(::localtime_r(&time, &tm) == nullptr) {
349 throw Encoding_Error("Could not convert time_t to localtime");
350 }
351#else
352 if(auto tmp = std::localtime(&time)) {
353 tm = *tmp;
354 } else {
355 throw Encoding_Error("Could not convert time_t to localtime");
356 }
357#endif
358
359 std::ostringstream oss;
360 oss << std::put_time(&tm, format.c_str());
361 return oss.str();
362}

◆ 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 637 of file os_utils.cpp.

637 {
638 const size_t page_size = OS::system_page_size();
639
640 for(void* ptr : pages) {
641 secure_scrub_memory(ptr, page_size);
642
643 // ptr points to the data page, guard pages are before and after
644 page_allow_access(static_cast<uint8_t*>(ptr) - page_size);
645 page_allow_access(static_cast<uint8_t*>(ptr) + page_size);
646
647#if defined(BOTAN_TARGET_OS_HAS_POSIX1) && defined(BOTAN_TARGET_OS_HAS_POSIX_MLOCK)
648 ::munlock(ptr, page_size);
649 ::munmap(static_cast<uint8_t*>(ptr) - page_size, 3 * page_size);
650#elif defined(BOTAN_TARGET_OS_HAS_VIRTUAL_LOCK)
651 ::VirtualUnlock(ptr, page_size);
652 ::VirtualFree(static_cast<uint8_t*>(ptr) - page_size, 0, MEM_RELEASE);
653#endif
654 }
655}
void page_allow_access(void *page)
Definition os_utils.cpp:609
void secure_scrub_memory(void *ptr, size_t n)
Definition mem_utils.cpp:24

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 136 of file os_utils.cpp.

136 {
137 if(const auto hwcap = get_auxval(auxval_hwcap())) {
138 // If hwcap worked/was valid, we don't require hwcap2 to also
139 // succeed but instead will return zeros if it failed.
140 auto hwcap2 = get_auxval(auxval_hwcap2()).value_or(0);
141 return std::make_pair(*hwcap, hwcap2);
142 } else {
143 return {};
144 }
145}

◆ get_cpu_available()

size_t Botan::OS::get_cpu_available ( )

Definition at line 236 of file os_utils.cpp.

236 {
237#if defined(BOTAN_TARGET_OS_HAS_POSIX1)
238
239 #if defined(_SC_NPROCESSORS_ONLN)
240 const long cpu_online = ::sysconf(_SC_NPROCESSORS_ONLN);
241 if(cpu_online > 0) {
242 return static_cast<size_t>(cpu_online);
243 }
244 #endif
245
246 #if defined(_SC_NPROCESSORS_CONF)
247 const long cpu_conf = ::sysconf(_SC_NPROCESSORS_CONF);
248 if(cpu_conf > 0) {
249 return static_cast<size_t>(cpu_conf);
250 }
251 #endif
252
253#endif
254
255#if defined(BOTAN_TARGET_OS_HAS_THREADS)
256 // hardware_concurrency is allowed to return 0 if the value is not
257 // well defined or not computable.
258 const size_t hw_concur = std::thread::hardware_concurrency();
259
260 if(hw_concur > 0) {
261 return hw_concur;
262 }
263#endif
264
265 return 1;
266}

◆ 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 169 of file os_utils.cpp.

169 {
170 uint64_t rtc = 0;
171
172#if defined(BOTAN_TARGET_OS_HAS_WIN32)
173 LARGE_INTEGER tv;
174 ::QueryPerformanceCounter(&tv);
175 rtc = tv.QuadPart;
176
177#elif defined(BOTAN_USE_GCC_INLINE_ASM)
178
179 #if defined(BOTAN_TARGET_ARCH_IS_X86_64)
180
181 uint32_t rtc_low = 0;
182 uint32_t rtc_high = 0;
183 asm volatile("rdtsc" : "=d"(rtc_high), "=a"(rtc_low));
184 rtc = (static_cast<uint64_t>(rtc_high) << 32) | rtc_low;
185
186 #elif defined(BOTAN_TARGET_ARCH_IS_X86_FAMILY) && defined(BOTAN_HAS_CPUID)
187
189 uint32_t rtc_low = 0;
190 uint32_t 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;
199 uint32_t rtc_high = 0;
200 uint32_t rtc_high2 = 0;
201 asm volatile("mftbu %0" : "=r"(rtc_high));
202 asm volatile("mftb %0" : "=r"(rtc_low));
203 asm volatile("mftbu %0" : "=r"(rtc_high2));
204
205 if(rtc_high == rtc_high2) {
206 rtc = (static_cast<uint64_t>(rtc_high) << 32) | rtc_low;
207 break;
208 }
209 }
210
211 #elif defined(BOTAN_TARGET_ARCH_IS_ALPHA)
212 asm volatile("rpcc %0" : "=r"(rtc));
213
214 // OpenBSD does not trap access to the %tick register
215 #elif defined(BOTAN_TARGET_ARCH_IS_SPARC64) && !defined(BOTAN_TARGET_OS_IS_OPENBSD)
216 asm volatile("rd %%tick, %0" : "=r"(rtc));
217
218 #elif defined(BOTAN_TARGET_ARCH_IS_IA64)
219 asm volatile("mov %0=ar.itc" : "=r"(rtc));
220
221 #elif defined(BOTAN_TARGET_ARCH_IS_S390X)
222 asm volatile("stck 0(%0)" : : "a"(&rtc) : "memory", "cc");
223
224 #elif defined(BOTAN_TARGET_ARCH_IS_HPPA)
225 asm volatile("mfctl 16,%0" : "=r"(rtc)); // 64-bit only?
226
227 #else
228 //#warning "OS::get_cpu_cycle_counter not implemented"
229 #endif
230
231#endif
232
233 return rtc;
234}
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 268 of file os_utils.cpp.

268 {
269 if(uint64_t cpu_clock = OS::get_cpu_cycle_counter()) {
270 return cpu_clock;
271 }
272
273#if defined(BOTAN_TARGET_OS_IS_EMSCRIPTEN)
274 return emscripten_get_now();
275#endif
276
277 /*
278 If we got here either we either don't have an asm instruction
279 above, or (for x86) RDTSC is not available at runtime. Try some
280 clock_gettimes and return the first one that works, or otherwise
281 fall back to std::chrono.
282 */
283
284#if defined(BOTAN_TARGET_OS_HAS_CLOCK_GETTIME)
285
286 // The ordering here is somewhat arbitrary...
287 const clockid_t clock_types[] = {
288 #if defined(CLOCK_MONOTONIC_HR)
289 CLOCK_MONOTONIC_HR,
290 #endif
291 #if defined(CLOCK_MONOTONIC_RAW)
292 CLOCK_MONOTONIC_RAW,
293 #endif
294 #if defined(CLOCK_MONOTONIC)
295 CLOCK_MONOTONIC,
296 #endif
297 #if defined(CLOCK_PROCESS_CPUTIME_ID)
298 CLOCK_PROCESS_CPUTIME_ID,
299 #endif
300 #if defined(CLOCK_THREAD_CPUTIME_ID)
301 CLOCK_THREAD_CPUTIME_ID,
302 #endif
303 };
304
305 for(clockid_t clock : clock_types) {
306 struct timespec ts {};
307
308 if(::clock_gettime(clock, &ts) == 0) {
309 return (static_cast<uint64_t>(ts.tv_sec) * 1000000000) + static_cast<uint64_t>(ts.tv_nsec);
310 }
311 }
312#endif
313
314#if defined(BOTAN_TARGET_OS_HAS_SYSTEM_CLOCK)
315 // Plain C++11 fallback
316 auto now = std::chrono::high_resolution_clock::now().time_since_epoch();
317 return std::chrono::duration_cast<std::chrono::nanoseconds>(now).count();
318#else
319 return 0;
320#endif
321}
uint64_t BOTAN_TEST_API get_cpu_cycle_counter()
Definition os_utils.cpp:169

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 hyptothetically allocate for the memory poool. Reads environment variable "BOTAN_MLOCK_POOL_SIZE", set to "0" to disable pool.

Definition at line 384 of file os_utils.cpp.

384 {
385 /*
386 * Linux defaults to only 64 KiB of mlockable memory per process (too small)
387 * but BSDs offer a small fraction of total RAM (more than we need). Bound the
388 * total mlock size to 512 KiB which is enough to run the entire test suite
389 * without spilling to non-mlock memory (and thus presumably also enough for
390 * many useful programs), but small enough that we should not cause problems
391 * even if many processes are mlocking on the same machine.
392 */
393 const size_t max_locked_kb = 512;
394
395 /*
396 * If RLIMIT_MEMLOCK is not defined, likely the OS does not support
397 * unprivileged mlock calls.
398 */
399#if defined(RLIMIT_MEMLOCK) && defined(BOTAN_TARGET_OS_HAS_POSIX1) && defined(BOTAN_TARGET_OS_HAS_POSIX_MLOCK)
400 const size_t mlock_requested =
401 std::min<size_t>(read_env_variable_sz("BOTAN_MLOCK_POOL_SIZE", max_locked_kb), max_locked_kb);
402
403 if(mlock_requested > 0) {
404 struct ::rlimit limits {};
405
406 ::getrlimit(RLIMIT_MEMLOCK, &limits);
407
408 if(limits.rlim_cur < limits.rlim_max) {
409 limits.rlim_cur = limits.rlim_max;
410 ::setrlimit(RLIMIT_MEMLOCK, &limits);
411 ::getrlimit(RLIMIT_MEMLOCK, &limits);
412 }
413
414 return std::min<size_t>(limits.rlim_cur, mlock_requested * 1024);
415 }
416
417#elif defined(BOTAN_TARGET_OS_HAS_VIRTUAL_LOCK)
418 const size_t mlock_requested =
419 std::min<size_t>(read_env_variable_sz("BOTAN_MLOCK_POOL_SIZE", max_locked_kb), max_locked_kb);
420
421 SIZE_T working_min = 0, working_max = 0;
422 if(!::GetProcessWorkingSetSize(::GetCurrentProcess(), &working_min, &working_max)) {
423 return 0;
424 }
425
426 // According to Microsoft MSDN:
427 // 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
428 // In the book "Windows Internals Part 2": the maximum lockable pages are minimum working set size - 8 pages
429 // But the information in the book seems to be inaccurate/outdated
430 // I've tested this on Windows 8.1 x64, Windows 10 x64 and Windows 7 x86
431 // On all three OS the value is 11 instead of 8
432 const size_t overhead = OS::system_page_size() * 11;
433 if(working_min > overhead) {
434 const size_t lockable_bytes = working_min - overhead;
435 return std::min<size_t>(lockable_bytes, mlock_requested * 1024);
436 }
437#else
438 // Not supported on this platform
439 BOTAN_UNUSED(max_locked_kb);
440#endif
441
442 return 0;
443}
size_t read_env_variable_sz(std::string_view var_name, size_t def_value=0)
Definition os_utils.cpp:478

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 77 of file os_utils.cpp.

77 {
78#if defined(BOTAN_TARGET_OS_HAS_POSIX1)
79 return ::getpid();
80#elif defined(BOTAN_TARGET_OS_HAS_WIN32)
81 return ::GetCurrentProcessId();
82#elif defined(BOTAN_TARGET_OS_IS_LLVM) || defined(BOTAN_TARGET_OS_IS_NONE)
83 return 0; // truly no meaningful value
84#else
85 #error "Missing get_process_id"
86#endif
87}

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 323 of file os_utils.cpp.

323 {
324#if defined(BOTAN_TARGET_OS_HAS_CLOCK_GETTIME)
325 struct timespec ts {};
326
327 if(::clock_gettime(CLOCK_REALTIME, &ts) == 0) {
328 return (static_cast<uint64_t>(ts.tv_sec) * 1000000000) + static_cast<uint64_t>(ts.tv_nsec);
329 }
330#endif
331
332#if defined(BOTAN_TARGET_OS_HAS_SYSTEM_CLOCK)
333 auto now = std::chrono::system_clock::now().time_since_epoch();
334 return std::chrono::duration_cast<std::chrono::nanoseconds>(now).count();
335#else
336 throw Not_Implemented("OS::get_system_timestamp_ns this system does not support a clock");
337#endif
338}

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 355 of file socket.cpp.

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

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 319 of file socket_udp.cpp.

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

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 334 of file socket_udp.cpp.

334 {
335 const auto uri = URI::from_any(uri_string);
336 if(uri.port() == 0) {
337 throw Invalid_Argument("UDP port not specified");
338 }
339 return open_socket_udp(uri.host(), std::to_string(uri.port()), timeout);
340}
static URI from_any(std::string_view uri)
Definition uri.cpp:180
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 609 of file os_utils.cpp.

609 {
610#if defined(BOTAN_TARGET_OS_HAS_POSIX1)
611 const size_t page_size = OS::system_page_size();
612 ::mprotect(page, page_size, PROT_READ | PROT_WRITE);
613#elif defined(BOTAN_TARGET_OS_HAS_VIRTUAL_LOCK)
614 const size_t page_size = OS::system_page_size();
615 DWORD old_perms = 0;
616 ::VirtualProtect(page, page_size, PAGE_READWRITE, &old_perms);
617 BOTAN_UNUSED(old_perms);
618#else
619 BOTAN_UNUSED(page);
620#endif
621}

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 657 of file os_utils.cpp.

657 {
658#if defined(BOTAN_TARGET_OS_HAS_PRCTL) && defined(PR_SET_VMA) && defined(PR_SET_VMA_ANON_NAME)
659 static constexpr char name[] = "Botan mlock pool";
660 // NOLINTNEXTLINE(*-vararg)
661 int r = prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, reinterpret_cast<uintptr_t>(page), size, name);
662 BOTAN_UNUSED(r);
663#else
664 BOTAN_UNUSED(page, size);
665#endif
666}

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 623 of file os_utils.cpp.

623 {
624#if defined(BOTAN_TARGET_OS_HAS_POSIX1)
625 const size_t page_size = OS::system_page_size();
626 ::mprotect(page, page_size, PROT_NONE);
627#elif defined(BOTAN_TARGET_OS_HAS_VIRTUAL_LOCK)
628 const size_t page_size = OS::system_page_size();
629 DWORD old_perms = 0;
630 ::VirtualProtect(page, page_size, PAGE_NOACCESS, &old_perms);
631 BOTAN_UNUSED(old_perms);
632#else
633 BOTAN_UNUSED(page);
634#endif
635}

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 445 of file os_utils.cpp.

445 {
446 value_out = "";
447
448 if(running_in_privileged_state()) {
449 return false;
450 }
451
452#if defined(BOTAN_TARGET_OS_HAS_WIN32) && defined(BOTAN_BUILD_COMPILER_IS_MSVC)
453 const std::string name(name_view);
454 char val[128] = {0};
455 size_t req_size = 0;
456 if(getenv_s(&req_size, val, sizeof(val), name.c_str()) == 0) {
457 // Microsoft's implementation always writes a terminating \0,
458 // and includes it in the reported length of the environment variable
459 // if a value exists.
460 if(req_size > 0 && val[req_size - 1] == '\0') {
461 value_out = std::string(val);
462 } else {
463 value_out = std::string(val, req_size);
464 }
465 return true;
466 }
467#else
468 const std::string name(name_view);
469 if(const char* val = std::getenv(name.c_str())) {
470 value_out = val;
471 return true;
472 }
473#endif
474
475 return false;
476}

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, similiar to glibc's secure_getenv.

Definition at line 478 of file os_utils.cpp.

478 {
479 std::string value;
480 if(read_env_variable(value, name) && !value.empty()) {
481 try {
482 const size_t val = std::stoul(value, nullptr);
483 return val;
484 } catch(std::exception&) { /* ignore it */
485 }
486 }
487
488 return def;
489}
bool read_env_variable(std::string &value_out, std::string_view var_name)
Definition os_utils.cpp:445

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 718 of file os_utils.cpp.

718 {
719 volatile int probe_result = -3;
720
721#if defined(BOTAN_TARGET_OS_HAS_POSIX1) && !defined(BOTAN_TARGET_OS_IS_EMSCRIPTEN)
722 struct sigaction old_sigaction {};
723
724 struct sigaction sigaction {};
725
726 sigaction.sa_handler = botan_sigill_handler;
727 sigemptyset(&sigaction.sa_mask);
728 sigaction.sa_flags = 0;
729
730 int rc = ::sigaction(SIGILL, &sigaction, &old_sigaction);
731
732 if(rc != 0) {
733 throw System_Error("run_cpu_instruction_probe sigaction failed", errno);
734 }
735
736 rc = sigsetjmp(g_sigill_jmp_buf, /*save sigs*/ 1);
737
738 if(rc == 0) {
739 // first call to sigsetjmp
740 probe_result = probe_fn();
741 } else if(rc == 1) {
742 // non-local return from siglongjmp in signal handler: return error
743 probe_result = -1;
744 }
745
746 // Restore old SIGILL handler, if any
747 rc = ::sigaction(SIGILL, &old_sigaction, nullptr);
748 if(rc != 0) {
749 throw System_Error("run_cpu_instruction_probe sigaction restore failed", errno);
750 }
751
752#else
753 BOTAN_UNUSED(probe_fn);
754#endif
755
756 return probe_result;
757}

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 759 of file os_utils.cpp.

759 {
760#if defined(BOTAN_TARGET_OS_HAS_POSIX1)
761 class POSIX_Echo_Suppression : public Echo_Suppression {
762 public:
763 POSIX_Echo_Suppression() : m_stdin_fd(fileno(stdin)), m_old_termios{} {
764 if(::tcgetattr(m_stdin_fd, &m_old_termios) != 0) {
765 throw System_Error("Getting terminal status failed", errno);
766 }
767
768 struct termios noecho_flags = m_old_termios;
769 noecho_flags.c_lflag &= ~ECHO;
770 noecho_flags.c_lflag |= ECHONL;
771
772 if(::tcsetattr(m_stdin_fd, TCSANOW, &noecho_flags) != 0) {
773 throw System_Error("Clearing terminal echo bit failed", errno);
774 }
775 }
776
777 void reenable_echo() override {
778 if(m_stdin_fd > 0) {
779 if(::tcsetattr(m_stdin_fd, TCSANOW, &m_old_termios) != 0) {
780 throw System_Error("Restoring terminal echo bit failed", errno);
781 }
782 m_stdin_fd = -1;
783 }
784 }
785
786 ~POSIX_Echo_Suppression() override {
787 try {
789 } catch(...) {}
790 }
791
792 POSIX_Echo_Suppression(const POSIX_Echo_Suppression& other) = delete;
793 POSIX_Echo_Suppression(POSIX_Echo_Suppression&& other) = delete;
794 POSIX_Echo_Suppression& operator=(const POSIX_Echo_Suppression& other) = delete;
795 POSIX_Echo_Suppression& operator=(POSIX_Echo_Suppression&& other) = delete;
796
797 private:
798 int m_stdin_fd;
799 struct termios m_old_termios;
800 };
801
802 return std::make_unique<POSIX_Echo_Suppression>();
803
804#elif defined(BOTAN_TARGET_OS_HAS_WIN32)
805
806 class Win32_Echo_Suppression : public Echo_Suppression {
807 public:
808 Win32_Echo_Suppression() {
809 m_input_handle = ::GetStdHandle(STD_INPUT_HANDLE);
810 if(::GetConsoleMode(m_input_handle, &m_console_state) == 0)
811 throw System_Error("Getting console mode failed", ::GetLastError());
812
813 DWORD new_mode = ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT;
814 if(::SetConsoleMode(m_input_handle, new_mode) == 0)
815 throw System_Error("Setting console mode failed", ::GetLastError());
816 }
817
818 void reenable_echo() override {
819 if(m_input_handle != INVALID_HANDLE_VALUE) {
820 if(::SetConsoleMode(m_input_handle, m_console_state) == 0)
821 throw System_Error("Setting console mode failed", ::GetLastError());
822 m_input_handle = INVALID_HANDLE_VALUE;
823 }
824 }
825
826 ~Win32_Echo_Suppression() override {
827 try {
828 reenable_echo();
829 } catch(...) {}
830 }
831
832 Win32_Echo_Suppression(const Win32_Echo_Suppression& other) = delete;
833 Win32_Echo_Suppression(Win32_Echo_Suppression&& other) = delete;
834 Win32_Echo_Suppression& operator=(const Win32_Echo_Suppression& other) = delete;
835 Win32_Echo_Suppression& operator=(Win32_Echo_Suppression&& other) = delete;
836
837 private:
838 HANDLE m_input_handle;
839 DWORD m_console_state;
840 };
841
842 return std::make_unique<Win32_Echo_Suppression>();
843
844#else
845
846 // Not supported on this platform, return null
847 return nullptr;
848#endif
849}
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 364 of file os_utils.cpp.

364 {
365 const size_t default_page_size = 4096;
366
367#if defined(BOTAN_TARGET_OS_HAS_POSIX1)
368 long p = ::sysconf(_SC_PAGESIZE);
369 if(p > 1) {
370 return static_cast<size_t>(p);
371 } else {
372 return default_page_size;
373 }
374#elif defined(BOTAN_TARGET_OS_HAS_VIRTUAL_LOCK)
375 BOTAN_UNUSED(default_page_size);
376 SYSTEM_INFO sys_info;
377 ::GetSystemInfo(&sys_info);
378 return sys_info.dwPageSize;
379#else
380 return default_page_size;
381#endif
382}

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().