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