diff options
author | Jack Lloyd <[email protected]> | 2017-01-31 20:07:06 -0500 |
---|---|---|
committer | Jack Lloyd <[email protected]> | 2017-01-31 20:07:06 -0500 |
commit | ae8d2bd859120839ee08bc35daebda7ef4971ea6 (patch) | |
tree | cd1a8db67e062854ccd7e9211707b13ee757f64d /src | |
parent | e934e03821ab28dc2573ff52da49a3b700d4cfb7 (diff) | |
parent | 328491ce573582058a4ebdef74d687cbd1466c44 (diff) |
Merge GH #860 Support CPU instruction probe on Windows, add OS::get_high_resolution_clock
Diffstat (limited to 'src')
-rw-r--r-- | src/lib/rng/rng.cpp | 2 | ||||
-rw-r--r-- | src/lib/rng/stateful_rng/stateful_rng.cpp | 2 | ||||
-rw-r--r-- | src/lib/utils/os_utils.cpp | 83 | ||||
-rw-r--r-- | src/lib/utils/os_utils.h | 28 | ||||
-rw-r--r-- | src/tests/test_os_utils.cpp | 64 |
5 files changed, 128 insertions, 51 deletions
diff --git a/src/lib/rng/rng.cpp b/src/lib/rng/rng.cpp index 4551ffe83..57e25cdd2 100644 --- a/src/lib/rng/rng.cpp +++ b/src/lib/rng/rng.cpp @@ -22,7 +22,7 @@ void RandomNumberGenerator::randomize_with_ts_input(uint8_t output[], size_t out */ uint8_t additional_input[16] = { 0 }; store_le(OS::get_system_timestamp_ns(), additional_input); - store_le(OS::get_processor_timestamp(), additional_input + 8); + store_le(OS::get_high_resolution_clock(), additional_input + 8); randomize_with_input(output, output_len, additional_input, sizeof(additional_input)); } diff --git a/src/lib/rng/stateful_rng/stateful_rng.cpp b/src/lib/rng/stateful_rng/stateful_rng.cpp index 81fe89cca..df33a2f54 100644 --- a/src/lib/rng/stateful_rng/stateful_rng.cpp +++ b/src/lib/rng/stateful_rng/stateful_rng.cpp @@ -40,7 +40,7 @@ void Stateful_RNG::randomize_with_ts_input(uint8_t output[], size_t output_len) { uint8_t additional_input[24] = { 0 }; store_le(OS::get_system_timestamp_ns(), additional_input); - store_le(OS::get_processor_timestamp(), additional_input + 8); + store_le(OS::get_high_resolution_clock(), additional_input + 8); store_le(m_last_pid, additional_input + 16); store_le(static_cast<uint32_t>(m_reseed_counter), additional_input + 20); diff --git a/src/lib/utils/os_utils.cpp b/src/lib/utils/os_utils.cpp index c6d99237c..fc401c3c1 100644 --- a/src/lib/utils/os_utils.cpp +++ b/src/lib/utils/os_utils.cpp @@ -28,9 +28,7 @@ namespace Botan { -namespace OS { - -uint32_t get_process_id() +uint32_t OS::get_process_id() { #if defined(BOTAN_TARGET_OS_TYPE_IS_UNIX) return ::getpid(); @@ -43,14 +41,16 @@ uint32_t get_process_id() #endif } -uint64_t get_processor_timestamp() +uint64_t OS::get_processor_timestamp() { #if defined(BOTAN_TARGET_OS_HAS_QUERY_PERF_COUNTER) LARGE_INTEGER tv; ::QueryPerformanceCounter(&tv); return tv.QuadPart; -#elif defined(BOTAN_USE_GCC_INLINE_ASM) && defined(BOTAN_TARGET_CPU_IS_X86_FAMILY) +#elif defined(BOTAN_USE_GCC_INLINE_ASM) + +#if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY) if(CPUID::has_rdtsc()) // not available on all x86 CPUs { uint32_t rtc_low = 0, rtc_high = 0; @@ -58,7 +58,7 @@ uint64_t get_processor_timestamp() return (static_cast<uint64_t>(rtc_high) << 32) | rtc_low; } -#elif defined(BOTAN_USE_GCC_INLINE_ASM) && defined(BOTAN_TARGET_ARCH_IS_PPC64) +#elif defined(BOTAN_TARGET_ARCH_IS_PPC64) uint32_t rtc_low = 0, rtc_high = 0; asm volatile("mftbu %0; mftb %1" : "=r" (rtc_high), "=r" (rtc_low)); @@ -71,33 +71,46 @@ uint64_t get_processor_timestamp() return (static_cast<uint64_t>(rtc_high) << 32) | rtc_low; } -#elif defined(BOTAN_USE_GCC_INLINE_ASM) && defined(BOTAN_TARGET_ARCH_IS_ALPHA) +#elif defined(BOTAN_TARGET_ARCH_IS_ALPHA) uint64_t rtc = 0; asm volatile("rpcc %0" : "=r" (rtc)); return rtc; // OpenBSD does not trap access to the %tick register -#elif defined(BOTAN_USE_GCC_INLINE_ASM) && defined(BOTAN_TARGET_ARCH_IS_SPARC64) && !defined(BOTAN_TARGET_OS_IS_OPENBSD) +#elif defined(BOTAN_TARGET_ARCH_IS_SPARC64) && !defined(BOTAN_TARGET_OS_IS_OPENBSD) uint64_t rtc = 0; asm volatile("rd %%tick, %0" : "=r" (rtc)); return rtc; -#elif defined(BOTAN_USE_GCC_INLINE_ASM) && defined(BOTAN_TARGET_ARCH_IS_IA64) +#elif defined(BOTAN_TARGET_ARCH_IS_IA64) uint64_t rtc = 0; asm volatile("mov %0=ar.itc" : "=r" (rtc)); return rtc; -#elif defined(BOTAN_USE_GCC_INLINE_ASM) && defined(BOTAN_TARGET_ARCH_IS_S390X) +#elif defined(BOTAN_TARGET_ARCH_IS_S390X) uint64_t rtc = 0; asm volatile("stck 0(%0)" : : "a" (&rtc) : "memory", "cc"); return rtc; -#elif defined(BOTAN_USE_GCC_INLINE_ASM) && defined(BOTAN_TARGET_ARCH_IS_HPPA) +#elif defined(BOTAN_TARGET_ARCH_IS_HPPA) uint64_t rtc = 0; asm volatile("mfctl 16,%0" : "=r" (rtc)); // 64-bit only? return rtc; + +#else + //#warning "OS::get_processor_timestamp not implemented" #endif +#endif + + return 0; + } + +uint64_t OS::get_high_resolution_clock() + { + if(uint64_t cpu_clock = OS::get_processor_timestamp()) + return cpu_clock; + /* If we got here either we either don't have an asm instruction above, or (for x86) RDTSC is not available at runtime. Try some @@ -141,7 +154,7 @@ uint64_t get_processor_timestamp() return std::chrono::duration_cast<std::chrono::nanoseconds>(now).count(); } -uint64_t get_system_timestamp_ns() +uint64_t OS::get_system_timestamp_ns() { #if defined(BOTAN_TARGET_OS_HAS_CLOCK_GETTIME) struct timespec ts; @@ -155,7 +168,7 @@ uint64_t get_system_timestamp_ns() return std::chrono::duration_cast<std::chrono::nanoseconds>(now).count(); } -size_t get_memory_locking_limit() +size_t OS::get_memory_locking_limit() { #if defined(BOTAN_TARGET_OS_HAS_POSIX_MLOCK) /* @@ -241,7 +254,7 @@ size_t get_memory_locking_limit() return 0; } -void* allocate_locked_pages(size_t length) +void* OS::allocate_locked_pages(size_t length) { #if defined(BOTAN_TARGET_OS_HAS_POSIX_MLOCK) @@ -298,7 +311,7 @@ void* allocate_locked_pages(size_t length) #endif } -void free_locked_pages(void* ptr, size_t length) +void OS::free_locked_pages(void* ptr, size_t length) { if(ptr == nullptr || length == 0) return; @@ -330,8 +343,10 @@ void botan_sigill_handler(int) } #endif -int run_cpu_instruction_probe(std::function<int ()> probe_fn) +int OS::run_cpu_instruction_probe(std::function<int ()> probe_fn) { + int probe_result = -3; + #if defined(BOTAN_TARGET_OS_TYPE_IS_UNIX) struct sigaction old_sigaction; struct sigaction sigaction; @@ -345,12 +360,6 @@ int run_cpu_instruction_probe(std::function<int ()> probe_fn) if(rc != 0) throw Exception("run_cpu_instruction_probe sigaction failed"); - /* - There doesn't seem to be any way for probe_result to not be initialized - by some code path below, but this initializer is left as error just in case. - */ - int probe_result = -3; - try { rc = ::sigsetjmp(g_sigill_jmp_buf, /*save sigs*/1); @@ -373,18 +382,34 @@ int run_cpu_instruction_probe(std::function<int ()> probe_fn) probe_result = -2; } + // Restore old SIGILL handler, if any rc = ::sigaction(SIGILL, &old_sigaction, nullptr); if(rc != 0) throw Exception("run_cpu_instruction_probe sigaction restore failed"); - return probe_result; -#else - // TODO: Windows support - return -9; // not supported -#endif - } +#elif defined(BOTAN_TARGET_OS_IS_WINDOWS) && defined(BOTAN_TARGET_COMPILER_IS_MSVC) + + // Windows SEH + __try + { + try + { + probe_result = probe_fn(); + } + catch(...) + { + probe_result = -2; + } + } + __except(::GetExceptionCode() == EXCEPTION_ILLEGAL_INSTRUCTION ? + EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) + { + probe_result = -1; + } +#endif -} + return probe_result; + } } diff --git a/src/lib/utils/os_utils.h b/src/lib/utils/os_utils.h index 60ef2cf5b..a1693bcc5 100644 --- a/src/lib/utils/os_utils.h +++ b/src/lib/utils/os_utils.h @@ -32,23 +32,31 @@ namespace OS { uint32_t BOTAN_DLL get_process_id(); /** -* @return highest resolution clock available on the system. +* @return 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. +*/ +uint64_t BOTAN_DLL get_processor_timestamp(); + +/* +* @return best resolution timestamp available * * The epoch and update rate of this clock is arbitrary and depending * on the hardware it may not tick at a constant rate. * * Uses hardware cycle counter, if available. -* On Windows, always calls QueryPerformanceCounter. -* Under GCC or Clang on supported platforms the hardware cycle counter is queried: -* x86, PPC, Alpha, SPARC, IA-64, S/390x, and HP-PA -* On other platforms clock_gettime is used with some monotonic timer, if available. +* On POSIX platforms clock_gettime is used with a monotonic timer * As a final fallback std::chrono::high_resolution_clock is used. */ -uint64_t BOTAN_DLL get_processor_timestamp(); +uint64_t BOTAN_DLL get_high_resolution_clock(); /** -* @return system clock with best resolution available, normalized to -* nanoseconds resolution. +* @return system clock (reflecting wall clock) with best resolution +* available, normalized to nanoseconds resolution. */ uint64_t BOTAN_DLL get_system_timestamp_ns(); @@ -86,6 +94,10 @@ void free_locked_pages(void* ptr, size_t length); * 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). +* * Return codes: * -1 illegal instruction detected * -2 exception thrown diff --git a/src/tests/test_os_utils.cpp b/src/tests/test_os_utils.cpp index bdcbf2a90..58858a4c2 100644 --- a/src/tests/test_os_utils.cpp +++ b/src/tests/test_os_utils.cpp @@ -8,6 +8,11 @@ #include "tests.h" #include <botan/internal/os_utils.h> +// For __ud2 intrinsic +#if defined(BOTAN_TARGET_COMPILER_IS_MSVC) + #include <intrin.h> +#endif + namespace Botan_Tests { namespace { @@ -31,6 +36,7 @@ class OS_Utils_Tests : public Test results.push_back(test_get_process_id()); results.push_back(test_get_processor_timestamp()); + results.push_back(test_get_high_resolution_clock()); results.push_back(test_get_system_timestamp()); results.push_back(test_memory_locking()); results.push_back(test_cpu_instruction_probe()); @@ -60,22 +66,45 @@ class OS_Utils_Tests : public Test Test::Result test_get_processor_timestamp() { + // TODO better tests Test::Result result("OS::get_processor_timestamp"); - uint64_t proc_ts1 = Botan::OS::get_processor_timestamp(); - result.test_ne("Processor timestamp value is never zero", proc_ts1, 0); + const uint64_t proc_ts1 = Botan::OS::get_processor_timestamp(); // do something that consumes a little time Botan::OS::get_process_id(); uint64_t proc_ts2 = Botan::OS::get_processor_timestamp(); - result.test_ne("Processor timestamp does not duplicate", proc_ts1, proc_ts2); + if(proc_ts1 == 0) + result.test_is_eq("Disabled processor timestamp stays at zero", proc_ts1, proc_ts2); + else + result.confirm("Processor timestamp does not duplicate", proc_ts1 != proc_ts2); + + return result; + } + + Test::Result test_get_high_resolution_clock() + { + // TODO better tests + + Test::Result result("OS::get_high_resolution_clock"); + + uint64_t hr_ts1 = Botan::OS::get_high_resolution_clock(); + result.test_ne("high resolution timestamp value is never zero", hr_ts1, 0); + + // do something that consumes a little time + Botan::OS::get_process_id(); + + uint64_t hr_ts2 = Botan::OS::get_high_resolution_clock(); + + result.test_ne("high resolution timestamp does not duplicate", hr_ts1, hr_ts2); return result; } Test::Result test_get_system_timestamp() { + // TODO better tests Test::Result result("OS::get_system_timestamp_ns"); uint64_t sys_ts1 = Botan::OS::get_system_timestamp_ns(); @@ -89,13 +118,14 @@ class OS_Utils_Tests : public Test result.confirm("System time moves forward", sys_ts1 <= sys_ts2); return result; - - return result; } Test::Result test_memory_locking() { Test::Result result("OS memory locked pages"); + + // TODO any tests... + return result; } @@ -103,27 +133,38 @@ class OS_Utils_Tests : public Test { Test::Result result("OS::run_cpu_instruction_probe"); -#if defined(BOTAN_TARGET_OS_TYPE_IS_UNIX) - // OS::run_cpu_instruction_probe only implemented for Unix signals right now + // OS::run_cpu_instruction_probe only implemented for Unix signals or Windows SEH std::function<int ()> ok_fn = []() -> int { return 5; }; const int run_rc = Botan::OS::run_cpu_instruction_probe(ok_fn); + + if(run_rc == -3) + { + result.test_note("run_cpu_instruction_probe not implemented on this platform"); + return {result}; + } + result.confirm("Correct result returned by working probe fn", run_rc == 5); std::function<int ()> throw_fn = []() -> int { throw 3.14159; return 5; }; const int throw_rc = Botan::OS::run_cpu_instruction_probe(throw_fn); result.confirm("Error return if probe function threw exception", throw_rc < 0); -#if defined(BOTAN_USE_GCC_INLINE_ASM) - std::function<int ()> crash_probe; +#if defined(BOTAN_TARGET_COMPILER_IS_MSVC) + crash_probe = []() -> int { __ud2(); return 3; }; + +#elif defined(BOTAN_USE_GCC_INLINE_ASM) + #if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY) crash_probe = []() -> int { asm volatile("ud2"); return 3; }; + #elif defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY) //ARM: asm volatile (".word 0xf7f0a000\n"); // illegal instruction in both ARM and Thumb modes crash_probe = []() -> int { asm volatile(".word 0xe7f0def0\n"); return 3; }; + #else /* PPC: "The instruction with primary opcode 0, when the instruction does not consist @@ -132,14 +173,13 @@ class OS_Utils_Tests : public Test */ #endif +#endif + if(crash_probe) { const int crash_rc = Botan::OS::run_cpu_instruction_probe(crash_probe); result.confirm("Result for function executing undefined opcode", crash_rc < 0); } -#endif - -#endif return result; } |