diff --git a/libs/execution_context/ebpf_core.c b/libs/execution_context/ebpf_core.c index 0f27a08592..ca235eed1d 100644 --- a/libs/execution_context/ebpf_core.c +++ b/libs/execution_context/ebpf_core.c @@ -13,6 +13,7 @@ #include "ebpf_native.h" #include "ebpf_pinning_table.h" #include "ebpf_program.h" +#include "ebpf_random.h" #include "ebpf_serialize.h" #include "ebpf_state.h" #include "ebpf_tracelog.h" @@ -181,6 +182,11 @@ ebpf_core_initiate() goto Done; } + return_value = ebpf_random_initiate(); + if (return_value != EBPF_SUCCESS) { + goto Done; + } + return_value = ebpf_trace_initiate(); if (return_value != EBPF_SUCCESS) { goto Done; @@ -286,6 +292,8 @@ ebpf_core_terminate() ebpf_trace_terminate(); + ebpf_random_terminate(); + ebpf_platform_terminate(); } diff --git a/libs/platform/CMakeLists.txt b/libs/platform/CMakeLists.txt index bd0d88b19e..74cb3d332c 100644 --- a/libs/platform/CMakeLists.txt +++ b/libs/platform/CMakeLists.txt @@ -46,6 +46,9 @@ set(platform_sources ebpf_interlocked.c ebpf_serialize.c ebpf_trampoline.c + + ebpf_random.c + ebpf_random.h ) set(platform_kernel_include_dirs diff --git a/libs/platform/ebpf_hash_table.c b/libs/platform/ebpf_hash_table.c index 6098c8e622..c2bd201815 100644 --- a/libs/platform/ebpf_hash_table.c +++ b/libs/platform/ebpf_hash_table.c @@ -3,6 +3,7 @@ #include "ebpf_epoch.h" #include "ebpf_hash_table.h" +#include "ebpf_random.h" // Buckets contain an array of pointers to value and keys. // Buckets are immutable once inserted in to the hash-table and replaced when diff --git a/libs/platform/ebpf_platform.c b/libs/platform/ebpf_platform.c index 0fc715f3a6..691e34f3ea 100644 --- a/libs/platform/ebpf_platform.c +++ b/libs/platform/ebpf_platform.c @@ -362,14 +362,6 @@ ebpf_safe_size_t_subtract( return RtlSizeTSub(minuend, subtrahend, result) == STATUS_SUCCESS ? EBPF_SUCCESS : EBPF_ARITHMETIC_OVERFLOW; } -uint32_t -ebpf_random_uint32() -{ - LARGE_INTEGER p = KeQueryPerformanceCounter(NULL); - unsigned long seed = p.LowPart ^ (unsigned long)p.HighPart; - return RtlRandomEx(&seed); -} - ebpf_result_t ebpf_utf8_string_to_unicode(_In_ const ebpf_utf8_string_t* input, _Outptr_ wchar_t** output) { diff --git a/libs/platform/ebpf_platform.h b/libs/platform/ebpf_platform.h index a18d9a09f1..e2837afc72 100644 --- a/libs/platform/ebpf_platform.h +++ b/libs/platform/ebpf_platform.h @@ -780,14 +780,6 @@ extern "C" ebpf_validate_security_descriptor( _In_ const ebpf_security_descriptor_t* security_descriptor, size_t security_descriptor_length); - /** - * @brief Return a pseudorandom number. - * - * @return A pseudorandom number. - */ - uint32_t - ebpf_random_uint32(); - /** * @brief Return time elapsed since boot in units of 100 nanoseconds. * diff --git a/libs/platform/ebpf_random.c b/libs/platform/ebpf_random.c new file mode 100644 index 0000000000..f266a703ad --- /dev/null +++ b/libs/platform/ebpf_random.c @@ -0,0 +1,62 @@ +// Copyright (c) Microsoft Corporation +// SPDX-License-Identifier: MIT + +#include "ebpf_platform.h" +#include "ebpf_random.h" + +// Pointer to cache aligned array of random number generator state. + +typedef __declspec(align(EBPF_CACHE_LINE_SIZE)) struct _ebpf_random_state +{ + uint64_t state; + uint8_t _padding[EBPF_CACHE_LINE_SIZE - sizeof(uint64_t)]; +} ebpf_random_state_t; + +static volatile ebpf_random_state_t* _ebpf_random_number_generator_state = NULL; + +_Must_inspect_result_ ebpf_result_t +ebpf_random_initiate() +{ + LARGE_INTEGER p = KeQueryPerformanceCounter(NULL); + unsigned long seed = p.LowPart ^ (unsigned long)p.HighPart; + + uint32_t cpu_count = ebpf_get_cpu_count(); + size_t state_size = cpu_count * sizeof(ebpf_random_state_t); + _ebpf_random_number_generator_state = ebpf_allocate_cache_aligned(state_size); + if (_ebpf_random_number_generator_state == NULL) { + return EBPF_NO_MEMORY; + } + for (uint32_t i = 0; i < cpu_count; i++) { + _ebpf_random_number_generator_state[i].state = RtlRandomEx(&seed); + } + return EBPF_SUCCESS; +} + +void +ebpf_random_terminate() +{ + ebpf_free_cache_aligned((void*)_ebpf_random_number_generator_state); + _ebpf_random_number_generator_state = NULL; +} + +#define LCG_MULTIPLIER ((uint64_t)1664525) +#define LCG_OFFSET ((uint64_t)1013904223) +#define LCG_MODULUS ((uint64_t)4294967296) + +// Implement a linear congruential random number generator. +// x(n+1) = (a * x(n) + c) % m +// where a = 1664525, c = 1013904223, and m = 4294967296 +// See: https://www.researchgate.net/publication/220420979_Random_Number_Generators_Good_Ones_Are_Hard_to_Find +// for more details. +// Note the linear congruential random number generator is not cryptographically secure. +// The values for a, c, and m are chosen to be fast to compute on 32-bit processors and to also have a long period. +uint32_t +ebpf_random_uint32() +{ + uint32_t cpu_index = ebpf_get_current_cpu(); + volatile ebpf_random_state_t* state = &_ebpf_random_number_generator_state[cpu_index]; + uint64_t state64 = state->state; + uint64_t next_state64 = (state64 * LCG_MULTIPLIER + LCG_OFFSET) % LCG_MODULUS; + state->state = next_state64; + return (uint32_t)next_state64; +} \ No newline at end of file diff --git a/libs/platform/ebpf_random.h b/libs/platform/ebpf_random.h new file mode 100644 index 0000000000..dae44a7764 --- /dev/null +++ b/libs/platform/ebpf_random.h @@ -0,0 +1,37 @@ +// Copyright (c) Microsoft Corporation +// SPDX-License-Identifier: MIT + +#include "ebpf_platform.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + /** + * @brief Initialize the random number generator. + * + * @retval EBPF_SUCCESS The operation was successful. + * @retval EBPF_NO_MEMORY Unable to allocate resources for this + * operation. + */ + _Must_inspect_result_ ebpf_result_t + ebpf_random_initiate(); + + /** + * @brief Terminate the random number generator. + */ + void + ebpf_random_terminate(); + + /** + * @brief Return a pseudorandom number. + * + * @return A pseudorandom number. + */ + uint32_t + ebpf_random_uint32(); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/libs/platform/kernel/platform_kernel.vcxproj b/libs/platform/kernel/platform_kernel.vcxproj index b92d7dd5f7..e6b36b0378 100644 --- a/libs/platform/kernel/platform_kernel.vcxproj +++ b/libs/platform/kernel/platform_kernel.vcxproj @@ -29,6 +29,7 @@ + @@ -57,6 +58,7 @@ + diff --git a/libs/platform/kernel/platform_kernel.vcxproj.filters b/libs/platform/kernel/platform_kernel.vcxproj.filters index 6f4a164957..a90cf7f575 100644 --- a/libs/platform/kernel/platform_kernel.vcxproj.filters +++ b/libs/platform/kernel/platform_kernel.vcxproj.filters @@ -73,7 +73,10 @@ Source Files - + + Source Files + + Source Files @@ -135,5 +138,8 @@ Header Files + + Header Files + \ No newline at end of file diff --git a/libs/platform/unit/platform_unit_test.cpp b/libs/platform/unit/platform_unit_test.cpp index 2e230ec253..f7d501b9cd 100644 --- a/libs/platform/unit/platform_unit_test.cpp +++ b/libs/platform/unit/platform_unit_test.cpp @@ -13,6 +13,7 @@ #include "ebpf_pinning_table.h" #include "ebpf_platform.h" #include "ebpf_program_types.h" +#include "ebpf_random.h" #include "ebpf_ring_buffer.h" #include "ebpf_serialize.h" #include "ebpf_state.h" @@ -64,6 +65,7 @@ class _test_helper { REQUIRE(ebpf_platform_initiate() == EBPF_SUCCESS); platform_initiated = true; + REQUIRE(ebpf_random_initiate() == EBPF_SUCCESS); REQUIRE(ebpf_epoch_initiate() == EBPF_SUCCESS); epoch_initiated = true; REQUIRE(ebpf_async_initiate() == EBPF_SUCCESS); @@ -83,6 +85,7 @@ class _test_helper ebpf_epoch_flush(); ebpf_epoch_terminate(); } + ebpf_random_terminate(); if (platform_initiated) { ebpf_platform_terminate(); } @@ -118,6 +121,9 @@ TEST_CASE("hash_table_test", "[platform]") uint8_t* returned_value = nullptr; std::vector returned_key(13); + _test_helper test_helper; + test_helper.initialize(); + for (auto& v : key_1) { v = static_cast(ebpf_random_uint32()); } @@ -1063,3 +1069,40 @@ TEST_CASE("get_authentication_id", "[platform]") REQUIRE(ebpf_platform_get_authentication_id(&authentication_id) == EBPF_SUCCESS); } + +// See https://en.wikipedia.org/wiki/Chi-squared_test for details. +#define SEQUENCE_LENGTH 100000000 +#define NUM_BINS 65536 +#define CHI_SQUARED_STATISTIC_THRESHOLD \ + 66131.63094 // Critical value for Chi-squared test with 65535 degrees of freedom with significance level of 0.05. + +bool +is_statistically_random(size_t sequence_length, std::function random_number_generator) +{ + std::vector observed_values(NUM_BINS, 0); + double expected_value = static_cast(sequence_length) / static_cast(NUM_BINS); + + for (int i = 0; i < sequence_length; i++) { + int bin = static_cast(random_number_generator() % NUM_BINS); + observed_values[bin]++; + } + + double chi_squared_statistic = 0.0; + for (int i = 0; i < NUM_BINS; i++) { + double observed = static_cast(observed_values[i]); + chi_squared_statistic += pow(observed - expected_value, 2) / expected_value; + } + + double critical_value = CHI_SQUARED_STATISTIC_THRESHOLD; + std::cout << chi_squared_statistic << std::endl; + return chi_squared_statistic < critical_value; +} + +TEST_CASE("verify random", "[platform]") +{ + _test_helper test_helper; + test_helper.initialize(); + + // Verify that the random number generator is statistically random. + REQUIRE(is_statistically_random(SEQUENCE_LENGTH, ebpf_random_uint32)); +} \ No newline at end of file diff --git a/libs/platform/user/platform_user.vcxproj b/libs/platform/user/platform_user.vcxproj index 71c62c0213..2463c47016 100644 --- a/libs/platform/user/platform_user.vcxproj +++ b/libs/platform/user/platform_user.vcxproj @@ -25,6 +25,7 @@ + @@ -44,6 +45,7 @@ + diff --git a/libs/platform/user/platform_user.vcxproj.filters b/libs/platform/user/platform_user.vcxproj.filters index 4edea359de..540d84249c 100644 --- a/libs/platform/user/platform_user.vcxproj.filters +++ b/libs/platform/user/platform_user.vcxproj.filters @@ -76,6 +76,9 @@ Source Files + + Source Files + @@ -111,5 +114,8 @@ Header Files + + Header Files + \ No newline at end of file diff --git a/tests/performance/performance.h b/tests/performance/performance.h index c8d057c0ca..c8bb3c5368 100644 --- a/tests/performance/performance.h +++ b/tests/performance/performance.h @@ -10,6 +10,7 @@ #include "ebpf_maps.h" #include "ebpf_object.h" #include "ebpf_program.h" +#include "ebpf_random.h" #include "helpers.h" #include "performance_measure.h" diff --git a/tests/performance/platform.cpp b/tests/performance/platform.cpp index 4abc311b7b..0879e599b8 100644 --- a/tests/performance/platform.cpp +++ b/tests/performance/platform.cpp @@ -75,6 +75,7 @@ typedef class _ebpf_hash_table_test_state cpu_count = ebpf_get_cpu_count(); REQUIRE(ebpf_platform_initiate() == EBPF_SUCCESS); platform_initiated = true; + REQUIRE(ebpf_random_initiate() == EBPF_SUCCESS); REQUIRE(ebpf_epoch_initiate() == EBPF_SUCCESS); epoch_initiated = true; @@ -106,6 +107,7 @@ typedef class _ebpf_hash_table_test_state if (epoch_initiated) { ebpf_epoch_terminate(); } + ebpf_random_terminate(); if (platform_initiated) { ebpf_platform_terminate(); } diff --git a/tools/bpf2c/templates/kernel_mode_bpf2c.vcxproj b/tools/bpf2c/templates/kernel_mode_bpf2c.vcxproj index cabda817d7..b81747318f 100644 --- a/tools/bpf2c/templates/kernel_mode_bpf2c.vcxproj +++ b/tools/bpf2c/templates/kernel_mode_bpf2c.vcxproj @@ -41,7 +41,7 @@ $(LatestTargetPlatformVersion) $(FileName)_km - true + true true