Botan  2.8.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)
 
bool running_in_privileged_state ()
 
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 317 of file os_utils.cpp.

References BOTAN_UNUSED.

318  {
319 #if defined(BOTAN_TARGET_OS_HAS_POSIX1) && defined(BOTAN_TARGET_OS_HAS_POSIX_MLOCK)
320 
321 #if !defined(MAP_NOCORE)
322  #define MAP_NOCORE 0
323 #endif
324 
325 #if !defined(MAP_ANONYMOUS)
326  #define MAP_ANONYMOUS MAP_ANON
327 #endif
328 
329  void* ptr = ::mmap(nullptr,
330  length,
331  PROT_READ | PROT_WRITE,
332  MAP_ANONYMOUS | MAP_SHARED | MAP_NOCORE,
333  /*fd*/-1,
334  /*offset*/0);
335 
336  if(ptr == MAP_FAILED)
337  {
338  return nullptr;
339  }
340 
341 #if defined(MADV_DONTDUMP)
342  ::madvise(ptr, length, MADV_DONTDUMP);
343 #endif
344 
345  if(::mlock(ptr, length) != 0)
346  {
347  ::munmap(ptr, length);
348  return nullptr; // failed to lock
349  }
350 
351  ::memset(ptr, 0, length);
352 
353  return ptr;
354 #elif defined(BOTAN_TARGET_OS_HAS_VIRTUAL_LOCK)
355  LPVOID ptr = ::VirtualAlloc(nullptr, length, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
356  if(!ptr)
357  {
358  return nullptr;
359  }
360 
361  if(::VirtualLock(ptr, length) == 0)
362  {
363  ::VirtualFree(ptr, 0, MEM_RELEASE);
364  return nullptr; // failed to lock
365  }
366 
367  return ptr;
368 #else
369  BOTAN_UNUSED(length);
370  return nullptr; /* not implemented */
371 #endif
372  }
#define BOTAN_UNUSED(...)
Definition: assert.h:142

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

References Botan::secure_scrub_memory().

375  {
376  if(ptr == nullptr || length == 0)
377  return;
378 
379 #if defined(BOTAN_TARGET_OS_HAS_POSIX1) && defined(BOTAN_TARGET_OS_HAS_POSIX_MLOCK)
380  secure_scrub_memory(ptr, length);
381  ::munlock(ptr, length);
382  ::munmap(ptr, length);
383 
384 #elif defined(BOTAN_TARGET_OS_HAS_VIRTUAL_LOCK)
385  secure_scrub_memory(ptr, length);
386  ::VirtualUnlock(ptr, length);
387  ::VirtualFree(ptr, 0, MEM_RELEASE);
388 
389 #else
390  // Invalid argument because no way this pointer was allocated by us
391  throw Invalid_Argument("Invalid ptr to free_locked_pages");
392 #endif
393  }
void secure_scrub_memory(void *ptr, size_t n)
Definition: os_utils.cpp:43

◆ get_high_resolution_clock()

uint64_t Botan::OS::get_high_resolution_clock ( )

Definition at line 155 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().

156  {
157  if(uint64_t cpu_clock = OS::get_processor_timestamp())
158  return cpu_clock;
159 
160  /*
161  If we got here either we either don't have an asm instruction
162  above, or (for x86) RDTSC is not available at runtime. Try some
163  clock_gettimes and return the first one that works, or otherwise
164  fall back to std::chrono.
165  */
166 
167 #if defined(BOTAN_TARGET_OS_HAS_CLOCK_GETTIME)
168 
169  // The ordering here is somewhat arbitrary...
170  const clockid_t clock_types[] = {
171 #if defined(CLOCK_MONOTONIC_HR)
172  CLOCK_MONOTONIC_HR,
173 #endif
174 #if defined(CLOCK_MONOTONIC_RAW)
175  CLOCK_MONOTONIC_RAW,
176 #endif
177 #if defined(CLOCK_MONOTONIC)
178  CLOCK_MONOTONIC,
179 #endif
180 #if defined(CLOCK_PROCESS_CPUTIME_ID)
181  CLOCK_PROCESS_CPUTIME_ID,
182 #endif
183 #if defined(CLOCK_THREAD_CPUTIME_ID)
184  CLOCK_THREAD_CPUTIME_ID,
185 #endif
186  };
187 
188  for(clockid_t clock : clock_types)
189  {
190  struct timespec ts;
191  if(::clock_gettime(clock, &ts) == 0)
192  {
193  return (static_cast<uint64_t>(ts.tv_sec) * 1000000000) + static_cast<uint64_t>(ts.tv_nsec);
194  }
195  }
196 #endif
197 
198  // Plain C++11 fallback
199  auto now = std::chrono::high_resolution_clock::now().time_since_epoch();
200  return std::chrono::duration_cast<std::chrono::nanoseconds>(now).count();
201  }
uint64_t BOTAN_TEST_API get_processor_timestamp()
Definition: os_utils.cpp:94

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

References running_in_privileged_state(), and system_page_size().

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

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

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

71  {
72 #if defined(BOTAN_TARGET_OS_HAS_POSIX1)
73  return ::getpid();
74 #elif defined(BOTAN_TARGET_OS_HAS_WIN32)
75  return ::GetCurrentProcessId();
76 #elif defined(BOTAN_TARGET_OS_IS_INCLUDEOS) || defined(BOTAN_TARGET_OS_IS_LLVM)
77  return 0; // truly no meaningful value
78 #else
79  #error "Missing get_process_id"
80 #endif
81  }

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

Referenced by Botan::Timer::get_cpu_cycle_counter(), and get_high_resolution_clock().

95  {
96  uint64_t rtc = 0;
97 
98 #if defined(BOTAN_TARGET_OS_HAS_WIN32)
99  LARGE_INTEGER tv;
100  ::QueryPerformanceCounter(&tv);
101  rtc = tv.QuadPart;
102 
103 #elif defined(BOTAN_USE_GCC_INLINE_ASM)
104 
105 #if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY)
106 
107  if(CPUID::has_rdtsc())
108  {
109  uint32_t rtc_low = 0, rtc_high = 0;
110  asm volatile("rdtsc" : "=d" (rtc_high), "=a" (rtc_low));
111  rtc = (static_cast<uint64_t>(rtc_high) << 32) | rtc_low;
112  }
113 
114 #elif defined(BOTAN_TARGET_ARCH_IS_PPC64)
115 
116  for(;;)
117  {
118  uint32_t rtc_low = 0, rtc_high = 0, rtc_high2 = 0;
119  asm volatile("mftbu %0" : "=r" (rtc_high));
120  asm volatile("mftb %0" : "=r" (rtc_low));
121  asm volatile("mftbu %0" : "=r" (rtc_high2));
122 
123  if(rtc_high == rtc_high2)
124  {
125  rtc = (static_cast<uint64_t>(rtc_high) << 32) | rtc_low;
126  break;
127  }
128  }
129 
130 #elif defined(BOTAN_TARGET_ARCH_IS_ALPHA)
131  asm volatile("rpcc %0" : "=r" (rtc));
132 
133  // OpenBSD does not trap access to the %tick register
134 #elif defined(BOTAN_TARGET_ARCH_IS_SPARC64) && !defined(BOTAN_TARGET_OS_IS_OPENBSD)
135  asm volatile("rd %%tick, %0" : "=r" (rtc));
136 
137 #elif defined(BOTAN_TARGET_ARCH_IS_IA64)
138  asm volatile("mov %0=ar.itc" : "=r" (rtc));
139 
140 #elif defined(BOTAN_TARGET_ARCH_IS_S390X)
141  asm volatile("stck 0(%0)" : : "a" (&rtc) : "memory", "cc");
142 
143 #elif defined(BOTAN_TARGET_ARCH_IS_HPPA)
144  asm volatile("mfctl 16,%0" : "=r" (rtc)); // 64-bit only?
145 
146 #else
147  //#warning "OS::get_processor_timestamp not implemented"
148 #endif
149 
150 #endif
151 
152  return rtc;
153  }

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

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

204  {
205 #if defined(BOTAN_TARGET_OS_HAS_CLOCK_GETTIME)
206  struct timespec ts;
207  if(::clock_gettime(CLOCK_REALTIME, &ts) == 0)
208  {
209  return (static_cast<uint64_t>(ts.tv_sec) * 1000000000) + static_cast<uint64_t>(ts.tv_nsec);
210  }
211 #endif
212 
213  auto now = std::chrono::system_clock::now().time_since_epoch();
214  return std::chrono::duration_cast<std::chrono::nanoseconds>(now).count();
215  }

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

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

◆ running_in_privileged_state()

bool Botan::OS::running_in_privileged_state ( )

Test if we are currently running with elevated permissions eg setuid, setgid, or with POSIX caps set.

Definition at line 83 of file os_utils.cpp.

Referenced by Botan::Entropy_Source::create(), Botan_FFI::ffi_error_exception_thrown(), and get_memory_locking_limit().

84  {
85 #if defined(BOTAN_TARGET_OS_HAS_GETAUXVAL) && defined(AT_SECURE)
86  return ::getauxval(AT_SECURE) != 0;
87 #elif defined(BOTAN_TARGET_OS_HAS_POSIX1)
88  return (::getuid() != ::geteuid()) || (::getgid() != ::getegid());
89 #else
90  return false;
91 #endif
92  }

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

Referenced by get_memory_locking_limit().

218  {
219  const size_t default_page_size = 4096;
220 
221 #if defined(BOTAN_TARGET_OS_HAS_POSIX1)
222  long p = ::sysconf(_SC_PAGESIZE);
223  if(p > 1)
224  return static_cast<size_t>(p);
225  else
226  return default_page_size;
227 #elif defined(BOTAN_TARGET_OS_HAS_VIRTUAL_LOCK)
228  SYSTEM_INFO sys_info;
229  ::GetSystemInfo(&sys_info);
230  return sys_info.dwPageSize;
231 #else
232  return default_page_size;
233 #endif
234  }