Botan 3.10.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 523 of file os_utils.cpp.

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

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

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

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

642 {
643 const size_t page_size = OS::system_page_size();
644
645 for(void* ptr : pages) {
646 secure_scrub_memory(ptr, page_size);
647
648 // ptr points to the data page, guard pages are before and after
649 page_allow_access(static_cast<uint8_t*>(ptr) - page_size);
650 page_allow_access(static_cast<uint8_t*>(ptr) + page_size);
651
652#if defined(BOTAN_TARGET_OS_HAS_POSIX1) && defined(BOTAN_TARGET_OS_HAS_POSIX_MLOCK)
653 ::munlock(ptr, page_size);
654 ::munmap(static_cast<uint8_t*>(ptr) - page_size, 3 * page_size);
655#elif defined(BOTAN_TARGET_OS_HAS_VIRTUAL_LOCK)
656 ::VirtualUnlock(ptr, page_size);
657 ::VirtualFree(static_cast<uint8_t*>(ptr) - page_size, 0, MEM_RELEASE);
658#endif
659 }
660}
void page_allow_access(void *page)
Definition os_utils.cpp:614
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 240 of file os_utils.cpp.

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

◆ 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 // NOLINTBEGIN(*-no-assembler)
180
181 #if defined(BOTAN_TARGET_ARCH_IS_X86_64)
182
183 uint32_t rtc_low = 0;
184 uint32_t rtc_high = 0;
185 asm volatile("rdtsc" : "=d"(rtc_high), "=a"(rtc_low));
186 rtc = (static_cast<uint64_t>(rtc_high) << 32) | rtc_low;
187
188 #elif defined(BOTAN_TARGET_ARCH_IS_X86_FAMILY) && defined(BOTAN_HAS_CPUID)
189
191 uint32_t rtc_low = 0;
192 uint32_t rtc_high = 0;
193 asm volatile("rdtsc" : "=d"(rtc_high), "=a"(rtc_low));
194 rtc = (static_cast<uint64_t>(rtc_high) << 32) | rtc_low;
195 }
196
197 #elif defined(BOTAN_TARGET_ARCH_IS_PPC64)
198
199 for(;;) {
200 uint32_t rtc_low = 0;
201 uint32_t rtc_high = 0;
202 uint32_t rtc_high2 = 0;
203 asm volatile("mftbu %0" : "=r"(rtc_high));
204 asm volatile("mftb %0" : "=r"(rtc_low));
205 asm volatile("mftbu %0" : "=r"(rtc_high2));
206
207 if(rtc_high == rtc_high2) {
208 rtc = (static_cast<uint64_t>(rtc_high) << 32) | rtc_low;
209 break;
210 }
211 }
212
213 #elif defined(BOTAN_TARGET_ARCH_IS_ALPHA)
214 asm volatile("rpcc %0" : "=r"(rtc));
215
216 #elif defined(BOTAN_TARGET_ARCH_IS_SPARC64) && !defined(BOTAN_TARGET_OS_IS_OPENBSD)
217 // OpenBSD does not trap access to the %tick register so we avoid it there
218 asm volatile("rd %%tick, %0" : "=r"(rtc));
219
220 #elif defined(BOTAN_TARGET_ARCH_IS_IA64)
221 asm volatile("mov %0=ar.itc" : "=r"(rtc));
222
223 #elif defined(BOTAN_TARGET_ARCH_IS_S390X)
224 asm volatile("stck 0(%0)" : : "a"(&rtc) : "memory", "cc");
225
226 #elif defined(BOTAN_TARGET_ARCH_IS_HPPA)
227 asm volatile("mfctl 16,%0" : "=r"(rtc)); // 64-bit only?
228
229 #else
230 //#warning "OS::get_cpu_cycle_counter not implemented"
231 #endif
232
233 // NOLINTEND(*-no-assembler)
234
235#endif
236
237 return rtc;
238}
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 272 of file os_utils.cpp.

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

Definition at line 388 of file os_utils.cpp.

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

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

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

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

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

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

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

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

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

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

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

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

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

628 {
629#if defined(BOTAN_TARGET_OS_HAS_POSIX1)
630 const size_t page_size = OS::system_page_size();
631 ::mprotect(page, page_size, PROT_NONE);
632#elif defined(BOTAN_TARGET_OS_HAS_VIRTUAL_LOCK)
633 const size_t page_size = OS::system_page_size();
634 DWORD old_perms = 0;
635 ::VirtualProtect(page, page_size, PAGE_NOACCESS, &old_perms);
636 BOTAN_UNUSED(old_perms);
637#else
638 BOTAN_UNUSED(page);
639#endif
640}

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

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

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

483 {
484 std::string value;
485 if(read_env_variable(value, name) && !value.empty()) {
486 try {
487 const size_t val = std::stoul(value, nullptr);
488 return val;
489 } catch(std::exception&) { /* ignore it */
490 }
491 }
492
493 return def;
494}
bool read_env_variable(std::string &value_out, std::string_view var_name)
Definition os_utils.cpp:449

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

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

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

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

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

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