Botan  2.1.0
Crypto and TLS for C++11
Functions
Botan::OS Namespace Reference

Functions

void * allocate_locked_pages (size_t length)
 
void free_locked_pages (void *ptr, size_t length)
 
uint64_t BOTAN_DLL get_high_resolution_clock ()
 
size_t get_memory_locking_limit ()
 
uint32_t BOTAN_DLL get_process_id ()
 
uint64_t BOTAN_DLL get_processor_timestamp ()
 
uint64_t BOTAN_DLL get_system_timestamp_ns ()
 
int BOTAN_DLL run_cpu_instruction_probe (std::function< int()> probe_fn)
 

Function Documentation

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

References BOTAN_UNUSED.

258  {
259 #if defined(BOTAN_TARGET_OS_HAS_POSIX_MLOCK)
260 
261 #if !defined(MAP_NOCORE)
262  #define MAP_NOCORE 0
263 #endif
264 
265 #if !defined(MAP_ANONYMOUS)
266  #define MAP_ANONYMOUS MAP_ANON
267 #endif
268 
269  void* ptr = ::mmap(nullptr,
270  length,
271  PROT_READ | PROT_WRITE,
272  MAP_ANONYMOUS | MAP_SHARED | MAP_NOCORE,
273  /*fd*/-1,
274  /*offset*/0);
275 
276  if(ptr == MAP_FAILED)
277  {
278  return nullptr;
279  }
280 
281 #if defined(MADV_DONTDUMP)
282  ::madvise(ptr, length, MADV_DONTDUMP);
283 #endif
284 
285  if(::mlock(ptr, length) != 0)
286  {
287  ::munmap(ptr, length);
288  return nullptr; // failed to lock
289  }
290 
291  ::memset(ptr, 0, length);
292 
293  return ptr;
294 #elif defined BOTAN_TARGET_OS_HAS_VIRTUAL_LOCK
295  LPVOID ptr = ::VirtualAlloc(nullptr, length, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
296  if(!ptr)
297  {
298  return nullptr;
299  }
300 
301  if(::VirtualLock(ptr, length) == 0)
302  {
303  ::VirtualFree(ptr, 0, MEM_RELEASE);
304  return nullptr; // failed to lock
305  }
306 
307  return ptr;
308 #else
309  BOTAN_UNUSED(length);
310  return nullptr; /* not implemented */
311 #endif
312  }
#define BOTAN_UNUSED(v)
Definition: assert.h:92
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 314 of file os_utils.cpp.

References Botan::secure_scrub_memory().

315  {
316  if(ptr == nullptr || length == 0)
317  return;
318 
319 #if defined(BOTAN_TARGET_OS_HAS_POSIX_MLOCK)
320  secure_scrub_memory(ptr, length);
321  ::munlock(ptr, length);
322  ::munmap(ptr, length);
323 #elif defined BOTAN_TARGET_OS_HAS_VIRTUAL_LOCK
324  secure_scrub_memory(ptr, length);
325  ::VirtualUnlock(ptr, length);
326  ::VirtualFree(ptr, 0, MEM_RELEASE);
327 #else
328  // Invalid argument because no way this pointer was allocated by us
329  throw Invalid_Argument("Invalid ptr to free_locked_pages");
330 #endif
331  }
void secure_scrub_memory(void *ptr, size_t n)
Definition: mem_ops.cpp:17
uint64_t Botan::OS::get_high_resolution_clock ( )

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

110  {
111  if(uint64_t cpu_clock = OS::get_processor_timestamp())
112  return cpu_clock;
113 
114  /*
115  If we got here either we either don't have an asm instruction
116  above, or (for x86) RDTSC is not available at runtime. Try some
117  clock_gettimes and return the first one that works, or otherwise
118  fall back to std::chrono.
119  */
120 
121 #if defined(BOTAN_TARGET_OS_HAS_CLOCK_GETTIME)
122 
123  // The ordering here is somewhat arbitrary...
124  const clockid_t clock_types[] = {
125 #if defined(CLOCK_MONOTONIC_HR)
126  CLOCK_MONOTONIC_HR,
127 #endif
128 #if defined(CLOCK_MONOTONIC_RAW)
129  CLOCK_MONOTONIC_RAW,
130 #endif
131 #if defined(CLOCK_MONOTONIC)
132  CLOCK_MONOTONIC,
133 #endif
134 #if defined(CLOCK_PROCESS_CPUTIME_ID)
135  CLOCK_PROCESS_CPUTIME_ID,
136 #endif
137 #if defined(CLOCK_THREAD_CPUTIME_ID)
138  CLOCK_THREAD_CPUTIME_ID,
139 #endif
140  };
141 
142  for(clockid_t clock : clock_types)
143  {
144  struct timespec ts;
145  if(::clock_gettime(clock, &ts) == 0)
146  {
147  return (static_cast<uint64_t>(ts.tv_sec) * 1000000000) + static_cast<uint64_t>(ts.tv_nsec);
148  }
149  }
150 #endif
151 
152  // Plain C++11 fallback
153  auto now = std::chrono::high_resolution_clock::now().time_since_epoch();
154  return std::chrono::duration_cast<std::chrono::nanoseconds>(now).count();
155  }
uint64_t BOTAN_DLL get_processor_timestamp()
Definition: os_utils.cpp:44
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 171 of file os_utils.cpp.

References Botan::CT::min().

172  {
173 #if defined(BOTAN_TARGET_OS_HAS_POSIX_MLOCK)
174  /*
175  * Linux defaults to only 64 KiB of mlockable memory per process
176  * (too small) but BSDs offer a small fraction of total RAM (more
177  * than we need). Bound the total mlock size to 512 KiB which is
178  * enough to run the entire test suite without spilling to non-mlock
179  * memory (and thus presumably also enough for many useful
180  * programs), but small enough that we should not cause problems
181  * even if many processes are mlocking on the same machine.
182  */
183  size_t mlock_requested = BOTAN_MLOCK_ALLOCATOR_MAX_LOCKED_KB;
184 
185  /*
186  * Allow override via env variable
187  */
188  if(const char* env = ::getenv("BOTAN_MLOCK_POOL_SIZE"))
189  {
190  try
191  {
192  const size_t user_req = std::stoul(env, nullptr);
193  mlock_requested = std::min(user_req, mlock_requested);
194  }
195  catch(std::exception&) { /* ignore it */ }
196  }
197 
198 #if defined(RLIMIT_MEMLOCK)
199  if(mlock_requested > 0)
200  {
201  struct ::rlimit limits;
202 
203  ::getrlimit(RLIMIT_MEMLOCK, &limits);
204 
205  if(limits.rlim_cur < limits.rlim_max)
206  {
207  limits.rlim_cur = limits.rlim_max;
208  ::setrlimit(RLIMIT_MEMLOCK, &limits);
209  ::getrlimit(RLIMIT_MEMLOCK, &limits);
210  }
211 
212  return std::min<size_t>(limits.rlim_cur, mlock_requested * 1024);
213  }
214 #else
215  /*
216  * If RLIMIT_MEMLOCK is not defined, likely the OS does not support
217  * unprivileged mlock calls.
218  */
219  return 0;
220 #endif
221 
222 #elif defined(BOTAN_TARGET_OS_HAS_VIRTUAL_LOCK) && defined(BOTAN_BUILD_COMPILER_IS_MSVC)
223  SIZE_T working_min = 0, working_max = 0;
224  DWORD working_flags = 0;
225  if(!::GetProcessWorkingSetSizeEx(::GetCurrentProcess(), &working_min, &working_max, &working_flags))
226  {
227  return 0;
228  }
229 
230  SYSTEM_INFO sSysInfo;
231  ::GetSystemInfo(&sSysInfo);
232 
233  // According to Microsoft MSDN:
234  // 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
235  // In the book "Windows Internals Part 2": the maximum lockable pages are minimum working set size - 8 pages
236  // But the information in the book seems to be inaccurate/outdated
237  // I've tested this on Windows 8.1 x64, Windows 10 x64 and Windows 7 x86
238  // On all three OS the value is 11 instead of 8
239  size_t overhead = sSysInfo.dwPageSize * 11ULL;
240  if(working_min > overhead)
241  {
242  size_t lockable_bytes = working_min - overhead;
243  if(lockable_bytes < (BOTAN_MLOCK_ALLOCATOR_MAX_LOCKED_KB * 1024ULL))
244  {
245  return lockable_bytes;
246  }
247  else
248  {
249  return BOTAN_MLOCK_ALLOCATOR_MAX_LOCKED_KB * 1024ULL;
250  }
251  }
252 #endif
253 
254  return 0;
255  }
T min(T a, T b)
Definition: ct_utils.h:180
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 31 of file os_utils.cpp.

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

32  {
33 #if defined(BOTAN_TARGET_OS_TYPE_IS_UNIX)
34  return ::getpid();
35 #elif defined(BOTAN_TARGET_OS_IS_WINDOWS) || defined(BOTAN_TARGET_OS_IS_MINGW)
36  return ::GetCurrentProcessId();
37 #elif defined(BOTAN_TARGET_OS_TYPE_IS_UNIKERNEL)
38  return 0; // truly no meaningful value
39 #else
40  #error "Missing get_process_id"
41 #endif
42  }
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 44 of file os_utils.cpp.

Referenced by get_high_resolution_clock().

45  {
46 #if defined(BOTAN_TARGET_OS_HAS_QUERY_PERF_COUNTER)
47  LARGE_INTEGER tv;
48  ::QueryPerformanceCounter(&tv);
49  return tv.QuadPart;
50 
51 #elif defined(BOTAN_USE_GCC_INLINE_ASM)
52 
53 #if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY)
54  if(CPUID::has_rdtsc()) // not available on all x86 CPUs
55  {
56  uint32_t rtc_low = 0, rtc_high = 0;
57  asm volatile("rdtsc" : "=d" (rtc_high), "=a" (rtc_low));
58  return (static_cast<uint64_t>(rtc_high) << 32) | rtc_low;
59  }
60 
61 #elif defined(BOTAN_TARGET_ARCH_IS_PPC64)
62  uint32_t rtc_low = 0, rtc_high = 0;
63  asm volatile("mftbu %0; mftb %1" : "=r" (rtc_high), "=r" (rtc_low));
64 
65  /*
66  qemu-ppc seems to not support mftb instr, it always returns zero.
67  If both time bases are 0, assume broken and return another clock.
68  */
69  if(rtc_high > 0 || rtc_low > 0)
70  {
71  return (static_cast<uint64_t>(rtc_high) << 32) | rtc_low;
72  }
73 
74 #elif defined(BOTAN_TARGET_ARCH_IS_ALPHA)
75  uint64_t rtc = 0;
76  asm volatile("rpcc %0" : "=r" (rtc));
77  return rtc;
78 
79  // OpenBSD does not trap access to the %tick register
80 #elif defined(BOTAN_TARGET_ARCH_IS_SPARC64) && !defined(BOTAN_TARGET_OS_IS_OPENBSD)
81  uint64_t rtc = 0;
82  asm volatile("rd %%tick, %0" : "=r" (rtc));
83  return rtc;
84 
85 #elif defined(BOTAN_TARGET_ARCH_IS_IA64)
86  uint64_t rtc = 0;
87  asm volatile("mov %0=ar.itc" : "=r" (rtc));
88  return rtc;
89 
90 #elif defined(BOTAN_TARGET_ARCH_IS_S390X)
91  uint64_t rtc = 0;
92  asm volatile("stck 0(%0)" : : "a" (&rtc) : "memory", "cc");
93  return rtc;
94 
95 #elif defined(BOTAN_TARGET_ARCH_IS_HPPA)
96  uint64_t rtc = 0;
97  asm volatile("mfctl 16,%0" : "=r" (rtc)); // 64-bit only?
98  return rtc;
99 
100 #else
101  //#warning "OS::get_processor_timestamp not implemented"
102 #endif
103 
104 #endif
105 
106  return 0;
107  }
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 157 of file os_utils.cpp.

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

158  {
159 #if defined(BOTAN_TARGET_OS_HAS_CLOCK_GETTIME)
160  struct timespec ts;
161  if(::clock_gettime(CLOCK_REALTIME, &ts) == 0)
162  {
163  return (static_cast<uint64_t>(ts.tv_sec) * 1000000000) + static_cast<uint64_t>(ts.tv_nsec);
164  }
165 #endif
166 
167  auto now = std::chrono::system_clock::now().time_since_epoch();
168  return std::chrono::duration_cast<std::chrono::nanoseconds>(now).count();
169  }
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 346 of file os_utils.cpp.

347  {
348  volatile int probe_result = -3;
349 
350 #if defined(BOTAN_TARGET_OS_TYPE_IS_UNIX)
351  struct sigaction old_sigaction;
352  struct sigaction sigaction;
353 
354  sigaction.sa_handler = botan_sigill_handler;
355  sigemptyset(&sigaction.sa_mask);
356  sigaction.sa_flags = 0;
357 
358  int rc = ::sigaction(SIGILL, &sigaction, &old_sigaction);
359 
360  if(rc != 0)
361  throw Exception("run_cpu_instruction_probe sigaction failed");
362 
363  rc = ::sigsetjmp(g_sigill_jmp_buf, /*save sigs*/1);
364 
365  if(rc == 0)
366  {
367  // first call to sigsetjmp
368  probe_result = probe_fn();
369  }
370  else if(rc == 1)
371  {
372  // non-local return from siglongjmp in signal handler: return error
373  probe_result = -1;
374  }
375 
376  // Restore old SIGILL handler, if any
377  rc = ::sigaction(SIGILL, &old_sigaction, nullptr);
378  if(rc != 0)
379  throw Exception("run_cpu_instruction_probe sigaction restore failed");
380 
381 #elif defined(BOTAN_TARGET_OS_IS_WINDOWS) && defined(BOTAN_TARGET_COMPILER_IS_MSVC)
382 
383  // Windows SEH
384  __try
385  {
386  probe_result = probe_fn();
387  }
388  __except(::GetExceptionCode() == EXCEPTION_ILLEGAL_INSTRUCTION ?
389  EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
390  {
391  probe_result = -1;
392  }
393 
394 #endif
395 
396  return probe_result;
397  }