Skip to content

Commit

Permalink
Merge pull request #572 from PatKamin/fuzz-test
Browse files Browse the repository at this point in the history
Add fuzz tests
  • Loading branch information
lukaszstolarczuk committed Jun 27, 2024
2 parents 7d0f8c4 + f268c74 commit 0176f45
Show file tree
Hide file tree
Showing 7 changed files with 372 additions and 0 deletions.
37 changes: 37 additions & 0 deletions .github/workflows/nightly.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,43 @@ permissions:
contents: read

jobs:
fuzz-test:
name: Fuzz test
strategy:
fail-fast: false
matrix:
build_type: [Debug, Release]
compiler: [{c: clang, cxx: clang++}]

runs-on: ubuntu-latest

steps:
- name: Checkout repository
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1

- name: Install apt packages
run: |
sudo apt-get update
sudo apt-get install -y cmake hwloc libhwloc-dev libnuma-dev libtbb-dev
- name: Configure CMake
run: >
cmake
-B ${{github.workspace}}/build
-DCMAKE_BUILD_TYPE=${{matrix.build_type}}
-DCMAKE_C_COMPILER=${{matrix.compiler.c}}
-DCMAKE_CXX_COMPILER=${{matrix.compiler.cxx}}
-DUMF_TESTS_FAIL_ON_SKIP=ON
-DUMF_DEVELOPER_MODE=ON
-DUMF_BUILD_FUZZTESTS=ON
- name: Build
run: cmake --build ${{github.workspace}}/build --config ${{matrix.build_type}} --verbose -j$(nproc)

- name: Fuzz long test
working-directory: ${{github.workspace}}/build
run: ctest -C ${{matrix.build_type}} --output-on-failure --verbose -L "fuzz-long"

valgrind:
name: Valgrind
strategy:
Expand Down
8 changes: 8 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ option(UMF_BUILD_GPU_TESTS "Build UMF GPU tests" OFF)
option(UMF_BUILD_BENCHMARKS "Build UMF benchmarks" OFF)
option(UMF_BUILD_BENCHMARKS_MT "Build UMF multithreaded benchmarks" OFF)
option(UMF_BUILD_EXAMPLES "Build UMF examples" ON)
option(UMF_BUILD_FUZZTESTS "Build UMF fuzz tests" OFF)
option(UMF_BUILD_GPU_EXAMPLES "Build UMF GPU examples" OFF)
option(UMF_DEVELOPER_MODE "Enable developer checks, treats warnings as errors"
OFF)
Expand Down Expand Up @@ -164,6 +165,13 @@ if(USE_MSAN)
"prevent reporting false-positives")
add_sanitizer_flag(memory)
endif()
# Fuzzer instrumentation for the whole library
if(UMF_BUILD_FUZZTESTS
AND CMAKE_CXX_COMPILER_ID MATCHES "Clang"
AND LINUX)
add_compile_options("-fsanitize=fuzzer-no-link")
add_link_options("-fsanitize=fuzzer-no-link")
endif()

# A header only library to specify include directories in transitive
# dependencies.
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ List of options provided by CMake:
| UMF_BUILD_GPU_TESTS | Build UMF GPU tests | ON/OFF | OFF |
| UMF_BUILD_BENCHMARKS | Build UMF benchmarks | ON/OFF | OFF |
| UMF_BUILD_EXAMPLES | Build UMF examples | ON/OFF | ON |
| UMF_BUILD_FUZZTESTS | Build UMF fuzz tests | ON/OFF | OFF |
| UMF_BUILD_GPU_EXAMPLES | Build UMF GPU examples | ON/OFF | OFF |
| UMF_DEVELOPER_MODE | Treat warnings as errors and enables additional checks | ON/OFF | OFF |
| UMF_FORMAT_CODE_STYLE | Add clang, cmake, and black -format-check and -format-apply targets to make | ON/OFF | OFF |
Expand Down
3 changes: 3 additions & 0 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,9 @@ if(LINUX) # OS-specific functions are implemented only for Linux now
NAME mempolicy
SRCS memspaces/mempolicy.cpp
LIBS ${LIBNUMA_LIBRARIES})
if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND UMF_BUILD_FUZZTESTS)
add_subdirectory(fuzz)
endif()
endif()

if(UMF_BUILD_GPU_TESTS AND UMF_BUILD_LEVEL_ZERO_PROVIDER)
Expand Down
26 changes: 26 additions & 0 deletions test/fuzz/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Copyright (C) 2024 Intel Corporation
# Under the Apache License v2.0 with LLVM Exceptions. See LICENSE.TXT.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

message(STATUS "Fuzzing tests enabled.")

function(add_fuzz_test name label)
add_test(
NAME "fuzz-${name}"
COMMAND fuzztest ${ARGN} -verbosity=1
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
set_tests_properties("fuzz-${name}" PROPERTIES LABELS ${label})
endfunction()

set(TEST_TARGET_NAME fuzztest)
add_umf_executable(
NAME ${TEST_TARGET_NAME}
SRCS umfFuzz.cpp
LIBS umf)
target_link_libraries(${TEST_TARGET_NAME} PRIVATE umf -fsanitize=fuzzer)
target_compile_options(${TEST_TARGET_NAME} PRIVATE -g -fsanitize=fuzzer)
target_include_directories(${TEST_TARGET_NAME}
PRIVATE ${UMF_CMAKE_SOURCE_DIR}/include)
target_link_directories(${TEST_TARGET_NAME} PRIVATE ${LIBHWLOC_LIBRARY_DIRS})

add_fuzz_test(basic_long fuzz-long -max_total_time=600 -seed=1)
220 changes: 220 additions & 0 deletions test/fuzz/umfFuzz.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
// Copyright (C) 2024 Intel Corporation
// Under the Apache License v2.0 with LLVM Exceptions. See LICENSE.TXT.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#include "utils.hpp"

namespace fuzz {

constexpr int MAX_PROVIDER_VECTOR_SIZE = 1024;
constexpr int MAX_POOLS_VECTOR_SIZE = 20;
constexpr int MAX_POOLS_ALLOC_SIZE = 1 * 1024; // 1 kB
constexpr int MAX_PROVIDER_ALLOC_SIZE = 100 * 1024; // 100 kB

int umf_memory_provider_create(TestState &test_state) {
std::cout << "Begin creating a provider" << std::endl;
umf_memory_provider_ops_t *provider_ops = umfOsMemoryProviderOps();
umf_os_memory_provider_params_t params = umfOsMemoryProviderParamsDefault();
umf_result_t res =
umfMemoryProviderCreate(provider_ops, &params, &test_state.provider);

if (res != UMF_RESULT_SUCCESS) {
std::cout << "Failed to create a memory provider: " << res << std::endl;
return -1;
}
std::cout << "OS memory provider created at " << (void *)test_state.provider
<< std::endl;
return 0;
}

int umf_memory_provider_alloc(TestState &test_state) {
std::cout << "Begin memory_provider_alloc" << std::endl;
void *ptr;
size_t alloc_size;
constexpr size_t alignment = 0;

if (test_state.provider_memory_allocations.size() >=
MAX_PROVIDER_VECTOR_SIZE) {
return -1;
}

int ret = test_state.get_next_alloc_size(test_state, alloc_size,
MAX_PROVIDER_ALLOC_SIZE);
if (ret != 0) {
std::cout << "Failed to get alloc size" << std::endl;
return -1;
}

umf_result_t res = umfMemoryProviderAlloc(test_state.provider, alloc_size,
alignment, &ptr);
if (res != UMF_RESULT_SUCCESS) {
std::cout << "Failed to allocate memory from the provider: " << res
<< std::endl;
return -1;
}
test_state.provider_memory_allocations.push_back(
std::make_pair(ptr, alloc_size));
std::cout << "Allocated memory at " << ptr << " with alloc_size "
<< alloc_size << std::endl;
std::cout << "Size of vector with allocated memory from the provider: "
<< test_state.provider_memory_allocations.size() << std::endl;
return 0;
}

int umf_memory_provider_free(TestState &test_state) {
std::cout << "Begin memory_provider_free" << std::endl;
if (test_state.provider_memory_allocations.empty()) {
std::cout << "No memory allocated" << std::endl;
return -1;
}

std::pair<void *, size_t> alloc =
test_state.provider_memory_allocations.back();
umf_result_t res =
umfMemoryProviderFree(test_state.provider, alloc.first, alloc.second);

if (res != UMF_RESULT_SUCCESS) {
std::cout << "Failed to free memory to the provider: " << res
<< std::endl;
;
return -1;
}

std::cout << "Freed memory from the provider at " << alloc.first
<< " with alloc_size " << alloc.second << std::endl;
test_state.provider_memory_allocations.pop_back();
return 0;
}

int umf_pool_create(TestState &test_state) {
if (test_state.pools.size() > MAX_POOLS_VECTOR_SIZE) {
std::cout << "Max pools limit reached" << std::endl;
return -1;
}

umf_memory_pool_ops_t *pool_ops = umfScalablePoolOps();
void *pool_params = NULL;
umf_pool_create_flags_t flags = 0;
umf_memory_pool_handle_t pool;
umf_result_t res =
umfPoolCreate(pool_ops, test_state.provider, pool_params, flags, &pool);

if (res != UMF_RESULT_SUCCESS) {
std::cout << "Failed to create a pool: " << res << std::endl;
return -1;
}

test_state.pools.insert(std::make_pair(pool, std::vector<void *>()));
std::cout << "Scalable memory pool created at " << pool
<< " and pools available: " << test_state.pools.size()
<< std::endl;
return 0;
}

int umf_pool_destroy(TestState &test_state) {
std::cout << "Begin destroy pool" << std::endl;
if (test_state.pools.empty()) {
std::cout << "No pools created" << std::endl;
return -1;
}
auto pool = (*test_state.pools.begin()).first;
umfPoolDestroy(pool);
test_state.pools.erase(pool);
std::cout << "Destroyed pool at " << pool << std::endl;
return 0;
}

int umf_pool_malloc(TestState &test_state) {
std::cout << "Begin pool_malloc" << std::endl;
if (test_state.pools.empty()) {
std::cout << "No pools created" << std::endl;
return -1;
}
size_t alloc_size;
int ret = test_state.get_next_alloc_size(test_state, alloc_size,
MAX_POOLS_ALLOC_SIZE);
if (ret != 0) {
std::cout << "Failed to get next allocation size" << std::endl;
return -1;
}
auto &pool_entry = *test_state.pools.rbegin();
void *ptr = umfPoolMalloc(pool_entry.first, alloc_size);
if (!ptr) {
std::cout
<< "Failed to allocate memory in the pool with handle address: "
<< pool_entry.first << std::endl;
}

pool_entry.second.push_back(ptr);
std::cout << "Allocated memory at " << ptr
<< " with allocation size: " << alloc_size << std::endl;
return 0;
}

int umf_free(TestState &test_state) {
std::cout << "Begin releasing pool memory" << std::endl;
for (auto &pool : test_state.pools) {
if (pool.second.empty()) {
continue;
} else {
umfFree(pool.second.back());
pool.second.pop_back();
std::cout << "Freed memory from the pool at: " << pool.second.back()
<< std::endl;
break;
}
std::cout << "No pool memory to free" << std::endl;
return -1;
}
return 0;
}

void cleanup(TestState &test_state) {
std::cout << "Begin cleanup state" << std::endl;
for (auto &alloc : test_state.provider_memory_allocations) {
umfMemoryProviderFree(test_state.provider, alloc.first, alloc.second);
}

for (auto &pool_entry : test_state.pools) {
for (auto &ptr : pool_entry.second) {
umfFree(ptr);
}
umfPoolDestroy(pool_entry.first);
}
std::cout << "Freed all allocated memory from provider and pools and "
"destroyed all pools"
<< std::endl;
umfMemoryProviderDestroy(test_state.provider);
std::cout << "Destroyed the provider" << std::endl;
}

extern "C" int LLVMFuzzerTestOneInput(uint8_t *data, size_t size) {
int next_api_call;
auto data_provider = std::make_unique<FuzzedDataProvider>(data, size);
TestState test_state(std::move(data_provider));
int ret = -1;

// clang-format off
int (*api_wrappers[])(TestState &) = {
umf_memory_provider_alloc,
umf_memory_provider_free,
umf_pool_create,
umf_pool_destroy,
umf_pool_malloc,
umf_free,
};
// clang-format on
umf_memory_provider_create(test_state);

while ((next_api_call = test_state.get_next_api_call()) != -1) {
ret = api_wrappers[next_api_call](test_state);
if (ret) {
cleanup(test_state);
return -1;
}
}

cleanup(test_state);
return 0;
}
} // namespace fuzz
Loading

0 comments on commit 0176f45

Please sign in to comment.