Botan 3.8.1
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 508 of file os_utils.cpp.

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

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

334 {
335 std::tm tm;
336
337#if defined(BOTAN_TARGET_OS_HAS_WIN32)
338 localtime_s(&tm, &time);
339#elif defined(BOTAN_TARGET_OS_HAS_POSIX1)
340 localtime_r(&time, &tm);
341#else
342 if(auto tmp = std::localtime(&time)) {
343 tm = *tmp;
344 } else {
345 throw Encoding_Error("Could not convert time_t to localtime");
346 }
347#endif
348
349 std::ostringstream oss;
350 oss << std::put_time(&tm, format.c_str());
351 return oss.str();
352}

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

627 {
628 const size_t page_size = OS::system_page_size();
629
630 for(size_t i = 0; i != pages.size(); ++i) {
631 void* ptr = pages[i];
632
633 secure_scrub_memory(ptr, page_size);
634
635 // ptr points to the data page, guard pages are before and after
636 page_allow_access(static_cast<uint8_t*>(ptr) - page_size);
637 page_allow_access(static_cast<uint8_t*>(ptr) + page_size);
638
639#if defined(BOTAN_TARGET_OS_HAS_POSIX1) && defined(BOTAN_TARGET_OS_HAS_POSIX_MLOCK)
640 ::munlock(ptr, page_size);
641 ::munmap(static_cast<uint8_t*>(ptr) - page_size, 3 * page_size);
642#elif defined(BOTAN_TARGET_OS_HAS_VIRTUAL_LOCK)
643 ::VirtualUnlock(ptr, page_size);
644 ::VirtualFree(static_cast<uint8_t*>(ptr) - page_size, 0, MEM_RELEASE);
645#endif
646 }
647}
void page_allow_access(void *page)
Definition os_utils.cpp:599
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 232 of file os_utils.cpp.

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

Referenced by Botan::Thread_Pool::Thread_Pool().

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

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

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

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

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

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

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

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:173
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 599 of file os_utils.cpp.

599 {
600#if defined(BOTAN_TARGET_OS_HAS_POSIX1)
601 const size_t page_size = OS::system_page_size();
602 ::mprotect(page, page_size, PROT_READ | PROT_WRITE);
603#elif defined(BOTAN_TARGET_OS_HAS_VIRTUAL_LOCK)
604 const size_t page_size = OS::system_page_size();
605 DWORD old_perms = 0;
606 ::VirtualProtect(page, page_size, PAGE_READWRITE, &old_perms);
607 BOTAN_UNUSED(old_perms);
608#else
609 BOTAN_UNUSED(page);
610#endif
611}

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

649 {
650#if defined(BOTAN_TARGET_OS_HAS_PRCTL) && defined(PR_SET_VMA) && defined(PR_SET_VMA_ANON_NAME)
651 static constexpr char name[] = "Botan mlock pool";
652 int r = prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, reinterpret_cast<uintptr_t>(page), size, name);
653 BOTAN_UNUSED(r);
654#else
655 BOTAN_UNUSED(page, size);
656#endif
657}

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 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_NONE);
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_NOACCESS, &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 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 435 of file os_utils.cpp.

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

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

468 {
469 std::string value;
470 if(read_env_variable(value, name) && !value.empty()) {
471 try {
472 const size_t val = std::stoul(value, nullptr);
473 return val;
474 } catch(std::exception&) { /* ignore it */
475 }
476 }
477
478 return def;
479}
bool read_env_variable(std::string &value_out, std::string_view var_name)
Definition os_utils.cpp:435

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

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

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

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

354 {
355 const size_t default_page_size = 4096;
356
357#if defined(BOTAN_TARGET_OS_HAS_POSIX1)
358 long p = ::sysconf(_SC_PAGESIZE);
359 if(p > 1) {
360 return static_cast<size_t>(p);
361 } else {
362 return default_page_size;
363 }
364#elif defined(BOTAN_TARGET_OS_HAS_VIRTUAL_LOCK)
365 BOTAN_UNUSED(default_page_size);
366 SYSTEM_INFO sys_info;
367 ::GetSystemInfo(&sys_info);
368 return sys_info.dwPageSize;
369#else
370 return default_page_size;
371#endif
372}

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