Botan  2.7.0
Crypto and TLS for C++11
Classes | Functions
Botan::OS Namespace Reference

Classes

class  Socket
 

Functions

void * allocate_locked_pages (size_t length)
 
void free_locked_pages (void *ptr, size_t length)
 
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_processor_timestamp ()
 
uint64_t BOTAN_TEST_API get_system_timestamp_ns ()
 
std::unique_ptr< Socket > BOTAN_TEST_API open_socket (const std::string &hostname, const std::string &service, std::chrono::milliseconds timeout)
 
int BOTAN_TEST_API run_cpu_instruction_probe (std::function< int()> probe_fn)
 
size_t system_page_size ()
 

Function Documentation

◆ allocate_locked_pages()

void * Botan::OS::allocate_locked_pages ( size_t  length)

Request so many bytes of page-aligned RAM locked into memory using mlock, VirtualLock, or similar. Returns null on failure. The memory returned is zeroed. Free it with free_locked_pages.

Parameters
lengthrequested allocation in bytes

Definition at line 300 of file os_utils.cpp.

References BOTAN_UNUSED.

301  {
302 #if defined(BOTAN_TARGET_OS_HAS_POSIX1)
303 
304 #if !defined(MAP_NOCORE)
305  #define MAP_NOCORE 0
306 #endif
307 
308 #if !defined(MAP_ANONYMOUS)
309  #define MAP_ANONYMOUS MAP_ANON
310 #endif
311 
312  void* ptr = ::mmap(nullptr,
313  length,
314  PROT_READ | PROT_WRITE,
315  MAP_ANONYMOUS | MAP_SHARED | MAP_NOCORE,
316  /*fd*/-1,
317  /*offset*/0);
318 
319  if(ptr == MAP_FAILED)
320  {
321  return nullptr;
322  }
323 
324 #if defined(MADV_DONTDUMP)
325  ::madvise(ptr, length, MADV_DONTDUMP);
326 #endif
327 
328 #if defined(BOTAN_TARGET_OS_HAS_POSIX_MLOCK)
329  if(::mlock(ptr, length) != 0)
330  {
331  ::munmap(ptr, length);
332  return nullptr; // failed to lock
333  }
334 #endif
335 
336  ::memset(ptr, 0, length);
337 
338  return ptr;
339 #elif defined(BOTAN_TARGET_OS_HAS_VIRTUAL_LOCK)
340  LPVOID ptr = ::VirtualAlloc(nullptr, length, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
341  if(!ptr)
342  {
343  return nullptr;
344  }
345 
346  if(::VirtualLock(ptr, length) == 0)
347  {
348  ::VirtualFree(ptr, 0, MEM_RELEASE);
349  return nullptr; // failed to lock
350  }
351 
352  return ptr;
353 #else
354  BOTAN_UNUSED(length);
355  return nullptr; /* not implemented */
356 #endif
357  }
#define BOTAN_UNUSED(...)
Definition: assert.h:130

◆ free_locked_pages()

void Botan::OS::free_locked_pages ( void *  ptr,
size_t  length 
)

Free memory allocated by allocate_locked_pages

Parameters
ptra pointer returned by allocate_locked_pages
lengthlength passed to allocate_locked_pages

Definition at line 359 of file os_utils.cpp.

References Botan::secure_scrub_memory().

360  {
361  if(ptr == nullptr || length == 0)
362  return;
363 
364 #if defined(BOTAN_TARGET_OS_HAS_POSIX1)
365  secure_scrub_memory(ptr, length);
366 
367 #if defined(BOTAN_TARGET_OS_HAS_POSIX_MLOCK)
368  ::munlock(ptr, length);
369 #endif
370 
371  ::munmap(ptr, length);
372 #elif defined(BOTAN_TARGET_OS_HAS_VIRTUAL_LOCK)
373  secure_scrub_memory(ptr, length);
374  ::VirtualUnlock(ptr, length);
375  ::VirtualFree(ptr, 0, MEM_RELEASE);
376 #else
377  // Invalid argument because no way this pointer was allocated by us
378  throw Invalid_Argument("Invalid ptr to free_locked_pages");
379 #endif
380  }
void secure_scrub_memory(void *ptr, size_t n)
Definition: os_utils.cpp:37

◆ get_high_resolution_clock()

uint64_t Botan::OS::get_high_resolution_clock ( )

Definition at line 138 of file os_utils.cpp.

References get_processor_timestamp().

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

139  {
140  if(uint64_t cpu_clock = OS::get_processor_timestamp())
141  return cpu_clock;
142 
143  /*
144  If we got here either we either don't have an asm instruction
145  above, or (for x86) RDTSC is not available at runtime. Try some
146  clock_gettimes and return the first one that works, or otherwise
147  fall back to std::chrono.
148  */
149 
150 #if defined(BOTAN_TARGET_OS_HAS_CLOCK_GETTIME)
151 
152  // The ordering here is somewhat arbitrary...
153  const clockid_t clock_types[] = {
154 #if defined(CLOCK_MONOTONIC_HR)
155  CLOCK_MONOTONIC_HR,
156 #endif
157 #if defined(CLOCK_MONOTONIC_RAW)
158  CLOCK_MONOTONIC_RAW,
159 #endif
160 #if defined(CLOCK_MONOTONIC)
161  CLOCK_MONOTONIC,
162 #endif
163 #if defined(CLOCK_PROCESS_CPUTIME_ID)
164  CLOCK_PROCESS_CPUTIME_ID,
165 #endif
166 #if defined(CLOCK_THREAD_CPUTIME_ID)
167  CLOCK_THREAD_CPUTIME_ID,
168 #endif
169  };
170 
171  for(clockid_t clock : clock_types)
172  {
173  struct timespec ts;
174  if(::clock_gettime(clock, &ts) == 0)
175  {
176  return (static_cast<uint64_t>(ts.tv_sec) * 1000000000) + static_cast<uint64_t>(ts.tv_nsec);
177  }
178  }
179 #endif
180 
181  // Plain C++11 fallback
182  auto now = std::chrono::high_resolution_clock::now().time_since_epoch();
183  return std::chrono::duration_cast<std::chrono::nanoseconds>(now).count();
184  }
uint64_t BOTAN_TEST_API get_processor_timestamp()
Definition: os_utils.cpp:77

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

References system_page_size().

219  {
220 #if defined(BOTAN_TARGET_OS_HAS_POSIX1)
221  /*
222  * Linux defaults to only 64 KiB of mlockable memory per process
223  * (too small) but BSDs offer a small fraction of total RAM (more
224  * than we need). Bound the total mlock size to 512 KiB which is
225  * enough to run the entire test suite without spilling to non-mlock
226  * memory (and thus presumably also enough for many useful
227  * programs), but small enough that we should not cause problems
228  * even if many processes are mlocking on the same machine.
229  */
230  size_t mlock_requested = BOTAN_MLOCK_ALLOCATOR_MAX_LOCKED_KB;
231 
232  /*
233  * Allow override via env variable
234  */
235  if(const char* env = std::getenv("BOTAN_MLOCK_POOL_SIZE"))
236  {
237  try
238  {
239  const size_t user_req = std::stoul(env, nullptr);
240  mlock_requested = std::min(user_req, mlock_requested);
241  }
242  catch(std::exception&) { /* ignore it */ }
243  }
244 
245 #if defined(RLIMIT_MEMLOCK)
246  if(mlock_requested > 0)
247  {
248  struct ::rlimit limits;
249 
250  ::getrlimit(RLIMIT_MEMLOCK, &limits);
251 
252  if(limits.rlim_cur < limits.rlim_max)
253  {
254  limits.rlim_cur = limits.rlim_max;
255  ::setrlimit(RLIMIT_MEMLOCK, &limits);
256  ::getrlimit(RLIMIT_MEMLOCK, &limits);
257  }
258 
259  return std::min<size_t>(limits.rlim_cur, mlock_requested * 1024);
260  }
261 #else
262  /*
263  * If RLIMIT_MEMLOCK is not defined, likely the OS does not support
264  * unprivileged mlock calls.
265  */
266  return 0;
267 #endif
268 
269 #elif defined(BOTAN_TARGET_OS_HAS_VIRTUAL_LOCK)
270  SIZE_T working_min = 0, working_max = 0;
271  if(!::GetProcessWorkingSetSize(::GetCurrentProcess(), &working_min, &working_max))
272  {
273  return 0;
274  }
275 
276  // According to Microsoft MSDN:
277  // 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
278  // In the book "Windows Internals Part 2": the maximum lockable pages are minimum working set size - 8 pages
279  // But the information in the book seems to be inaccurate/outdated
280  // I've tested this on Windows 8.1 x64, Windows 10 x64 and Windows 7 x86
281  // On all three OS the value is 11 instead of 8
282  size_t overhead = OS::system_page_size() * 11ULL;
283  if(working_min > overhead)
284  {
285  size_t lockable_bytes = working_min - overhead;
286  if(lockable_bytes < (BOTAN_MLOCK_ALLOCATOR_MAX_LOCKED_KB * 1024ULL))
287  {
288  return lockable_bytes;
289  }
290  else
291  {
292  return BOTAN_MLOCK_ALLOCATOR_MAX_LOCKED_KB * 1024ULL;
293  }
294  }
295 #endif
296 
297  return 0;
298  }
size_t system_page_size()
Definition: os_utils.cpp:200

◆ 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 IncludeOS it returns 0 since there is no process ID to speak of in a unikernel.

Definition at line 64 of file os_utils.cpp.

Referenced by Botan::Stateful_RNG::reseed_check().

65  {
66 #if defined(BOTAN_TARGET_OS_HAS_POSIX1)
67  return ::getpid();
68 #elif defined(BOTAN_TARGET_OS_HAS_WIN32)
69  return ::GetCurrentProcessId();
70 #elif defined(BOTAN_TARGET_OS_IS_INCLUDEOS) || defined(BOTAN_TARGET_OS_IS_LLVM)
71  return 0; // truly no meaningful value
72 #else
73  #error "Missing get_process_id"
74 #endif
75  }

◆ get_processor_timestamp()

uint64_t Botan::OS::get_processor_timestamp ( )
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 77 of file os_utils.cpp.

Referenced by get_high_resolution_clock().

78  {
79  uint64_t rtc = 0;
80 
81 #if defined(BOTAN_TARGET_OS_HAS_WIN32)
82  LARGE_INTEGER tv;
83  ::QueryPerformanceCounter(&tv);
84  rtc = tv.QuadPart;
85 
86 #elif defined(BOTAN_USE_GCC_INLINE_ASM)
87 
88 #if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY)
89 
90  if(CPUID::has_rdtsc())
91  {
92  uint32_t rtc_low = 0, rtc_high = 0;
93  asm volatile("rdtsc" : "=d" (rtc_high), "=a" (rtc_low));
94  rtc = (static_cast<uint64_t>(rtc_high) << 32) | rtc_low;
95  }
96 
97 #elif defined(BOTAN_TARGET_ARCH_IS_PPC64)
98 
99  for(;;)
100  {
101  uint32_t rtc_low = 0, rtc_high = 0, rtc_high2 = 0;
102  asm volatile("mftbu %0" : "=r" (rtc_high));
103  asm volatile("mftb %0" : "=r" (rtc_low));
104  asm volatile("mftbu %0" : "=r" (rtc_high2));
105 
106  if(rtc_high == rtc_high2)
107  {
108  rtc = (static_cast<uint64_t>(rtc_high) << 32) | rtc_low;
109  break;
110  }
111  }
112 
113 #elif defined(BOTAN_TARGET_ARCH_IS_ALPHA)
114  asm volatile("rpcc %0" : "=r" (rtc));
115 
116  // OpenBSD does not trap access to the %tick register
117 #elif defined(BOTAN_TARGET_ARCH_IS_SPARC64) && !defined(BOTAN_TARGET_OS_IS_OPENBSD)
118  asm volatile("rd %%tick, %0" : "=r" (rtc));
119 
120 #elif defined(BOTAN_TARGET_ARCH_IS_IA64)
121  asm volatile("mov %0=ar.itc" : "=r" (rtc));
122 
123 #elif defined(BOTAN_TARGET_ARCH_IS_S390X)
124  asm volatile("stck 0(%0)" : : "a" (&rtc) : "memory", "cc");
125 
126 #elif defined(BOTAN_TARGET_ARCH_IS_HPPA)
127  asm volatile("mfctl 16,%0" : "=r" (rtc)); // 64-bit only?
128 
129 #else
130  //#warning "OS::get_processor_timestamp not implemented"
131 #endif
132 
133 #endif
134 
135  return rtc;
136  }

◆ 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.

Definition at line 186 of file os_utils.cpp.

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

187  {
188 #if defined(BOTAN_TARGET_OS_HAS_CLOCK_GETTIME)
189  struct timespec ts;
190  if(::clock_gettime(CLOCK_REALTIME, &ts) == 0)
191  {
192  return (static_cast<uint64_t>(ts.tv_sec) * 1000000000) + static_cast<uint64_t>(ts.tv_nsec);
193  }
194 #endif
195 
196  auto now = std::chrono::system_clock::now().time_since_epoch();
197  return std::chrono::duration_cast<std::chrono::nanoseconds>(now).count();
198  }

◆ open_socket()

std::unique_ptr< OS::Socket > Botan::OS::open_socket ( const std::string &  hostname,
const std::string &  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 352 of file socket.cpp.

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

◆ run_cpu_instruction_probe()

int Botan::OS::run_cpu_instruction_probe ( 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 395 of file os_utils.cpp.

396  {
397  volatile int probe_result = -3;
398 
399 #if defined(BOTAN_TARGET_OS_HAS_POSIX1)
400  struct sigaction old_sigaction;
401  struct sigaction sigaction;
402 
403  sigaction.sa_handler = botan_sigill_handler;
404  sigemptyset(&sigaction.sa_mask);
405  sigaction.sa_flags = 0;
406 
407  int rc = ::sigaction(SIGILL, &sigaction, &old_sigaction);
408 
409  if(rc != 0)
410  throw Exception("run_cpu_instruction_probe sigaction failed");
411 
412  rc = sigsetjmp(g_sigill_jmp_buf, /*save sigs*/1);
413 
414  if(rc == 0)
415  {
416  // first call to sigsetjmp
417  probe_result = probe_fn();
418  }
419  else if(rc == 1)
420  {
421  // non-local return from siglongjmp in signal handler: return error
422  probe_result = -1;
423  }
424 
425  // Restore old SIGILL handler, if any
426  rc = ::sigaction(SIGILL, &old_sigaction, nullptr);
427  if(rc != 0)
428  throw Exception("run_cpu_instruction_probe sigaction restore failed");
429 
430 #elif defined(BOTAN_TARGET_OS_IS_WINDOWS) && defined(BOTAN_TARGET_COMPILER_IS_MSVC)
431 
432  // Windows SEH
433  __try
434  {
435  probe_result = probe_fn();
436  }
437  __except(::GetExceptionCode() == EXCEPTION_ILLEGAL_INSTRUCTION ?
438  EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
439  {
440  probe_result = -1;
441  }
442 
443 #endif
444 
445  return probe_result;
446  }

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

Referenced by get_memory_locking_limit().

201  {
202 #if defined(BOTAN_TARGET_OS_HAS_POSIX1)
203  long p = ::sysconf(_SC_PAGESIZE);
204  if(p > 1)
205  return static_cast<size_t>(p);
206  else
207  return 4096;
208 #elif defined(BOTAN_TARGET_OS_HAS_VIRTUAL_LOCK)
209  SYSTEM_INFO sys_info;
210  ::GetSystemInfo(&sys_info);
211  return sys_info.dwPageSize;
212 #endif
213 
214  // default value
215  return 4096;
216  }