Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fuzz verifier #3417

Merged
merged 2 commits into from
Apr 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
123 changes: 45 additions & 78 deletions ebpf-for-windows.sln

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion libs/api/ebpf_api.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2326,7 +2326,7 @@ _initialize_ebpf_object_from_file(
std::string path = std::get<std::string>(file_or_data);
new_object->file_name = cxplat_duplicate_string(path.c_str());
} else {
new_object->file_name = cxplat_duplicate_string("memory");
new_object->file_name = cxplat_duplicate_string(object_name ? object_name : "memory");
}
if (new_object->file_name == nullptr) {
result = EBPF_NO_MEMORY;
Expand Down
8 changes: 8 additions & 0 deletions libs/execution_context/ebpf_program.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@
static size_t _ebpf_program_state_index = MAXUINT64;
#define EBPF_MAX_HASH_SIZE 128

// Global flag to disable invoking programs. This is used when fuzzing the IOCTL interface.
bool ebpf_program_disable_invoke = false;

typedef struct _ebpf_program
{
ebpf_core_object_t object;
Expand Down Expand Up @@ -1412,6 +1415,11 @@ ebpf_program_invoke(
_Out_ uint32_t* result,
_Inout_ ebpf_execution_context_state_t* execution_state)
{
if (ebpf_program_disable_invoke) {
*result = 0;
return;
}

// High volume call - Skip entry/exit logging.
const ebpf_program_t* current_program = program;

Expand Down
3 changes: 3 additions & 0 deletions tests/api_test/api_test.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,9 @@
<ProjectReference Include="..\..\external\Catch2\build\src\Catch2WithMain.vcxproj">
<Project>{8bd3552a-2cfb-4a59-ab15-2031b97ada1e}</Project>
</ProjectReference>
<ProjectReference Include="..\..\external\usersim\cxplat\src\cxplat_winuser\cxplat_winuser.vcxproj">
<Project>{f2ca70ab-af9a-47d1-9da9-94d5ab573ac2}</Project>
</ProjectReference>
<ProjectReference Include="..\..\rpc_interface\rpc_interface.vcxproj">
<Project>{1423245d-0249-40fc-a077-ff7780acfe3f}</Project>
</ProjectReference>
Expand Down
192 changes: 189 additions & 3 deletions tests/end_to_end/helpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -660,13 +660,199 @@ static ebpf_program_data_t _ebpf_xdp_test_program_data = {
static ebpf_program_data_t _ebpf_bind_program_data = {
{EBPF_PROGRAM_DATA_CURRENT_VERSION, EBPF_PROGRAM_DATA_CURRENT_VERSION_SIZE}, &_ebpf_bind_program_info, NULL};

// CGROUP_SOCK_ADDR.
// SOCK_ADDR.
static int
_ebpf_sock_addr_get_current_pid_tgid(_In_ const bpf_sock_addr_t* ctx)
{
UNREFERENCED_PARAMETER(ctx);
return -ENOTSUP;
}

static int
_ebpf_sock_addr_set_redirect_context(_In_ const bpf_sock_addr_t* ctx, _In_ void* data, _In_ uint32_t data_size)
{
UNREFERENCED_PARAMETER(ctx);
UNREFERENCED_PARAMETER(data);
UNREFERENCED_PARAMETER(data_size);
return -ENOTSUP;
}

static int
_ebpf_sock_addr_get_current_logon_id(_In_ const bpf_sock_addr_t* ctx)
{
UNREFERENCED_PARAMETER(ctx);
return -ENOTSUP;
}

static int
_ebpf_sock_addr_is_current_admin(_In_ const bpf_sock_addr_t* ctx)
{
UNREFERENCED_PARAMETER(ctx);
return -ENOTSUP;
}

static ebpf_result_t
_ebpf_sock_addr_context_create(
_In_reads_bytes_opt_(data_size_in) const uint8_t* data_in,
size_t data_size_in,
_In_reads_bytes_opt_(context_size_in) const uint8_t* context_in,
size_t context_size_in,
_Outptr_ void** context)
{
UNREFERENCED_PARAMETER(data_in);
UNREFERENCED_PARAMETER(data_size_in);

ebpf_result_t retval;
*context = nullptr;

bpf_sock_addr_t* sock_addr_context = reinterpret_cast<bpf_sock_addr_t*>(ebpf_allocate(sizeof(bpf_sock_addr_t)));
if (sock_addr_context == nullptr) {
retval = EBPF_NO_MEMORY;
goto Done;
}

if (context_in) {
if (context_size_in < sizeof(bpf_sock_addr_t)) {
retval = EBPF_INVALID_ARGUMENT;
goto Done;
}
bpf_sock_addr_t* provided_context = (bpf_sock_addr_t*)context_in;
*sock_addr_context = *provided_context;
}

*context = sock_addr_context;
sock_addr_context = nullptr;
retval = EBPF_SUCCESS;
Done:
ebpf_free(sock_addr_context);
sock_addr_context = nullptr;
return retval;
}

static void
_ebpf_sock_addr_context_destroy(
_In_opt_ void* context,
_Out_writes_bytes_to_(*data_size_out, *data_size_out) uint8_t* data_out,
_Inout_ size_t* data_size_out,
_Out_writes_bytes_to_(*context_size_out, *context_size_out) uint8_t* context_out,
_Inout_ size_t* context_size_out)
{
UNREFERENCED_PARAMETER(data_out);
if (!context) {
return;
}

bpf_sock_addr_t* sock_addr_context = reinterpret_cast<bpf_sock_addr_t*>(context);
if (context_out && *context_size_out >= sizeof(bpf_sock_addr_t)) {
bpf_sock_addr_t* provided_context = (bpf_sock_addr_t*)context_out;
*provided_context = *sock_addr_context;
*context_size_out = sizeof(bpf_sock_addr_t);
}

ebpf_free(sock_addr_context);
sock_addr_context = nullptr;

*data_size_out = 0;
return;
}

static const void* _ebpf_sock_addr_specific_helper_functions[] = {
(void*)_ebpf_sock_addr_get_current_pid_tgid, (void*)_ebpf_sock_addr_set_redirect_context};

static ebpf_helper_function_addresses_t _ebpf_sock_addr_specific_helper_function_address_table = {
{EBPF_HELPER_FUNCTION_PROTOTYPE_CURRENT_VERSION, EBPF_MAX_GENERAL_HELPER_FUNCTION},
EBPF_COUNT_OF(_ebpf_sock_addr_specific_helper_functions),
(uint64_t*)_ebpf_sock_addr_specific_helper_functions};

static const void* _ebpf_sock_addr_global_helper_functions[] = {
(void*)_ebpf_sock_addr_get_current_logon_id, (void*)_ebpf_sock_addr_is_current_admin};

static ebpf_helper_function_addresses_t _ebpf_sock_addr_global_helper_function_address_table = {
{EBPF_HELPER_FUNCTION_PROTOTYPE_CURRENT_VERSION, EBPF_MAX_GENERAL_HELPER_FUNCTION},
EBPF_COUNT_OF(_ebpf_sock_addr_global_helper_functions),
(uint64_t*)_ebpf_sock_addr_global_helper_functions};

static ebpf_program_data_t _ebpf_sock_addr_program_data = {
{EBPF_PROGRAM_DATA_CURRENT_VERSION, EBPF_PROGRAM_DATA_CURRENT_VERSION_SIZE}, &_ebpf_sock_addr_program_info, NULL};
.header = {EBPF_PROGRAM_DATA_CURRENT_VERSION, EBPF_PROGRAM_DATA_CURRENT_VERSION_SIZE},
.program_info = &_ebpf_sock_addr_program_info,
.program_type_specific_helper_function_addresses = &_ebpf_sock_addr_specific_helper_function_address_table,
.global_helper_function_addresses = &_ebpf_sock_addr_global_helper_function_address_table,
.context_create = &_ebpf_sock_addr_context_create,
.context_destroy = &_ebpf_sock_addr_context_destroy,
.required_irql = DISPATCH_LEVEL,
};

// SOCK_OPS.
static ebpf_result_t
_ebpf_sock_ops_context_create(
_In_reads_bytes_opt_(data_size_in) const uint8_t* data_in,
size_t data_size_in,
_In_reads_bytes_opt_(context_size_in) const uint8_t* context_in,
size_t context_size_in,
_Outptr_ void** context)
{
UNREFERENCED_PARAMETER(data_in);
UNREFERENCED_PARAMETER(data_size_in);
ebpf_result_t retval;
*context = nullptr;

bpf_sock_ops_t* sock_ops_context = reinterpret_cast<bpf_sock_ops_t*>(ebpf_allocate(sizeof(bpf_sock_ops_t)));
if (sock_ops_context == nullptr) {
retval = EBPF_NO_MEMORY;
goto Done;
}

if (context_in) {
if (context_size_in < sizeof(bpf_sock_ops_t)) {
retval = EBPF_INVALID_ARGUMENT;
goto Done;
}
bpf_sock_ops_t* provided_context = (bpf_sock_ops_t*)context_in;
*sock_ops_context = *provided_context;
}

*context = sock_ops_context;
sock_ops_context = nullptr;
retval = EBPF_SUCCESS;
Done:
ebpf_free(sock_ops_context);
sock_ops_context = nullptr;
return retval;
}

static void
_ebpf_sock_ops_context_destroy(
_In_opt_ void* context,
_Out_writes_bytes_to_(*data_size_out, *data_size_out) uint8_t* data_out,
_Inout_ size_t* data_size_out,
_Out_writes_bytes_to_(*context_size_out, *context_size_out) uint8_t* context_out,
_Inout_ size_t* context_size_out)
{
UNREFERENCED_PARAMETER(data_out);
if (!context) {
return;
}

bpf_sock_ops_t* sock_ops_context = reinterpret_cast<bpf_sock_ops_t*>(context);
if (context_out && *context_size_out >= sizeof(bpf_sock_ops_t)) {
bpf_sock_ops_t* provided_context = (bpf_sock_ops_t*)context_out;
*provided_context = *sock_ops_context;
*context_size_out = sizeof(bpf_sock_ops_t);
}

ebpf_free(sock_ops_context);

*data_size_out = 0;
return;
}

static ebpf_program_data_t _ebpf_sock_ops_program_data = {
{EBPF_PROGRAM_DATA_CURRENT_VERSION, EBPF_PROGRAM_DATA_CURRENT_VERSION_SIZE}, &_ebpf_sock_ops_program_info, NULL};
.header = {EBPF_PROGRAM_DATA_CURRENT_VERSION, EBPF_PROGRAM_DATA_CURRENT_VERSION_SIZE},
.program_info = &_ebpf_sock_ops_program_info,
.context_create = &_ebpf_sock_ops_context_create,
.context_destroy = &_ebpf_sock_ops_context_destroy,
.required_irql = DISPATCH_LEVEL,
};

// Sample extension.
static const void* _sample_ebpf_ext_helper_functions[] = {
Expand Down
11 changes: 9 additions & 2 deletions tests/end_to_end/test_helper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
#include "api_internal.h"
#include "bpf/bpf.h"
#include "bpf2c.h"
#include "catch_wrapper.hpp"
#include "cxplat_fault_injection.h"
#include "ebpf_async.h"
#include "ebpf_core.h"
Expand All @@ -24,6 +23,14 @@
#include <sstream>
using namespace std::chrono_literals;

#if defined(NO_CATCH2)
#define REQUIRE(x) \
if (!(x)) \
throw std::runtime_error("REQUIRE failed")
#else
#include "catch_wrapper.hpp"
#endif

bool _ebpf_capture_corpus = false;

extern "C" metadata_table_t*
Expand Down Expand Up @@ -778,7 +785,7 @@ _test_helper_end_to_end::~_test_helper_end_to_end()
_expect_native_module_load_failures = false;

set_verification_in_progress(false);
} catch (Catch::TestFailureException&) {
} catch (...) {
}
}

Expand Down
27 changes: 25 additions & 2 deletions tests/libfuzzer/execution_context/libfuzz_harness.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@
#include "platform.h"

#include <chrono>
#include <condition_variable>
#include <filesystem>
#include <map>
#include <mutex>
#include <vector>

#define REQUIRE(X) \
Expand Down Expand Up @@ -148,8 +150,17 @@ static std::map<std::string, ebpf_map_definition_in_memory_t> _map_definitions =
},
};

static std::mutex _ebpf_fuzzer_async_mutex;
static std::condition_variable _ebpf_fuzzer_async_cv;
static bool _ebpf_fuzzer_async_done = false;

void
fuzz_async_completion(void*, size_t, ebpf_result_t){};
fuzz_async_completion(void*, size_t, ebpf_result_t)
{
std::unique_lock<std::mutex> lock(_ebpf_fuzzer_async_mutex);
_ebpf_fuzzer_async_done = true;
_ebpf_fuzzer_async_cv.notify_all();
};

class fuzz_wrapper
{
Expand Down Expand Up @@ -214,6 +225,12 @@ fuzz_ioctl(std::vector<uint8_t>& random_buffer)
std::vector<uint8_t> request;
std::vector<uint8_t> reply;
uint16_t reply_buffer_length = 0;

{
std::unique_lock<std::mutex> lock(_ebpf_fuzzer_async_mutex);
_ebpf_fuzzer_async_done = false;
}

if (random_buffer.size() < sizeof(ebpf_operation_header_t)) {
return;
}
Expand Down Expand Up @@ -245,14 +262,20 @@ fuzz_ioctl(std::vector<uint8_t>& random_buffer)
async ? &async : nullptr,
async ? &fuzz_async_completion : nullptr);

if (result == EBPF_PENDING) {
if ((result == EBPF_PENDING) && async) {
ebpf_core_cancel_protocol_handler(&async);
std::unique_lock<std::mutex> lock(_ebpf_fuzzer_async_mutex);
_ebpf_fuzzer_async_cv.wait(lock, []() { return _ebpf_fuzzer_async_done; });
}
}

// Disable program invocation for fuzzing.
extern "C" bool ebpf_program_disable_invoke;

FUZZ_EXPORT int __cdecl LLVMFuzzerInitialize(int*, char***)
{
ebpf_fuzzing_memory_limit = 1024 * 1024 * 10;
ebpf_program_disable_invoke = true;
return 0;
}

Expand Down
Loading
Loading