/* * OS and machine specific utility functions * (C) 2015,2016 Jack Lloyd * (C) 2016 Daniel Neus * * Botan is released under the Simplified BSD License (see license.txt) */ #include #include #include #include #include #if defined(BOTAN_TARGET_OS_TYPE_IS_UNIX) #include #include #include #include #endif #if defined(BOTAN_TARGET_OS_IS_WINDOWS) #include #endif namespace Botan { namespace OS { uint32_t get_process_id() { #if defined(BOTAN_TARGET_OS_TYPE_IS_UNIX) return ::getpid(); #elif defined(BOTAN_TARGET_OS_IS_WINDOWS) return ::GetCurrentProcessId(); #else throw Exception("get_process_id not supported"); #endif } uint64_t get_processor_timestamp() { uint64_t rtc = 0; #if defined(BOTAN_TARGET_OS_HAS_QUERY_PERF_COUNTER) LARGE_INTEGER tv; ::QueryPerformanceCounter(&tv); rtc = tv.QuadPart; #endif #if defined(BOTAN_USE_GCC_INLINE_ASM) #if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY) if(CPUID::has_rdtsc()) // not available on all x86 CPUs { uint32_t rtc_low = 0, rtc_high = 0; asm volatile("rdtsc" : "=d" (rtc_high), "=a" (rtc_low)); rtc = (static_cast(rtc_high) << 32) | rtc_low; } #elif defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY) uint32_t rtc_low = 0, rtc_high = 0; asm volatile("mftbu %0; mftb %1" : "=r" (rtc_high), "=r" (rtc_low)); rtc = (static_cast(rtc_high) << 32) | rtc_low; #elif defined(BOTAN_TARGET_ARCH_IS_ALPHA) asm volatile("rpcc %0" : "=r" (rtc)); #elif defined(BOTAN_TARGET_ARCH_IS_SPARC64) && !defined(BOTAN_TARGET_OS_IS_OPENBSD) // OpenBSD does not trap access to the %tick register asm volatile("rd %%tick, %0" : "=r" (rtc)); #elif defined(BOTAN_TARGET_ARCH_IS_IA64) asm volatile("mov %0=ar.itc" : "=r" (rtc)); #elif defined(BOTAN_TARGET_ARCH_IS_S390X) asm volatile("stck 0(%0)" : : "a" (&rtc) : "memory", "cc"); #elif defined(BOTAN_TARGET_ARCH_IS_HPPA) asm volatile("mfctl 16,%0" : "=r" (rtc)); // 64-bit only? #endif #endif return rtc; } uint64_t get_system_timestamp_ns() { #if defined(BOTAN_TARGET_OS_HAS_CLOCK_GETTIME) struct timespec ts; if(::clock_gettime(CLOCK_REALTIME, &ts) == 0) { return (static_cast(ts.tv_sec) * 1000000000) + static_cast(ts.tv_nsec); } #endif auto now = std::chrono::system_clock::now().time_since_epoch(); return std::chrono::duration_cast(now).count(); } size_t get_memory_locking_limit() { #if defined(BOTAN_TARGET_OS_HAS_POSIX_MLOCK) /* * Linux defaults to only 64 KiB of mlockable memory per process * (too small) but BSDs offer a small fraction of total RAM (more * than we need). Bound the total mlock size to 512 KiB which is * enough to run the entire test suite without spilling to non-mlock * memory (and thus presumably also enough for many useful * programs), but small enough that we should not cause problems * even if many processes are mlocking on the same machine. */ size_t mlock_requested = BOTAN_MLOCK_ALLOCATOR_MAX_LOCKED_KB; /* * Allow override via env variable */ if(const char* env = ::getenv("BOTAN_MLOCK_POOL_SIZE")) { try { const size_t user_req = std::stoul(env, nullptr); mlock_requested = std::min(user_req, mlock_requested); } catch(std::exception&) { /* ignore it */ } } if(mlock_requested > 0) { struct ::rlimit limits; ::getrlimit(RLIMIT_MEMLOCK, &limits); if(limits.rlim_cur < limits.rlim_max) { limits.rlim_cur = limits.rlim_max; ::setrlimit(RLIMIT_MEMLOCK, &limits); ::getrlimit(RLIMIT_MEMLOCK, &limits); } return std::min(limits.rlim_cur, mlock_requested * 1024); } #elif defined BOTAN_TARGET_OS_HAS_VIRTUAL_LOCK SIZE_T working_min = 0, working_max = 0; DWORD working_flags = 0; if(!::GetProcessWorkingSetSizeEx(::GetCurrentProcess(), &working_min, &working_max, &working_flags)) { return 0; } SYSTEM_INFO sSysInfo; ::GetSystemInfo(&sSysInfo); // According to Microsoft MSDN: // 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 // In the book "Windows Internals Part 2": the maximum lockable pages are minimum working set size - 8 pages // But the information in the book seems to be inaccurate/outdated // I've tested this on Windows 8.1 x64, Windows 10 x64 and Windows 7 x86 // On all three OS the value is 11 instead of 8 size_t overhead = sSysInfo.dwPageSize * 11ULL; if(working_min > overhead) { size_t lockable_bytes = working_min - overhead; if(lockable_bytes < (BOTAN_MLOCK_ALLOCATOR_MAX_LOCKED_KB * 1024ULL)) { return lockable_bytes; } else { return BOTAN_MLOCK_ALLOCATOR_MAX_LOCKED_KB * 1024ULL; } } #endif return 0; } void* allocate_locked_pages(size_t length) { #if defined(BOTAN_TARGET_OS_HAS_POSIX_MLOCK) #if !defined(MAP_NOCORE) #define MAP_NOCORE 0 #endif #if !defined(MAP_ANONYMOUS) #define MAP_ANONYMOUS MAP_ANON #endif void* ptr = ::mmap(nullptr, length, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED | MAP_NOCORE, /*fd*/-1, /*offset*/0); if(ptr == MAP_FAILED) { return nullptr; } #if defined(MADV_DONTDUMP) ::madvise(ptr, length, MADV_DONTDUMP); #endif if(::mlock(ptr, length) != 0) { ::munmap(ptr, length); return nullptr; // failed to lock } ::memset(ptr, 0, length); return ptr; #elif defined BOTAN_TARGET_OS_HAS_VIRTUAL_LOCK LPVOID ptr = ::VirtualAlloc(nullptr, length, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); if(!ptr) { return nullptr; } if(::VirtualLock(ptr, length) == 0) { ::VirtualFree(ptr, 0, MEM_RELEASE); return nullptr; // failed to lock } return ptr; #else return nullptr; /* not implemented */ #endif } void free_locked_pages(void* ptr, size_t length) { if(ptr == nullptr || length == 0) return; #if defined(BOTAN_TARGET_OS_HAS_POSIX_MLOCK) zero_mem(ptr, length); ::munlock(ptr, length); ::munmap(ptr, length); #elif defined BOTAN_TARGET_OS_HAS_VIRTUAL_LOCK zero_mem(ptr, length); ::VirtualUnlock(ptr, length); ::VirtualFree(ptr, 0, MEM_RELEASE); #else // Invalid argument because no way this pointer was allocated by us throw Invalid_Argument("Invalid ptr to free_locked_pages"); #endif } } }