diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index a9b6d2bd1e..37cd7bb886 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -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++} @@ -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} @@ -63,6 +65,10 @@ jobs: wget -O ${{github.workspace}}/dpcpp_compiler.tar.gz https://github.com/intel/llvm/releases/download/sycl-nightly%2F20230626/dpcpp-compiler.tar.gz tar -xvf ${{github.workspace}}/dpcpp_compiler.tar.gz + - name: Setup DPC++ + run: | + source ${{github.workspace}}/dpcpp_compiler/startup.sh + - name: Configure CMake run: > cmake @@ -73,6 +79,8 @@ jobs: -DCMAKE_BUILD_TYPE=${{matrix.build_type}} -DUR_BUILD_TESTS=ON -DUR_FORMAT_CPP_STYLE=ON + -DUR_USE_ASAN=ON + -DUR_USE_UBSAN=ON -DUR_DPCXX=${{github.workspace}}/dpcpp_compiler/bin/clang++ ${{matrix.libbacktrace}} ${{matrix.pool_tracking}} @@ -88,6 +96,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$" + adapter-build: name: Build - Adapters on Ubuntu strategy: diff --git a/source/adapters/null/ur_null.cpp b/source/adapters/null/ur_null.cpp index 18c8d89ef5..5a62761b67 100644 --- a/source/adapters/null/ur_null.cpp +++ b/source/adapters/null/ur_null.cpp @@ -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(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(pPropValue) = pMem ? SIZE_MAX : 0; + if (pPropSizeRet != nullptr) { + *pPropSizeRet = sizeof(size_t); + } + break; + default: + pPropValue = nullptr; + break; + } + return UR_RESULT_SUCCESS; + }; } } // namespace driver diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 79ca48236c..1d7e84f0c3 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -24,3 +24,6 @@ add_subdirectory(unit) if(UR_BUILD_TOOLS) add_subdirectory(tools) endif() +if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND DEFINED UR_DPCXX) + add_subdirectory(fuzz) +endif() diff --git a/test/conformance/CMakeLists.txt b/test/conformance/CMakeLists.txt index f68744d279..dadf5d5406 100644 --- a/test/conformance/CMakeLists.txt +++ b/test/conformance/CMakeLists.txt @@ -88,7 +88,7 @@ if(DEFINED UR_DPCXX) add_custom_target(generate_device_binaries) set(UR_CONFORMANCE_DEVICE_BINARIES_DIR - "${CMAKE_CURRENT_BINARY_DIR}/device_binaries/") + "${CMAKE_CURRENT_BINARY_DIR}/device_binaries" CACHE INTERNAL UR_CONFORMANCE_DEVICE_BINARIES_DIR) file(MAKE_DIRECTORY ${UR_CONFORMANCE_DEVICE_BINARIES_DIR}) if(DEFINED UR_CONFORMANCE_TARGET_TRIPLES) diff --git a/test/fuzz/CMakeLists.txt b/test/fuzz/CMakeLists.txt new file mode 100644 index 0000000000..72f6c460ea --- /dev/null +++ b/test/fuzz/CMakeLists.txt @@ -0,0 +1,45 @@ +# 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 label) + set(TEST_TARGET_NAME fuzztest-${name}) + add_ur_executable(${TEST_TARGET_NAME} + urFuzz.cpp) + 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} ${ARGN} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + set_tests_properties(${TEST_TARGET_NAME} PROPERTIES + LABELS ${label} + ENVIRONMENT + "UR_ENABLE_LAYERS=UR_LAYER_FULL_VALIDATION" + "UR_ADAPTERS_FORCE_LOAD=\"$\"") + target_compile_options(${TEST_TARGET_NAME} PRIVATE -g -fsanitize=fuzzer) + target_compile_definitions(${TEST_TARGET_NAME} PRIVATE -DKERNEL_IL_PATH="${UR_CONFORMANCE_DEVICE_BINARIES_DIR}/bar/sycl_spir641.spv") + target_include_directories(${TEST_TARGET_NAME} PRIVATE ${UR_CONFORMANCE_DEVICE_BINARIES_DIR}) + + add_dependencies(${TEST_TARGET_NAME} generate_device_binaries) +endfunction() + +add_fuzz_test(base fuzz-long + -max_total_time=1 -seed=1 -verbosity=1) + +set(CORPUS_DIR ${CMAKE_CURRENT_SOURCE_DIR}/corpus) +add_fuzz_test(alloc fuzz + ${CORPUS_DIR}/alloc -verbosity=1) + +add_fuzz_test(create-release fuzz + ${CORPUS_DIR}/create-release -verbosity=1) + +add_fuzz_test(kernel-launch fuzz + ${CORPUS_DIR}/kernel-launch -verbosity=1) + +add_fuzz_test(pool-alloc fuzz + ${CORPUS_DIR}/pool-alloc -verbosity=1) diff --git a/test/fuzz/corpus/README.md b/test/fuzz/corpus/README.md new file mode 100644 index 0000000000..9017814cce --- /dev/null +++ b/test/fuzz/corpus/README.md @@ -0,0 +1,87 @@ +# Corpra for fuzz tests with fixed API calls scenarios +These corpra contain UR API calls in a predefined order described below. +All scenarios begin with single calls to urInit() and urAdapterGet(). + +## create-release +Test device/context/pool create and release + +urPlatformGet() +urPlatformGet() +urDeviceGet() +urDeviceGet() +urDeviceRelease() +urDeviceGet() +urDeviceGet() +urContextCreate() +urContextRelease() +urContextCreate() +urUSMPoolCreate() +urUSMPoolCreate() +urUSMPoolRelease() +urUSMPoolRelease() +urUSMPoolCreate() +urUSMPoolCreate() +urUSMPoolCreate() +urUSMPoolCreate() +urUSMPoolRelease() +urUSMPoolRelease() +urUSMPoolRelease() +urUSMPoolRelease() +urContextRelease() + +## pool-alloc +Allocate with host and device pools + +urPlatformGet() +urPlatformGet() +urDeviceGet() +urDeviceGet() +urContextCreate() +urUSMPoolCreate() +urUSMPoolCreate() +urUSMPoolCreate() +urUSMPoolCreate() +urUSMHostAlloc() +urUSMDeviceAlloc() +urUSMFree() +urUSMPoolRelease() +urUSMPoolRelease() +urUSMFree() +urUSMPoolRelease() +urUSMPoolRelease() +urContextRelease() + +## alloc +Allocate on host/device, no pools + +urPlatformGet() +urPlatformGet() +urDeviceGet() +urDeviceGet() +urContextCreate() +urUSMHostAlloc() +urUSMDeviceAlloc() +urUSMFree() +urUSMFree() +urContextRelease() + +## kernel-launch +Launch kernel + +urPlatformGet() +urPlatformGet() +urDeviceGet() +urDeviceGet() +urContextCreate() +urProgramCreateWithIL() +urProgramBuild() +urKernelCreate() +urQueueCreate() +urEnqueueKernelLaunch() +urEventWait() +urEventRelease() +urQueueFinish() +urQueueRelease() +urKernelRelease() +urProgramRelease() +urContextRelease() diff --git a/test/fuzz/corpus/alloc b/test/fuzz/corpus/alloc new file mode 100644 index 0000000000..8272438896 Binary files /dev/null and b/test/fuzz/corpus/alloc differ diff --git a/test/fuzz/corpus/create-release b/test/fuzz/corpus/create-release new file mode 100644 index 0000000000..9b2d34b67c Binary files /dev/null and b/test/fuzz/corpus/create-release differ diff --git a/test/fuzz/corpus/kernel-launch b/test/fuzz/corpus/kernel-launch new file mode 100644 index 0000000000..a34254f7b1 Binary files /dev/null and b/test/fuzz/corpus/kernel-launch differ diff --git a/test/fuzz/corpus/pool-alloc b/test/fuzz/corpus/pool-alloc new file mode 100644 index 0000000000..46a24151aa Binary files /dev/null and b/test/fuzz/corpus/pool-alloc differ diff --git a/test/fuzz/urFuzz.cpp b/test/fuzz/urFuzz.cpp new file mode 100644 index 0000000000..3cb4e8c872 --- /dev/null +++ b/test/fuzz/urFuzz.cpp @@ -0,0 +1,415 @@ +// 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 "kernel_entry_points.h" +#include "ur_api.h" +#include "utils.hpp" + +namespace fuzz { + +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; + } + if (state.platforms.size() != state.num_platforms) { + state.platforms.resize(state.num_platforms); + } + + return 0; +} + +int ur_device_get(TestState &state) { + if (state.platforms.empty() || + state.platform_num >= state.platforms.size() || + state.platforms[0] == nullptr) { + return -1; + } + + ur_result_t res = urDeviceGet(state.platforms[state.platform_num], + state.device_type, state.num_entries, + state.devices.data(), &state.num_devices); + if (res != UR_RESULT_SUCCESS) { + return -1; + } + if (state.devices.size() != state.num_devices) { + state.devices.resize(state.num_devices); + } + + return 0; +} + +int ur_device_release(TestState &state) { + if (state.devices.empty()) { + return -1; + } + + ur_result_t res = urDeviceRelease(state.devices.back()); + if (res == UR_RESULT_SUCCESS) { + state.devices.pop_back(); + } + + return 0; +} + +int ur_context_create(TestState &state) { + if (!state.device_exists()) { + return -1; + } + + ur_context_handle_t context; + ur_result_t res = urContextCreate(state.devices.size(), + state.devices.data(), nullptr, &context); + if (res == UR_RESULT_SUCCESS) { + // TODO: Add limit to max size of vectors + state.contexts.emplace_back(std::make_unique(context)); + } + + return 0; +} + +int ur_context_release(TestState &state) { + if (!state.context_exists() || + !state.contexts[state.context_num]->host_pools.empty() || + !state.contexts[state.context_num]->device_pools.empty() || + !state.contexts[state.context_num]->no_pool_host_allocs.empty() || + !state.contexts[state.context_num]->no_pool_device_allocs.empty()) { + return -1; + } + + state.contexts.pop_back(); + + return 0; +} + +int pool_create(TestState &state, Pools &pools) { + ur_usm_pool_handle_t pool_handle; + ur_usm_pool_desc_t pool_desc{UR_STRUCTURE_TYPE_USM_POOL_DESC, nullptr, + UR_USM_POOL_FLAG_ZERO_INITIALIZE_BLOCK}; + ur_result_t res = urUSMPoolCreate(state.contexts[state.context_num]->handle, + &pool_desc, &pool_handle); + if (res == UR_RESULT_SUCCESS) { + pools.emplace_back(std::make_unique(pool_handle)); + } + + return 0; +} + +int ur_usm_pool_create_host(TestState &state) { + if (!state.context_exists()) { + return -1; + } + + return pool_create(state, state.contexts[state.context_num]->host_pools); +} + +int ur_usm_pool_create_device(TestState &state) { + if (!state.context_exists()) { + return -1; + } + + return pool_create(state, state.contexts[state.context_num]->device_pools); +} + +int pool_release(TestState &state, Pools &pools) { + if (pools.empty()) { + return -1; + } + + uint8_t index = state.get_vec_index(pools.size()); + pools.erase(pools.begin() + index); + + return 0; +} + +int ur_usm_pool_release_host(TestState &state) { + if (!state.context_exists()) { + return -1; + } + + return pool_release(state, state.contexts[state.context_num]->host_pools); +} + +int ur_usm_pool_release_device(TestState &state) { + if (!state.context_exists()) { + return -1; + } + + return pool_release(state, state.contexts[state.context_num]->device_pools); +} + +int alloc_setup(TestState &state, uint16_t &alloc_size) { + if (!state.context_exists()) { + return -1; + } + + if (state.get_next_input_data(&alloc_size) != 0) { + return -1; + } + + return 0; +} + +int ur_usm_host_alloc_pool(TestState &state) { + void *ptr; + uint16_t alloc_size; + ur_result_t res = UR_RESULT_SUCCESS; + + int ret = alloc_setup(state, alloc_size); + if (ret != 0) { + return -1; + } + + auto &context = state.contexts[state.context_num]; + auto &pools = context->host_pools; + auto index = state.get_vec_index(pools.size()); + if (index == -1) { + return -1; + } + auto &pool = *(pools.begin() + index); + + res = urUSMHostAlloc(context->handle, nullptr, pool->handle, alloc_size, + &ptr); + if (res == UR_RESULT_SUCCESS) { + pool->allocs.emplace_back( + std::make_unique(context->handle, ptr)); + } + + return 0; +} + +int ur_usm_host_alloc_no_pool(TestState &state) { + void *ptr; + uint16_t alloc_size; + ur_result_t res = UR_RESULT_SUCCESS; + + int ret = alloc_setup(state, alloc_size); + if (ret != 0) { + return -1; + } + + auto &context = state.contexts[state.context_num]; + + res = urUSMHostAlloc(context->handle, nullptr, nullptr, alloc_size, &ptr); + if (res == UR_RESULT_SUCCESS) { + context->no_pool_host_allocs.emplace_back( + std::make_unique(context->handle, ptr)); + } + + return 0; +} + +int ur_usm_device_alloc_pool(TestState &state) { + void *ptr; + uint16_t alloc_size; + ur_result_t res = UR_RESULT_SUCCESS; + + int ret = alloc_setup(state, alloc_size); + if (ret != 0) { + return -1; + } + + if (!state.device_exists()) { + return -1; + } + + auto &device_pools = state.contexts[state.context_num]->device_pools; + auto index = state.get_vec_index(device_pools.size()); + if (index == -1) { + return -1; + } + + auto &pool = *(device_pools.begin() + index); + auto &context = state.contexts[state.context_num]->handle; + auto &device = state.devices[state.device_num]; + res = urUSMDeviceAlloc(context, device, nullptr, pool->handle, alloc_size, + &ptr); + if (res == UR_RESULT_SUCCESS) { + pool->allocs.emplace_back(std::make_unique(context, ptr)); + } + + return 0; +} + +int ur_usm_device_alloc_no_pool(TestState &state) { + void *ptr; + uint16_t alloc_size; + ur_result_t res = UR_RESULT_SUCCESS; + + int ret = alloc_setup(state, alloc_size); + if (ret != 0) { + return -1; + } + + if (!state.device_exists()) { + return -1; + } + + auto &context = state.contexts[state.context_num]; + auto &device = state.devices[state.device_num]; + res = urUSMDeviceAlloc(context->handle, device, nullptr, nullptr, + alloc_size, &ptr); + if (res == UR_RESULT_SUCCESS) { + context->no_pool_device_allocs.emplace_back( + std::make_unique(context->handle, ptr)); + } + + return 0; +} + +int free_pool(TestState &state, Pools &pools) { + if (pools.empty()) { + return -1; + } + + auto index = state.get_vec_index(pools.size()); + if (index == -1) { + return -1; + } + + pools.erase(pools.begin() + index); + + return 0; +} + +int ur_usm_free_host_pool(TestState &state) { + if (!state.context_exists()) { + return -1; + } + + return free_pool(state, state.contexts[state.context_num]->host_pools); +} + +int ur_usm_free_device_pool(TestState &state) { + if (!state.context_exists()) { + return -1; + } + + return free_pool(state, state.contexts[state.context_num]->device_pools); +} + +int free_no_pool(Allocs &allocs) { + if (allocs.empty()) { + return -1; + } + + allocs.pop_back(); + + return 0; +} + +int ur_usm_free_host_no_pool(TestState &state) { + if (!state.context_exists()) { + return -1; + } + + return free_no_pool(state.contexts[state.context_num]->no_pool_host_allocs); +} + +int ur_usm_free_device_no_pool(TestState &state) { + if (!state.context_exists()) { + return -1; + } + + return free_no_pool( + state.contexts[state.context_num]->no_pool_device_allocs); +} + +int ur_program_create_with_il(TestState &state) { + if (!state.context_exists() || !state.device_exists()) { + return -1; + } + + std::vector il_bin; + ur_program_handle_t program = nullptr; + ur_kernel_handle_t kernel = nullptr; + ur_queue_handle_t queue = nullptr; + ur_event_handle_t event = nullptr; + auto &context = state.contexts[state.context_num]->handle; + auto &device = state.devices[state.device_num]; + std::string kernel_name = + uur::device_binaries::program_kernel_map["bar"][0]; + + state.load_kernel_source(il_bin); + urProgramCreateWithIL(context, il_bin.data(), il_bin.size(), nullptr, + &program); + urProgramBuild(context, program, nullptr); + urKernelCreate(program, kernel_name.data(), &kernel); + urQueueCreate(context, device, nullptr, &queue); + + const uint32_t nDim = 3; + const size_t gWorkOffset[] = {0, 0, 0}; + const size_t gWorkSize[] = {128, 128, 128}; + + urEnqueueKernelLaunch(queue, kernel, nDim, gWorkOffset, gWorkSize, nullptr, + 0, nullptr, &event); + + urEventWait(1, &event); + urEventRelease(event); + urQueueFinish(queue); + urQueueRelease(queue); + urKernelRelease(kernel); + urProgramRelease(program); + + return 0; +} + +extern "C" int LLVMFuzzerTestOneInput(uint8_t *data, size_t size) { + int next_api_call; + auto data_provider = std::make_unique(data, size); + TestState test_state(std::move(data_provider)); + int ret = -1; + + int (*api_wrappers[])(TestState &) = { + ur_platform_get, + ur_device_get, + ur_device_release, + ur_context_create, + ur_context_release, + ur_usm_pool_create_host, + ur_usm_pool_create_device, + ur_usm_pool_release_host, + ur_usm_pool_release_device, + ur_usm_host_alloc_pool, + ur_usm_host_alloc_no_pool, + ur_usm_device_alloc_pool, + ur_usm_device_alloc_no_pool, + ur_usm_free_host_pool, + ur_usm_free_host_no_pool, + ur_usm_free_device_pool, + ur_usm_free_device_no_pool, + ur_program_create_with_il, + }; + + ret = test_state.init(); + if (ret == -1) { + return ret; + } + + LoaderConfig config; + ur_result_t res = urInit(0, config.handle); + 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 ((next_api_call = test_state.get_next_api_call()) != -1) { + ret = api_wrappers[next_api_call](test_state); + if (ret) { + return -1; + } + } + + return 0; +} +} // namespace fuzz diff --git a/test/fuzz/utils.hpp b/test/fuzz/utils.hpp new file mode 100644 index 0000000000..6d9cf1e556 --- /dev/null +++ b/test/fuzz/utils.hpp @@ -0,0 +1,228 @@ +// 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 +#include +#include +#include +#include +#include +#include + +namespace fuzz { + +enum FuzzerAPICall : uint8_t { + UR_PLATFORM_GET, + UR_DEVICE_GET, + UR_DEVICE_RELEASE, + UR_CONTEXT_CREATE, + UR_CONTEXT_RELEASE, + UR_USM_POOL_CREATE_HOST, + UR_USM_POOL_CREATE_DEVICE, + UR_USM_POOL_RELEASE_HOST, + UR_USM_POOL_RELEASE_DEVICE, + UR_USM_HOST_ALLOC_POOL, + UR_USM_HOST_ALLOC_NO_POOL, + UR_USM_DEVICE_ALLOC_POOL, + UR_USM_DEVICE_ALLOC_NO_POOL, + UR_USM_FREE_HOST_POOL, + UR_USM_FREE_HOST_NO_POOL, + UR_USM_FREE_DEVICE_POOL, + UR_USM_FREE_DEVICE_NO_POOL, + UR_PROGRAM_CREATE_WITH_IL, + kMaxValue = UR_PROGRAM_CREATE_WITH_IL, +}; + +struct LoaderConfig { + ur_loader_config_handle_t handle; + + LoaderConfig() { + urLoaderConfigCreate(&handle); + urLoaderConfigEnableLayer(handle, "UR_LAYER_FULL_VALIDATION"); + } + ~LoaderConfig() { urLoaderConfigRelease(handle); } +}; + +struct Alloc { + ur_context_handle_t context; + void *ptr; + + Alloc(ur_context_handle_t context, void *ptr) + : context(context), ptr(ptr) {} + ~Alloc() { urUSMFree(context, ptr); } +}; +using Allocs = std::vector>; + +struct Pool { + ur_usm_pool_handle_t handle; + Allocs allocs; + + Pool(ur_usm_pool_handle_t handle) : handle(handle) {} + ~Pool() { + allocs.clear(); + urUSMPoolRelease(handle); + } +}; +using Pools = std::vector>; + +struct Context { + ur_context_handle_t handle; + Pools host_pools; + Pools device_pools; + Allocs no_pool_host_allocs; + Allocs no_pool_device_allocs; + + Context(ur_context_handle_t handle) : handle(handle) {} + ~Context() { + host_pools.clear(); + device_pools.clear(); + no_pool_host_allocs.clear(); + no_pool_device_allocs.clear(); + urContextRelease(handle); + } +}; +using Contexts = std::vector>; + +struct TestState { + static constexpr uint32_t num_entries = 1; + + std::unique_ptr data_provider; + + std::vector adapters; + std::vector platforms; + std::vector devices; + Contexts contexts; + 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; + + TestState(std::unique_ptr data_provider) + : data_provider(std::move(data_provider)) {} + + template int get_next_input_data(IntType *data) { + if (data_provider->remaining_bytes() < sizeof(IntType)) { + return -1; + } + *data = data_provider->ConsumeIntegral(); + + return 0; + } + + template + int get_next_input_data_in_range(IntType *data, IntType min, IntType max) { + if (data_provider->remaining_bytes() < sizeof(IntType)) { + return -1; + } + *data = data_provider->ConsumeIntegralInRange(min, max); + + return 0; + } + + template int get_next_input_data_enum(EnumType *data) { + if (data_provider->remaining_bytes() < sizeof(EnumType)) { + return -1; + } + *data = data_provider->ConsumeEnum(); + + return 0; + } + + int init() { + constexpr uint8_t UR_DEVICE_TYPE_MIN = 1; + constexpr uint8_t UR_DEVICE_TYPE_MAX = 7; + uint8_t device_type_int = 0; + + if (get_next_input_data(&platform_num) != 0) { + return -1; + } + + if (get_next_input_data(&device_num) != 0) { + return -1; + } + + if (get_next_input_data(&context_num) != 0) { + return -1; + } + + if (get_next_input_data_in_range(&device_type_int, UR_DEVICE_TYPE_MIN, + UR_DEVICE_TYPE_MAX) != 0) { + return -1; + } + device_type = static_cast(device_type_int); + + return 0; + } + + int get_next_api_call() { + FuzzerAPICall next_api_call; + return get_next_input_data_enum(&next_api_call) == 0 ? next_api_call + : -1; + } + + bool device_exists() { + if (devices.empty() || device_num >= devices.size() || + devices[0] == nullptr) { + return false; + } + + return true; + } + + bool context_exists() { + if (contexts.empty() || context_num >= contexts.size() || + contexts[0]->handle == nullptr) { + return false; + } + + return true; + } + + int get_vec_index(const uint8_t vec_size) { + if (vec_size == 0 || + data_provider->remaining_bytes() < sizeof(vec_size)) { + return -1; + } + return data_provider->ConsumeIntegralInRange( + 0, vec_size - 1); + } + + int load_kernel_source(std::vector &binary_out) { + std::string source_path = KERNEL_IL_PATH; + + std::ifstream source_file; + source_file.open(source_path, + std::ios::binary | std::ios::in | std::ios::ate); + if (!source_file.is_open()) { + std::cerr << "Failed to open a kernel source file: " << source_path + << std::endl; + return -1; + } + + size_t source_size = static_cast(source_file.tellg()); + source_file.seekg(0, std::ios::beg); + + std::vector device_binary(source_size); + source_file.read(device_binary.data(), source_size); + if (!source_file) { + source_file.close(); + std::cerr << "failed reading kernel source data from file: " + << source_path << std::endl; + return -1; + } + source_file.close(); + + binary_out = std::vector(std::move(device_binary)); + + return 0; + } +}; + +} // namespace fuzz