Skip to content

Commit 0176f45

Browse files
Merge pull request #572 from PatKamin/fuzz-test
Add fuzz tests
2 parents 7d0f8c4 + f268c74 commit 0176f45

File tree

7 files changed

+372
-0
lines changed

7 files changed

+372
-0
lines changed

.github/workflows/nightly.yml

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,43 @@ permissions:
1111
contents: read
1212

1313
jobs:
14+
fuzz-test:
15+
name: Fuzz test
16+
strategy:
17+
fail-fast: false
18+
matrix:
19+
build_type: [Debug, Release]
20+
compiler: [{c: clang, cxx: clang++}]
21+
22+
runs-on: ubuntu-latest
23+
24+
steps:
25+
- name: Checkout repository
26+
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
27+
28+
- name: Install apt packages
29+
run: |
30+
sudo apt-get update
31+
sudo apt-get install -y cmake hwloc libhwloc-dev libnuma-dev libtbb-dev
32+
33+
- name: Configure CMake
34+
run: >
35+
cmake
36+
-B ${{github.workspace}}/build
37+
-DCMAKE_BUILD_TYPE=${{matrix.build_type}}
38+
-DCMAKE_C_COMPILER=${{matrix.compiler.c}}
39+
-DCMAKE_CXX_COMPILER=${{matrix.compiler.cxx}}
40+
-DUMF_TESTS_FAIL_ON_SKIP=ON
41+
-DUMF_DEVELOPER_MODE=ON
42+
-DUMF_BUILD_FUZZTESTS=ON
43+
44+
- name: Build
45+
run: cmake --build ${{github.workspace}}/build --config ${{matrix.build_type}} --verbose -j$(nproc)
46+
47+
- name: Fuzz long test
48+
working-directory: ${{github.workspace}}/build
49+
run: ctest -C ${{matrix.build_type}} --output-on-failure --verbose -L "fuzz-long"
50+
1451
valgrind:
1552
name: Valgrind
1653
strategy:

CMakeLists.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ option(UMF_BUILD_GPU_TESTS "Build UMF GPU tests" OFF)
3636
option(UMF_BUILD_BENCHMARKS "Build UMF benchmarks" OFF)
3737
option(UMF_BUILD_BENCHMARKS_MT "Build UMF multithreaded benchmarks" OFF)
3838
option(UMF_BUILD_EXAMPLES "Build UMF examples" ON)
39+
option(UMF_BUILD_FUZZTESTS "Build UMF fuzz tests" OFF)
3940
option(UMF_BUILD_GPU_EXAMPLES "Build UMF GPU examples" OFF)
4041
option(UMF_DEVELOPER_MODE "Enable developer checks, treats warnings as errors"
4142
OFF)
@@ -164,6 +165,13 @@ if(USE_MSAN)
164165
"prevent reporting false-positives")
165166
add_sanitizer_flag(memory)
166167
endif()
168+
# Fuzzer instrumentation for the whole library
169+
if(UMF_BUILD_FUZZTESTS
170+
AND CMAKE_CXX_COMPILER_ID MATCHES "Clang"
171+
AND LINUX)
172+
add_compile_options("-fsanitize=fuzzer-no-link")
173+
add_link_options("-fsanitize=fuzzer-no-link")
174+
endif()
167175

168176
# A header only library to specify include directories in transitive
169177
# dependencies.

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ List of options provided by CMake:
116116
| UMF_BUILD_GPU_TESTS | Build UMF GPU tests | ON/OFF | OFF |
117117
| UMF_BUILD_BENCHMARKS | Build UMF benchmarks | ON/OFF | OFF |
118118
| UMF_BUILD_EXAMPLES | Build UMF examples | ON/OFF | ON |
119+
| UMF_BUILD_FUZZTESTS | Build UMF fuzz tests | ON/OFF | OFF |
119120
| UMF_BUILD_GPU_EXAMPLES | Build UMF GPU examples | ON/OFF | OFF |
120121
| UMF_DEVELOPER_MODE | Treat warnings as errors and enables additional checks | ON/OFF | OFF |
121122
| UMF_FORMAT_CODE_STYLE | Add clang, cmake, and black -format-check and -format-apply targets to make | ON/OFF | OFF |

test/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,9 @@ if(LINUX) # OS-specific functions are implemented only for Linux now
187187
NAME mempolicy
188188
SRCS memspaces/mempolicy.cpp
189189
LIBS ${LIBNUMA_LIBRARIES})
190+
if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND UMF_BUILD_FUZZTESTS)
191+
add_subdirectory(fuzz)
192+
endif()
190193
endif()
191194

192195
if(UMF_BUILD_GPU_TESTS AND UMF_BUILD_LEVEL_ZERO_PROVIDER)

test/fuzz/CMakeLists.txt

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# Copyright (C) 2024 Intel Corporation
2+
# Under the Apache License v2.0 with LLVM Exceptions. See LICENSE.TXT.
3+
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
4+
5+
message(STATUS "Fuzzing tests enabled.")
6+
7+
function(add_fuzz_test name label)
8+
add_test(
9+
NAME "fuzz-${name}"
10+
COMMAND fuzztest ${ARGN} -verbosity=1
11+
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
12+
set_tests_properties("fuzz-${name}" PROPERTIES LABELS ${label})
13+
endfunction()
14+
15+
set(TEST_TARGET_NAME fuzztest)
16+
add_umf_executable(
17+
NAME ${TEST_TARGET_NAME}
18+
SRCS umfFuzz.cpp
19+
LIBS umf)
20+
target_link_libraries(${TEST_TARGET_NAME} PRIVATE umf -fsanitize=fuzzer)
21+
target_compile_options(${TEST_TARGET_NAME} PRIVATE -g -fsanitize=fuzzer)
22+
target_include_directories(${TEST_TARGET_NAME}
23+
PRIVATE ${UMF_CMAKE_SOURCE_DIR}/include)
24+
target_link_directories(${TEST_TARGET_NAME} PRIVATE ${LIBHWLOC_LIBRARY_DIRS})
25+
26+
add_fuzz_test(basic_long fuzz-long -max_total_time=600 -seed=1)

test/fuzz/umfFuzz.cpp

Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
// Copyright (C) 2024 Intel Corporation
2+
// Under the Apache License v2.0 with LLVM Exceptions. See LICENSE.TXT.
3+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
4+
5+
#include "utils.hpp"
6+
7+
namespace fuzz {
8+
9+
constexpr int MAX_PROVIDER_VECTOR_SIZE = 1024;
10+
constexpr int MAX_POOLS_VECTOR_SIZE = 20;
11+
constexpr int MAX_POOLS_ALLOC_SIZE = 1 * 1024; // 1 kB
12+
constexpr int MAX_PROVIDER_ALLOC_SIZE = 100 * 1024; // 100 kB
13+
14+
int umf_memory_provider_create(TestState &test_state) {
15+
std::cout << "Begin creating a provider" << std::endl;
16+
umf_memory_provider_ops_t *provider_ops = umfOsMemoryProviderOps();
17+
umf_os_memory_provider_params_t params = umfOsMemoryProviderParamsDefault();
18+
umf_result_t res =
19+
umfMemoryProviderCreate(provider_ops, &params, &test_state.provider);
20+
21+
if (res != UMF_RESULT_SUCCESS) {
22+
std::cout << "Failed to create a memory provider: " << res << std::endl;
23+
return -1;
24+
}
25+
std::cout << "OS memory provider created at " << (void *)test_state.provider
26+
<< std::endl;
27+
return 0;
28+
}
29+
30+
int umf_memory_provider_alloc(TestState &test_state) {
31+
std::cout << "Begin memory_provider_alloc" << std::endl;
32+
void *ptr;
33+
size_t alloc_size;
34+
constexpr size_t alignment = 0;
35+
36+
if (test_state.provider_memory_allocations.size() >=
37+
MAX_PROVIDER_VECTOR_SIZE) {
38+
return -1;
39+
}
40+
41+
int ret = test_state.get_next_alloc_size(test_state, alloc_size,
42+
MAX_PROVIDER_ALLOC_SIZE);
43+
if (ret != 0) {
44+
std::cout << "Failed to get alloc size" << std::endl;
45+
return -1;
46+
}
47+
48+
umf_result_t res = umfMemoryProviderAlloc(test_state.provider, alloc_size,
49+
alignment, &ptr);
50+
if (res != UMF_RESULT_SUCCESS) {
51+
std::cout << "Failed to allocate memory from the provider: " << res
52+
<< std::endl;
53+
return -1;
54+
}
55+
test_state.provider_memory_allocations.push_back(
56+
std::make_pair(ptr, alloc_size));
57+
std::cout << "Allocated memory at " << ptr << " with alloc_size "
58+
<< alloc_size << std::endl;
59+
std::cout << "Size of vector with allocated memory from the provider: "
60+
<< test_state.provider_memory_allocations.size() << std::endl;
61+
return 0;
62+
}
63+
64+
int umf_memory_provider_free(TestState &test_state) {
65+
std::cout << "Begin memory_provider_free" << std::endl;
66+
if (test_state.provider_memory_allocations.empty()) {
67+
std::cout << "No memory allocated" << std::endl;
68+
return -1;
69+
}
70+
71+
std::pair<void *, size_t> alloc =
72+
test_state.provider_memory_allocations.back();
73+
umf_result_t res =
74+
umfMemoryProviderFree(test_state.provider, alloc.first, alloc.second);
75+
76+
if (res != UMF_RESULT_SUCCESS) {
77+
std::cout << "Failed to free memory to the provider: " << res
78+
<< std::endl;
79+
;
80+
return -1;
81+
}
82+
83+
std::cout << "Freed memory from the provider at " << alloc.first
84+
<< " with alloc_size " << alloc.second << std::endl;
85+
test_state.provider_memory_allocations.pop_back();
86+
return 0;
87+
}
88+
89+
int umf_pool_create(TestState &test_state) {
90+
if (test_state.pools.size() > MAX_POOLS_VECTOR_SIZE) {
91+
std::cout << "Max pools limit reached" << std::endl;
92+
return -1;
93+
}
94+
95+
umf_memory_pool_ops_t *pool_ops = umfScalablePoolOps();
96+
void *pool_params = NULL;
97+
umf_pool_create_flags_t flags = 0;
98+
umf_memory_pool_handle_t pool;
99+
umf_result_t res =
100+
umfPoolCreate(pool_ops, test_state.provider, pool_params, flags, &pool);
101+
102+
if (res != UMF_RESULT_SUCCESS) {
103+
std::cout << "Failed to create a pool: " << res << std::endl;
104+
return -1;
105+
}
106+
107+
test_state.pools.insert(std::make_pair(pool, std::vector<void *>()));
108+
std::cout << "Scalable memory pool created at " << pool
109+
<< " and pools available: " << test_state.pools.size()
110+
<< std::endl;
111+
return 0;
112+
}
113+
114+
int umf_pool_destroy(TestState &test_state) {
115+
std::cout << "Begin destroy pool" << std::endl;
116+
if (test_state.pools.empty()) {
117+
std::cout << "No pools created" << std::endl;
118+
return -1;
119+
}
120+
auto pool = (*test_state.pools.begin()).first;
121+
umfPoolDestroy(pool);
122+
test_state.pools.erase(pool);
123+
std::cout << "Destroyed pool at " << pool << std::endl;
124+
return 0;
125+
}
126+
127+
int umf_pool_malloc(TestState &test_state) {
128+
std::cout << "Begin pool_malloc" << std::endl;
129+
if (test_state.pools.empty()) {
130+
std::cout << "No pools created" << std::endl;
131+
return -1;
132+
}
133+
size_t alloc_size;
134+
int ret = test_state.get_next_alloc_size(test_state, alloc_size,
135+
MAX_POOLS_ALLOC_SIZE);
136+
if (ret != 0) {
137+
std::cout << "Failed to get next allocation size" << std::endl;
138+
return -1;
139+
}
140+
auto &pool_entry = *test_state.pools.rbegin();
141+
void *ptr = umfPoolMalloc(pool_entry.first, alloc_size);
142+
if (!ptr) {
143+
std::cout
144+
<< "Failed to allocate memory in the pool with handle address: "
145+
<< pool_entry.first << std::endl;
146+
}
147+
148+
pool_entry.second.push_back(ptr);
149+
std::cout << "Allocated memory at " << ptr
150+
<< " with allocation size: " << alloc_size << std::endl;
151+
return 0;
152+
}
153+
154+
int umf_free(TestState &test_state) {
155+
std::cout << "Begin releasing pool memory" << std::endl;
156+
for (auto &pool : test_state.pools) {
157+
if (pool.second.empty()) {
158+
continue;
159+
} else {
160+
umfFree(pool.second.back());
161+
pool.second.pop_back();
162+
std::cout << "Freed memory from the pool at: " << pool.second.back()
163+
<< std::endl;
164+
break;
165+
}
166+
std::cout << "No pool memory to free" << std::endl;
167+
return -1;
168+
}
169+
return 0;
170+
}
171+
172+
void cleanup(TestState &test_state) {
173+
std::cout << "Begin cleanup state" << std::endl;
174+
for (auto &alloc : test_state.provider_memory_allocations) {
175+
umfMemoryProviderFree(test_state.provider, alloc.first, alloc.second);
176+
}
177+
178+
for (auto &pool_entry : test_state.pools) {
179+
for (auto &ptr : pool_entry.second) {
180+
umfFree(ptr);
181+
}
182+
umfPoolDestroy(pool_entry.first);
183+
}
184+
std::cout << "Freed all allocated memory from provider and pools and "
185+
"destroyed all pools"
186+
<< std::endl;
187+
umfMemoryProviderDestroy(test_state.provider);
188+
std::cout << "Destroyed the provider" << std::endl;
189+
}
190+
191+
extern "C" int LLVMFuzzerTestOneInput(uint8_t *data, size_t size) {
192+
int next_api_call;
193+
auto data_provider = std::make_unique<FuzzedDataProvider>(data, size);
194+
TestState test_state(std::move(data_provider));
195+
int ret = -1;
196+
197+
// clang-format off
198+
int (*api_wrappers[])(TestState &) = {
199+
umf_memory_provider_alloc,
200+
umf_memory_provider_free,
201+
umf_pool_create,
202+
umf_pool_destroy,
203+
umf_pool_malloc,
204+
umf_free,
205+
};
206+
// clang-format on
207+
umf_memory_provider_create(test_state);
208+
209+
while ((next_api_call = test_state.get_next_api_call()) != -1) {
210+
ret = api_wrappers[next_api_call](test_state);
211+
if (ret) {
212+
cleanup(test_state);
213+
return -1;
214+
}
215+
}
216+
217+
cleanup(test_state);
218+
return 0;
219+
}
220+
} // namespace fuzz

0 commit comments

Comments
 (0)