Botan 2.1.0 Crypto and TLS for C++11

- Main Page
- Namespaces
- Classes
- Files

- File List
- File Members

os_utils.cpp
Go to the documentation of this file.
1 /*
2 * OS and machine specific utility functions
3 * (C) 2015,2016 Jack Lloyd
4 * (C) 2016 Daniel Neus
5 *
6 * Botan is released under the Simplified BSD License (see license.txt)
7 */
8
9 #include <botan/internal/os_utils.h>
10 #include <botan/cpuid.h>
11 #include <botan/exceptn.h>
12 #include <botan/mem_ops.h>
13 #include <chrono>
14
15 #if defined(BOTAN_TARGET_OS_TYPE_IS_UNIX)
16 #include <sys/types.h>
17 #include <sys/mman.h>
18 #include <sys/resource.h>
19 #include <unistd.h>
20 #include <signal.h>
21 #include <setjmp.h>
22 #endif
23
24 #if defined(BOTAN_TARGET_OS_IS_WINDOWS) || defined(BOTAN_TARGET_OS_IS_MINGW)
25 #define NOMINMAX 1
26 #include <windows.h>
27 #endif
28
29 namespace Botan {
30
31 uint32_t OS::get_process_id()
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 }
43
44 uint64_t OS::get_processor_timestamp()
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 }
108
109 uint64_t OS::get_high_resolution_clock()
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 }
156
157 uint64_t OS::get_system_timestamp_ns()
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 }
170
171 size_t OS::get_memory_locking_limit()
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 }
256
257 void* OS::allocate_locked_pages(size_t length)
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 }
313
314 void OS::free_locked_pages(void* ptr, size_t length)
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 }
332
333 #if defined(BOTAN_TARGET_OS_TYPE_IS_UNIX)
334 namespace {
335
336 static ::sigjmp_buf g_sigill_jmp_buf;
337
338 void botan_sigill_handler(int)
339 {
340 ::siglongjmp(g_sigill_jmp_buf, /*non-zero return value*/1);
341 }
342
343 }
344 #endif
345
346 int OS::run_cpu_instruction_probe(std::function<int ()> probe_fn)
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 }
398
399 }
Botan::secure_scrub_memory
void secure_scrub_memory(void *ptr, size_t n)
Definition: mem_ops.cpp:17
Botan::Invalid_Argument
Definition: exceptn.h:34
Botan::Exception
Definition: exceptn.h:21
Botan::OS::run_cpu_instruction_probe
int BOTAN_DLL run_cpu_instruction_probe(std::function< int()> probe_fn)
Definition: os_utils.cpp:346
Botan::OS::get_process_id
uint32_t BOTAN_DLL get_process_id()
Definition: os_utils.cpp:31
Botan::OS::allocate_locked_pages
void * allocate_locked_pages(size_t length)
Definition: os_utils.cpp:257
Botan::OS::get_memory_locking_limit
size_t get_memory_locking_limit()
Definition: os_utils.cpp:171
Botan::OS::get_processor_timestamp
uint64_t BOTAN_DLL get_processor_timestamp()
Definition: os_utils.cpp:44
BOTAN_UNUSED
#define BOTAN_UNUSED(v)
Definition: assert.h:92
Botan::OS::get_system_timestamp_ns
uint64_t BOTAN_DLL get_system_timestamp_ns()
Definition: os_utils.cpp:157
Botan
Definition: alg_id.cpp:13
Botan::CT::min
T min(T a, T b)
Definition: ct_utils.h:180
Botan::OS::free_locked_pages
void free_locked_pages(void *ptr, size_t length)
Definition: os_utils.cpp:314
Botan::OS::get_high_resolution_clock
uint64_t BOTAN_DLL get_high_resolution_clock()
Definition: os_utils.cpp:109