Botan 2.1.0 Crypto and TLS for C++11

- Main Page
- Namespaces
- Classes
- Files

- Namespace List
- Namespace Members

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

  length | requested 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 }
BOTAN_UNUSED
#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

  ptr | a pointer returned by allocate_locked_pages
  length | length 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 }
Botan::secure_scrub_memory
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 }
Botan::OS::get_processor_timestamp
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 }
Botan::CT::min
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 }