aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJack Lloyd <[email protected]>2017-01-31 20:07:06 -0500
committerJack Lloyd <[email protected]>2017-01-31 20:07:06 -0500
commitae8d2bd859120839ee08bc35daebda7ef4971ea6 (patch)
treecd1a8db67e062854ccd7e9211707b13ee757f64d
parente934e03821ab28dc2573ff52da49a3b700d4cfb7 (diff)
parent328491ce573582058a4ebdef74d687cbd1466c44 (diff)
Merge GH #860 Support CPU instruction probe on Windows, add OS::get_high_resolution_clock
-rw-r--r--src/lib/rng/rng.cpp2
-rw-r--r--src/lib/rng/stateful_rng/stateful_rng.cpp2
-rw-r--r--src/lib/utils/os_utils.cpp83
-rw-r--r--src/lib/utils/os_utils.h28
-rw-r--r--src/tests/test_os_utils.cpp64
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;
}