1 /* 2 * Testing operating system specific wrapper code 3 * (C) 2017 Jack Lloyd 4 * 5 * Botan is released under the Simplified BSD License (see license.txt) 6 */ 7 8 #include "tests.h" 9 #include <botan/internal/os_utils.h> 10 11 // For __ud2 intrinsic 12 #if defined(BOTAN_TARGET_COMPILER_IS_MSVC) 13 #include <intrin.h> 14 #endif 15 16 namespace Botan_Tests { 17 18 namespace { 19 20 /* 21 uint32_t get_process_id(); 22 uint64_t get_cpu_cycle_counter(); 23 uint64_t get_system_timestamp_ns(); 24 size_t get_memory_locking_limit(); 25 void* allocate_locked_pages(size_t length); 26 void free_locked_pages(void* ptr, size_t length); 27 int run_cpu_instruction_probe(std::function<int ()> probe_fn); 28 */ 29 30 class OS_Utils_Tests final : public Test 31 { 32 public: run()33 std::vector<Test::Result> run() override 34 { 35 std::vector<Test::Result> results; 36 37 results.push_back(test_get_process_id()); 38 results.push_back(test_get_cpu_cycle_counter()); 39 results.push_back(test_get_high_resolution_clock()); 40 results.push_back(test_get_cpu_numbers()); 41 results.push_back(test_get_system_timestamp()); 42 results.push_back(test_memory_locking()); 43 results.push_back(test_cpu_instruction_probe()); 44 45 return results; 46 } 47 48 private: 49 test_get_process_id()50 Test::Result test_get_process_id() 51 { 52 Test::Result result("OS::get_process_id"); 53 54 uint32_t pid1 = Botan::OS::get_process_id(); 55 uint32_t pid2 = Botan::OS::get_process_id(); 56 57 result.test_eq("PID same across calls", static_cast<size_t>(pid1), static_cast<size_t>(pid2)); 58 59 #if defined(BOTAN_TARGET_OS_IS_INCLUDEOS) || defined(BOTAN_TARGET_OS_IS_LLVM) 60 result.test_eq("PID is expected to be zero on this platform", pid1, size_t(0)); 61 #else 62 result.test_ne("PID is non-zero on systems with processes", pid1, 0); 63 #endif 64 65 return result; 66 } 67 test_get_cpu_cycle_counter()68 Test::Result test_get_cpu_cycle_counter() 69 { 70 const size_t max_trials = 1024; 71 const size_t max_repeats = 32; 72 73 Test::Result result("OS::get_cpu_cycle_counter"); 74 75 const uint64_t proc_ts1 = Botan::OS::get_cpu_cycle_counter(); 76 77 if(proc_ts1 == 0) 78 { 79 const uint64_t proc_ts2 = Botan::OS::get_cpu_cycle_counter(); 80 result.test_is_eq("Disabled processor timestamp stays at zero", proc_ts1, proc_ts2); 81 return result; 82 } 83 84 size_t counts = 0; 85 while(counts < max_trials && (Botan::OS::get_cpu_cycle_counter() == proc_ts1)) 86 ++counts; 87 88 result.test_lt("CPU cycle counter eventually changes value", counts, max_repeats); 89 90 return result; 91 } 92 test_get_high_resolution_clock()93 Test::Result test_get_high_resolution_clock() 94 { 95 const size_t max_trials = 1024; 96 const size_t max_repeats = 128; 97 98 Test::Result result("OS::get_high_resolution_clock"); 99 100 // TODO better tests 101 const uint64_t hr_ts1 = Botan::OS::get_high_resolution_clock(); 102 result.confirm("high resolution timestamp value is never zero", hr_ts1 != 0); 103 104 size_t counts = 0; 105 while(counts < max_trials && (Botan::OS::get_high_resolution_clock() == hr_ts1)) 106 ++counts; 107 108 result.test_lt("high resolution clock eventually changes value", counts, max_repeats); 109 110 return result; 111 } 112 test_get_cpu_numbers()113 Test::Result test_get_cpu_numbers() 114 { 115 Test::Result result("OS::get_cpu_total/OS::get_cpu_available"); 116 117 size_t tt = Botan::OS::get_cpu_total(); 118 size_t ta = Botan::OS::get_cpu_available(); 119 120 result.test_lte("get_cpu_available not greater than total", ta, tt); 121 122 return result; 123 } 124 test_get_system_timestamp()125 Test::Result test_get_system_timestamp() 126 { 127 // TODO better tests 128 Test::Result result("OS::get_system_timestamp_ns"); 129 130 uint64_t sys_ts1 = Botan::OS::get_system_timestamp_ns(); 131 result.confirm("System timestamp value is never zero", sys_ts1 != 0); 132 133 // do something that consumes a little time 134 Botan::OS::get_process_id(); 135 136 uint64_t sys_ts2 = Botan::OS::get_system_timestamp_ns(); 137 138 result.confirm("System time moves forward", sys_ts1 <= sys_ts2); 139 140 return result; 141 } 142 test_memory_locking()143 Test::Result test_memory_locking() 144 { 145 Test::Result result("OS memory locked pages"); 146 147 // TODO any tests... 148 149 return result; 150 } 151 test_cpu_instruction_probe()152 Test::Result test_cpu_instruction_probe() 153 { 154 Test::Result result("OS::run_cpu_instruction_probe"); 155 156 // OS::run_cpu_instruction_probe only implemented for Unix signals or Windows SEH 157 158 std::function<int ()> ok_fn = []() noexcept -> int { return 5; }; 159 const int run_rc = Botan::OS::run_cpu_instruction_probe(ok_fn); 160 161 if(run_rc == -3) 162 { 163 result.test_note("run_cpu_instruction_probe not implemented on this platform"); 164 return {result}; 165 } 166 167 result.confirm("Correct result returned by working probe fn", run_rc == 5); 168 169 std::function<int ()> crash_probe; 170 171 #if defined(BOTAN_TARGET_COMPILER_IS_MSVC) 172 crash_probe = []() noexcept -> int { __ud2(); return 3; }; 173 174 #elif defined(BOTAN_USE_GCC_INLINE_ASM) 175 176 #if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY) 177 crash_probe = []() noexcept -> int { asm volatile("ud2"); return 3; }; 178 179 #elif defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY) 180 //ARM: asm volatile (".word 0xf7f0a000\n"); 181 // illegal instruction in both ARM and Thumb modes 182 crash_probe = []() noexcept -> int { asm volatile(".word 0xe7f0def0\n"); return 3; }; 183 184 #else 185 /* 186 PPC: "The instruction with primary opcode 0, when the instruction does not consist 187 entirely of binary zeros" 188 Others ? 189 */ 190 #endif 191 192 #endif 193 194 if(crash_probe) 195 { 196 const int crash_rc = Botan::OS::run_cpu_instruction_probe(crash_probe); 197 result.confirm("Result for function executing undefined opcode", crash_rc < 0); 198 } 199 200 return result; 201 } 202 }; 203 204 BOTAN_REGISTER_TEST("utils", "os_utils", OS_Utils_Tests); 205 206 } 207 208 } 209