Skip to content

Commit

Permalink
Implement bpf_get_prandom_u32 using linear congruential generator (#2761
Browse files Browse the repository at this point in the history
)

* Implment bpf_get_prandom_u32 using linear congruential generator

Signed-off-by: Alan Jowett <alan.jowett@microsoft.com>

* Add chi-squared test for randomness

Signed-off-by: Alan Jowett <alan.jowett@microsoft.com>

* Fix test failure

Signed-off-by: Alan Jowett <alan.jowett@microsoft.com>

* PR feedback

Signed-off-by: Alan Jowett <alan.jowett@microsoft.com>

---------

Signed-off-by: Alan Jowett <alan.jowett@microsoft.com>
Co-authored-by: Alan Jowett <alan.jowett@microsoft.com>
  • Loading branch information
Alan-Jowett and Alan Jowett committed Aug 17, 2023
1 parent 47590e4 commit e57858f
Show file tree
Hide file tree
Showing 15 changed files with 175 additions and 18 deletions.
8 changes: 8 additions & 0 deletions libs/execution_context/ebpf_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -286,6 +292,8 @@ ebpf_core_terminate()

ebpf_trace_terminate();

ebpf_random_terminate();

ebpf_platform_terminate();
}

Expand Down
3 changes: 3 additions & 0 deletions libs/platform/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions libs/platform/ebpf_hash_table.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 0 additions & 8 deletions libs/platform/ebpf_platform.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down
8 changes: 0 additions & 8 deletions libs/platform/ebpf_platform.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Expand Down
62 changes: 62 additions & 0 deletions libs/platform/ebpf_random.c
Original file line number Diff line number Diff line change
@@ -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;
}
37 changes: 37 additions & 0 deletions libs/platform/ebpf_random.h
Original file line number Diff line number Diff line change
@@ -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
2 changes: 2 additions & 0 deletions libs/platform/kernel/platform_kernel.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
<ClCompile Include="..\ebpf_epoch.c" />
<ClCompile Include="..\ebpf_error.c" />
<ClCompile Include="..\ebpf_platform.c" />
<ClCompile Include="..\ebpf_random.c" />
<ClCompile Include="..\ebpf_tracelog.c" />
<ClCompile Include="..\ebpf_hash_table.c" />
<ClCompile Include="..\ebpf_interlocked.c" />
Expand Down Expand Up @@ -57,6 +58,7 @@
<ClInclude Include="..\ebpf_object.h" />
<ClInclude Include="..\ebpf_pinning_table.h" />
<ClInclude Include="..\ebpf_platform.h" />
<ClInclude Include="..\ebpf_random.h" />
<ClInclude Include="..\ebpf_ring_buffer.h" />
<ClInclude Include="..\ebpf_serialize.h" />
<ClInclude Include="..\ebpf_state.h" />
Expand Down
8 changes: 7 additions & 1 deletion libs/platform/kernel/platform_kernel.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,10 @@
<ClCompile Include="..\ebpf_platform.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include=".\ebpf_fault_injection_kernel.c">
<ClCompile Include="ebpf_fault_injection_kernel.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\ebpf_random.c">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
Expand Down Expand Up @@ -135,5 +138,8 @@
<ClInclude Include="..\ebpf_platform.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\ebpf_random.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
</Project>
43 changes: 43 additions & 0 deletions libs/platform/unit/platform_unit_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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);
Expand All @@ -83,6 +85,7 @@ class _test_helper
ebpf_epoch_flush();
ebpf_epoch_terminate();
}
ebpf_random_terminate();
if (platform_initiated) {
ebpf_platform_terminate();
}
Expand Down Expand Up @@ -118,6 +121,9 @@ TEST_CASE("hash_table_test", "[platform]")
uint8_t* returned_value = nullptr;
std::vector<uint8_t> returned_key(13);

_test_helper test_helper;
test_helper.initialize();

for (auto& v : key_1) {
v = static_cast<uint8_t>(ebpf_random_uint32());
}
Expand Down Expand Up @@ -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<uint32_t()> random_number_generator)
{
std::vector<int> observed_values(NUM_BINS, 0);
double expected_value = static_cast<double>(sequence_length) / static_cast<double>(NUM_BINS);

for (int i = 0; i < sequence_length; i++) {
int bin = static_cast<int>(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<double>(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));
}
2 changes: 2 additions & 0 deletions libs/platform/user/platform_user.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
<ClCompile Include="..\ebpf_epoch.c" />
<ClCompile Include="..\ebpf_error.c" />
<ClCompile Include="..\ebpf_platform.c" />
<ClCompile Include="..\ebpf_random.c" />
<ClCompile Include="..\ebpf_tracelog.c" />
<ClCompile Include="..\ebpf_hash_table.c" />
<ClCompile Include="..\ebpf_interlocked.c" />
Expand All @@ -44,6 +45,7 @@
<ClInclude Include="..\ebpf_object.h" />
<ClInclude Include="..\ebpf_pinning_table.h" />
<ClInclude Include="..\ebpf_platform.h" />
<ClInclude Include="..\ebpf_random.h" />
<ClInclude Include="..\ebpf_ring_buffer.h" />
<ClInclude Include="..\ebpf_state.h" />
<ClInclude Include="..\ebpf_tracelog.h" />
Expand Down
6 changes: 6 additions & 0 deletions libs/platform/user/platform_user.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,9 @@
<ClCompile Include="..\ebpf_platform.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\ebpf_random.c">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\ebpf_epoch.h">
Expand Down Expand Up @@ -111,5 +114,8 @@
<ClInclude Include="..\ebpf_tracelog.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\ebpf_random.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
</Project>
1 change: 1 addition & 0 deletions tests/performance/performance.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down
2 changes: 2 additions & 0 deletions tests/performance/platform.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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();
}
Expand Down
2 changes: 1 addition & 1 deletion tools/bpf2c/templates/kernel_mode_bpf2c.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
<WindowsTargetPlatformVersion>$(LatestTargetPlatformVersion)</WindowsTargetPlatformVersion>
<ProjectName>$(FileName)_km</ProjectName>
<!-- Required for Microsoft OneBranch builds -->
<UseInternalMSUniCrtPackage>true</UseInternalMSUniCrtPackage>
<UseInternalMSUniCrtPackage>true</UseInternalMSUniCrtPackage>
<UndockedKernelModeBuild>true</UndockedKernelModeBuild>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
Expand Down

0 comments on commit e57858f

Please sign in to comment.