Skip to content

Commit

Permalink
Test parts of API with libFuzzer.
Browse files Browse the repository at this point in the history
  • Loading branch information
PatKamin committed Jul 26, 2023
1 parent ae807d8 commit c6b81f2
Show file tree
Hide file tree
Showing 5 changed files with 359 additions and 0 deletions.
7 changes: 7 additions & 0 deletions .github/workflows/cmake.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ jobs:
build_type: Release
compiler: {c: clang, cxx: clang++}
libbacktrace: '-DVAL_USE_LIBBACKTRACE_BACKTRACE=OFF'
fuzztest: ON
- os: 'ubuntu-22.04'
build_type: Release
compiler: {c: gcc, cxx: g++}
Expand All @@ -25,6 +26,7 @@ jobs:
build_type: Release
compiler: {c: clang, cxx: clang++}
libbacktrace: '-DVAL_USE_LIBBACKTRACE_BACKTRACE=ON'
fuzztest: ON
- os: 'ubuntu-20.04'
build_type: Release
compiler: {c: gcc-7, cxx: g++-7}
Expand Down Expand Up @@ -89,6 +91,11 @@ jobs:
working-directory: ${{github.workspace}}/build
run: ctest -C ${{matrix.build_type}} --output-on-failure -L "python|umf|loader|validation|tracing|unit|urtrace"

- name: Fuzz test
working-directory: ${{github.workspace}}/build
if: matrix.fuzztest == 'ON'
run: ctest -C ${{matrix.build_type}} --output-on-failure -L "fuzz"

windows-build:
name: Build - Windows
strategy:
Expand Down
63 changes: 63 additions & 0 deletions source/adapters/null/ur_null.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -163,5 +163,68 @@ context_t::context_t() {
}
return UR_RESULT_SUCCESS;
};

//////////////////////////////////////////////////////////////////////////
urDdiTable.USM.pfnHostAlloc =
[](ur_context_handle_t hContext, const ur_usm_desc_t *pUSMDesc,
ur_usm_pool_handle_t pool, size_t size, void **ppMem) {
if (size == 0) {
*ppMem = nullptr;
return UR_RESULT_ERROR_UNSUPPORTED_SIZE;
}
*ppMem = malloc(size);
if (ppMem == nullptr) {
return UR_RESULT_ERROR_OUT_OF_HOST_MEMORY;
}
return UR_RESULT_SUCCESS;
};

//////////////////////////////////////////////////////////////////////////
urDdiTable.USM.pfnDeviceAlloc =
[](ur_context_handle_t hContext, ur_device_handle_t hDevice,
const ur_usm_desc_t *pUSMDesc, ur_usm_pool_handle_t pool,
size_t size, void **ppMem) {
if (size == 0) {
*ppMem = nullptr;
return UR_RESULT_ERROR_UNSUPPORTED_SIZE;
}
*ppMem = malloc(size);
if (ppMem == nullptr) {
return UR_RESULT_ERROR_OUT_OF_HOST_MEMORY;
}
return UR_RESULT_SUCCESS;
};

//////////////////////////////////////////////////////////////////////////
urDdiTable.USM.pfnFree = [](ur_context_handle_t hContext, void *pMem) {
free(pMem);
return UR_RESULT_SUCCESS;
};

//////////////////////////////////////////////////////////////////////////
urDdiTable.USM.pfnGetMemAllocInfo =
[](ur_context_handle_t hContext, const void *pMem,
ur_usm_alloc_info_t propName, size_t propSize, void *pPropValue,
size_t *pPropSizeRet) {
switch (propName) {
case UR_USM_ALLOC_INFO_TYPE:
*reinterpret_cast<ur_usm_type_t *>(pPropValue) =
pMem ? UR_USM_TYPE_DEVICE : UR_USM_TYPE_UNKNOWN;
if (pPropSizeRet != nullptr) {
*pPropSizeRet = sizeof(ur_usm_type_t);
}
break;
case UR_USM_ALLOC_INFO_SIZE:
*reinterpret_cast<size_t *>(pPropValue) = pMem ? SIZE_MAX : 0;
if (pPropSizeRet != nullptr) {
*pPropSizeRet = sizeof(size_t);
}
break;
default:
pPropValue = nullptr;
break;
}
return UR_RESULT_SUCCESS;
};
}
} // namespace driver
3 changes: 3 additions & 0 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,6 @@ add_subdirectory(unit)
if(UR_BUILD_TOOLS)
add_subdirectory(tools)
endif()
if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
add_subdirectory(fuzz)
endif()
24 changes: 24 additions & 0 deletions test/fuzz/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Copyright (C) 2023 Intel Corporation
# Part of the Unified-Runtime Project, under the Apache License v2.0 with LLVM Exceptions.
# See LICENSE.TXT
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

function(add_fuzz_test name)
set(TEST_TARGET_NAME fuzztest-${name})
add_executable(${TEST_TARGET_NAME}
${ARGN})
target_link_libraries(${TEST_TARGET_NAME}
PRIVATE
${PROJECT_NAME}::loader
${PROJECT_NAME}::headers
${PROJECT_NAME}::common
-fsanitize=fuzzer)
add_test(NAME ${TEST_TARGET_NAME}
COMMAND ${TEST_TARGET_NAME} -max_total_time=60 -seed=1 -shrink=1
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
set_tests_properties(${TEST_TARGET_NAME} PROPERTIES LABELS "fuzz")
target_compile_options(${TEST_TARGET_NAME} PRIVATE -g -fsanitize=fuzzer)
endfunction()

add_fuzz_test(base
urFuzz.cpp)
262 changes: 262 additions & 0 deletions test/fuzz/urFuzz.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,262 @@
// Copyright (C) 2023 Intel Corporation
// Part of the Unified-Runtime Project, under the Apache License v2.0 with LLVM Exceptions.
// See LICENSE.TXT
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#include <map>
#include <vector>

#include "ur_api.h"

enum FuzzerData {
UR_PLATFORM_GET,
UR_DEVICE_GET,
UR_CONTEXT_CREATE,
UR_USM_POOL_CREATE,
UR_USM_POOL_RELEASE,
UR_USM_HOST_ALLOC,
UR_USM_DEVICE_ALLOC,
UR_USM_FREE,
UR_MAX_FUZZER_DATA,
};

typedef struct TestState {
static constexpr uint32_t num_entries = 1;

std::vector<ur_adapter_handle_t> adapters;
std::vector<ur_platform_handle_t> platforms;
std::vector<ur_device_handle_t> devices;
std::vector<ur_context_handle_t> contexts;
std::map<ur_usm_pool_handle_t, std::vector<void *>> pool_host_allocs;
std::vector<void *> no_pool_host_allocs;
ur_platform_handle_t platform = nullptr;
ur_device_type_t device_type = UR_DEVICE_TYPE_ALL;

uint32_t num_adapters;
uint32_t num_platforms;
uint32_t num_devices;

uint8_t platform_num;
uint8_t device_num;
uint8_t context_num;
uint8_t device_type_fuzz;
uint16_t alloc_size;
} TestState;

bool check_context_exists(TestState &state);

int ur_platform_get(TestState &state) {
ur_result_t res = urPlatformGet(
state.adapters.data(), state.adapters.size(), state.num_entries,
state.platforms.data(), &state.num_platforms);
if (res != UR_RESULT_SUCCESS) {
return -1;
}
state.platforms.resize(state.num_platforms);

return 0;
}

int ur_device_get(TestState &state) {
if (!state.platform) {
if (state.platform_num >= state.platforms.size()) {
return -1;
}
state.platform = state.platforms[state.platform_num];
}
ur_result_t res =
urDeviceGet(state.platform, state.device_type, state.num_entries,
state.devices.data(), &state.num_devices);
if (res != UR_RESULT_SUCCESS) {
return -1;
}
state.devices.resize(state.num_devices);

return 0;
}

int ur_context_create(TestState &state) {
if (!state.devices.empty()) {
ur_context_handle_t context;
if (state.device_num >= state.devices.size()) {
return -1;
}
urContextCreate(state.device_num, state.devices.data(), nullptr,
&context);
state.contexts.push_back(context);
}

return 0;
}

int ur_usm_pool_create(TestState &state) {
if (!check_context_exists(state)) {
return -1;
}

ur_usm_pool_handle_t pool;
urUSMPoolCreate(state.contexts[state.context_num], nullptr, &pool);
state.pool_host_allocs[pool] = {};

return 0;
}

int ur_usm_pool_release(TestState &state) {
if (state.pool_host_allocs.empty() || !check_context_exists(state)) {
return -1;
}

auto &[pool, allocs] = *state.pool_host_allocs.begin();
if (!allocs.empty()) {
for (auto &ptr : allocs) {
urUSMFree(state.contexts[state.context_num], ptr);
}
}
urUSMPoolRelease(pool);
state.pool_host_allocs.erase(pool);

return 0;
}

int ur_usm_host_alloc(TestState &state) {
if (!check_context_exists(state)) {
return -1;
}

void *ptr;
if (!state.pool_host_allocs.empty()) {
auto &[pool, allocs] = *state.pool_host_allocs.end();
urUSMHostAlloc(state.contexts[state.context_num], nullptr, pool,
state.alloc_size, &ptr);
allocs.push_back(ptr);
} else {
urUSMHostAlloc(state.contexts[state.context_num], nullptr, nullptr,
state.alloc_size, &ptr);
state.no_pool_host_allocs.push_back(ptr);
}

return 0;
}

int ur_usm_device_alloc(TestState &state) { return 0; }

int ur_usm_free(TestState &state) {
if (!state.no_pool_host_allocs.empty()) {
urUSMFree(state.contexts[state.context_num],
state.no_pool_host_allocs.back());
state.no_pool_host_allocs.pop_back();
} else if (!state.pool_host_allocs.empty()) {
for (auto pool_it = state.pool_host_allocs.rbegin();
pool_it != state.pool_host_allocs.rend(); ++pool_it) {
auto &[pool, allocs] = *pool_it;
if (!allocs.empty()) {
urUSMFree(state.contexts[state.context_num], allocs.back());
allocs.pop_back();
break;
}
}
}

return 0;
}

int init_random_data(uint8_t **data, size_t size, TestState &state) {
if (size < 6) {
return -1;
}

state.platform_num = **data;
(*data)++;
state.device_num = **data;
(*data)++;
state.context_num = **data;
(*data)++;
state.device_type_fuzz = **data;
if (state.device_type_fuzz < 1 || state.device_type_fuzz > 7) {
return -1;
}
state.device_type = static_cast<ur_device_type_t>(state.device_type_fuzz);
(*data)++;
state.alloc_size = **data;
(*data)++;

return 0;
}

int get_next_api_call(uint8_t *data) {
if (*data >= UR_MAX_FUZZER_DATA || *data < 0) {
return -1;
} else {
return *data;
}
}

bool check_context_exists(TestState &state) {
if (state.contexts.empty() || state.context_num >= state.contexts.size()) {
return false;
}

return true;
}

void cleanup(TestState &state) {
for (const auto &[pool, allocs] : state.pool_host_allocs) {
for (auto &alloc : allocs) {
urUSMFree(state.contexts[state.context_num], alloc);
}
urUSMPoolRelease(pool);
}
for (auto &alloc : state.no_pool_host_allocs) {
urUSMFree(state.contexts[state.context_num], alloc);
}
for (auto &context : state.contexts) {
urContextRelease(context);
}
for (auto &device : state.devices) {
urDeviceRelease(device);
}
}

extern "C" int LLVMFuzzerTestOneInput(uint8_t *data, size_t size) {
uint8_t *data_ptr = data;
TestState test_state;
int next_api_call = -1;
int ret = -1;

int (*api_wrappers[])(TestState &) = {
ur_platform_get, ur_device_get, ur_context_create,
ur_usm_pool_create, ur_usm_pool_release, ur_usm_host_alloc,
ur_usm_device_alloc, ur_usm_free,
};

ret = init_random_data(&data_ptr, size, test_state);
if (ret != 0) {
return -1;
}

ur_result_t res = urInit(0, nullptr);
if (res != UR_RESULT_SUCCESS) {
return -1;
}
test_state.adapters.resize(test_state.num_entries);
res = urAdapterGet(test_state.num_entries, test_state.adapters.data(),
&test_state.num_adapters);
if (res != UR_RESULT_SUCCESS || test_state.num_adapters == 0) {
return -1;
}

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

cleanup(test_state);
return 0;
}

0 comments on commit c6b81f2

Please sign in to comment.