Botan  2.4.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)
 

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

References BOTAN_UNUSED.

Referenced by Botan::mlock_allocator::deallocate().

284  {
285 #if defined(BOTAN_TARGET_OS_HAS_POSIX_MLOCK)
286 
287 #if !defined(MAP_NOCORE)
288  #define MAP_NOCORE 0
289 #endif
290 
291 #if !defined(MAP_ANONYMOUS)
292  #define MAP_ANONYMOUS MAP_ANON
293 #endif
294 
295  void* ptr = ::mmap(nullptr,
296  length,
297  PROT_READ | PROT_WRITE,
298  MAP_ANONYMOUS | MAP_SHARED | MAP_NOCORE,
299  /*fd*/-1,
300  /*offset*/0);
301 
302  if(ptr == MAP_FAILED)
303  {
304  return nullptr;
305  }
306 
307 #if defined(MADV_DONTDUMP)
308  ::madvise(ptr, length, MADV_DONTDUMP);
309 #endif
310 
311  if(::mlock(ptr, length) != 0)
312  {
313  ::munmap(ptr, length);
314  return nullptr; // failed to lock
315  }
316 
317  ::memset(ptr, 0, length);
318 
319  return ptr;
320 #elif defined BOTAN_TARGET_OS_HAS_VIRTUAL_LOCK
321  LPVOID ptr = ::VirtualAlloc(nullptr, length, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
322  if(!ptr)
323  {
324  return nullptr;
325  }
326 
327  if(::VirtualLock(ptr, length) == 0)
328  {
329  ::VirtualFree(ptr, 0, MEM_RELEASE);
330  return nullptr; // failed to lock
331  }
332 
333  return ptr;
334 #else
335  BOTAN_UNUSED(length);
336  return nullptr; /* not implemented */
337 #endif
338  }
#define BOTAN_UNUSED(...)
Definition: assert.h:106

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

References Botan::secure_scrub_memory().

Referenced by Botan::mlock_allocator::deallocate().

341  {
342  if(ptr == nullptr || length == 0)
343  return;
344 
345 #if defined(BOTAN_TARGET_OS_HAS_POSIX_MLOCK)
346  secure_scrub_memory(ptr, length);
347  ::munlock(ptr, length);
348  ::munmap(ptr, length);
349 #elif defined BOTAN_TARGET_OS_HAS_VIRTUAL_LOCK
350  secure_scrub_memory(ptr, length);
351  ::VirtualUnlock(ptr, length);
352  ::VirtualFree(ptr, 0, MEM_RELEASE);
353 #else
354  // Invalid argument because no way this pointer was allocated by us
355  throw Invalid_Argument("Invalid ptr to free_locked_pages");
356 #endif
357  }
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 135 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().

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

Referenced by Botan::mlock_allocator::deallocate().

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

◆ 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_TYPE_IS_UNIX)
67  return ::getpid();
68 #elif defined(BOTAN_TARGET_OS_IS_WINDOWS) || defined(BOTAN_TARGET_OS_IS_MINGW)
69  return ::GetCurrentProcessId();
70 #elif defined(BOTAN_TARGET_OS_TYPE_IS_UNIKERNEL) || 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_QUERY_PERF_COUNTER)
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  uint32_t rtc_low = 0, rtc_high = 0;
99  asm volatile("mftbu %0; mftb %1" : "=r" (rtc_high), "=r" (rtc_low));
100 
101  /*
102  qemu-ppc seems to not support mftb instr, it always returns zero.
103  If both time bases are 0, assume broken and return another clock.
104  */
105  if(rtc_high > 0 || rtc_low > 0)
106  {
107  rtc = (static_cast<uint64_t>(rtc_high) << 32) | rtc_low;
108  }
109 
110 #elif defined(BOTAN_TARGET_ARCH_IS_ALPHA)
111  asm volatile("rpcc %0" : "=r" (rtc));
112 
113  // OpenBSD does not trap access to the %tick register
114 #elif defined(BOTAN_TARGET_ARCH_IS_SPARC64) && !defined(BOTAN_TARGET_OS_IS_OPENBSD)
115  asm volatile("rd %%tick, %0" : "=r" (rtc));
116 
117 #elif defined(BOTAN_TARGET_ARCH_IS_IA64)
118  asm volatile("mov %0=ar.itc" : "=r" (rtc));
119 
120 #elif defined(BOTAN_TARGET_ARCH_IS_S390X)
121  asm volatile("stck 0(%0)" : : "a" (&rtc) : "memory", "cc");
122 
123 #elif defined(BOTAN_TARGET_ARCH_IS_HPPA)
124  asm volatile("mfctl 16,%0" : "=r" (rtc)); // 64-bit only?
125 
126 #else
127  //#warning "OS::get_processor_timestamp not implemented"
128 #endif
129 
130 #endif
131 
132  return rtc;
133  }

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

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

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

◆ 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_TYPE_IS_UNIX) || (defined(BOTAN_TARGET_OS_TYPE_IS_WINDOWS) && !defined(BOTAN_TARGET_OS_IS_MINGW))
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 372 of file os_utils.cpp.

373  {
374  volatile int probe_result = -3;
375 
376 #if defined(BOTAN_TARGET_OS_TYPE_IS_UNIX)
377  struct sigaction old_sigaction;
378  struct sigaction sigaction;
379 
380  sigaction.sa_handler = botan_sigill_handler;
381  sigemptyset(&sigaction.sa_mask);
382  sigaction.sa_flags = 0;
383 
384  int rc = ::sigaction(SIGILL, &sigaction, &old_sigaction);
385 
386  if(rc != 0)
387  throw Exception("run_cpu_instruction_probe sigaction failed");
388 
389  rc = sigsetjmp(g_sigill_jmp_buf, /*save sigs*/1);
390 
391  if(rc == 0)
392  {
393  // first call to sigsetjmp
394  probe_result = probe_fn();
395  }
396  else if(rc == 1)
397  {
398  // non-local return from siglongjmp in signal handler: return error
399  probe_result = -1;
400  }
401 
402  // Restore old SIGILL handler, if any
403  rc = ::sigaction(SIGILL, &old_sigaction, nullptr);
404  if(rc != 0)
405  throw Exception("run_cpu_instruction_probe sigaction restore failed");
406 
407 #elif defined(BOTAN_TARGET_OS_IS_WINDOWS) && defined(BOTAN_TARGET_COMPILER_IS_MSVC)
408 
409  // Windows SEH
410  __try
411  {
412  probe_result = probe_fn();
413  }
414  __except(::GetExceptionCode() == EXCEPTION_ILLEGAL_INSTRUCTION ?
415  EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
416  {
417  probe_result = -1;
418  }
419 
420 #endif
421 
422  return probe_result;
423  }