From 8f53448c44df0a575abf7a9c84794212e7f48939 Mon Sep 17 00:00:00 2001 From: Patryk Kaminski Date: Fri, 30 Jun 2023 16:21:32 +0200 Subject: [PATCH] Test parts of API with libFuzzer. --- source/adapters/null/ur_null.cpp | 63 ++++++ test/conformance/CMakeLists.txt | 21 ++ test/conformance/fuzz/CMakeLists.txt | 7 + .../fuzz/urConformanceFuzz copy.cpp | 200 ++++++++++++++++++ test/conformance/fuzz/urConformanceFuzz.cpp | 126 +++++++++++ 5 files changed, 417 insertions(+) create mode 100644 test/conformance/fuzz/CMakeLists.txt create mode 100644 test/conformance/fuzz/urConformanceFuzz copy.cpp create mode 100644 test/conformance/fuzz/urConformanceFuzz.cpp diff --git a/source/adapters/null/ur_null.cpp b/source/adapters/null/ur_null.cpp index 5653ca57db..d200712178 100644 --- a/source/adapters/null/ur_null.cpp +++ b/source/adapters/null/ur_null.cpp @@ -142,5 +142,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/conformance/CMakeLists.txt b/test/conformance/CMakeLists.txt index 2b2c5238c6..0eedd8add1 100644 --- a/test/conformance/CMakeLists.txt +++ b/test/conformance/CMakeLists.txt @@ -44,6 +44,23 @@ function(add_conformance_test_with_platform_environment name) target_compile_definitions("test-${name}" PRIVATE PLATFORM_ENVIRONMENT) endfunction() +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=10 -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_subdirectory(testing) add_subdirectory(platform) @@ -74,3 +91,7 @@ if(DEFINED UR_DPCXX) add_subdirectory(program) add_subdirectory(enqueue) endif() + +if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + add_subdirectory(fuzz) +endif() diff --git a/test/conformance/fuzz/CMakeLists.txt b/test/conformance/fuzz/CMakeLists.txt new file mode 100644 index 0000000000..ab8af96776 --- /dev/null +++ b/test/conformance/fuzz/CMakeLists.txt @@ -0,0 +1,7 @@ +# 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 + +add_fuzz_test(conformance + urConformanceFuzz.cpp) diff --git a/test/conformance/fuzz/urConformanceFuzz copy.cpp b/test/conformance/fuzz/urConformanceFuzz copy.cpp new file mode 100644 index 0000000000..cd5586d54f --- /dev/null +++ b/test/conformance/fuzz/urConformanceFuzz copy.cpp @@ -0,0 +1,200 @@ +// 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 "ur_api.h" + +struct FuzzData { + uint16_t platform_count; + uint16_t device_count; + uint8_t device_type; + uint32_t device_alloc_size; + uint32_t host_alloc_size; + uint8_t usm_alloc_info_prop_name; + uint16_t usm_alloc_info_prop_size; + void *usm_alloc_info_prop_value; + uint16_t num_subdevices; + ur_context_properties_t context_props; +}; + +int ParseData(uint8_t *data, size_t size, FuzzData *fuzz_data) { + // Make sure fuzzer data size is sufficient to store all variables + if (size < sizeof(*fuzz_data)) { + return -1; + } + + uint8_t *data_ptr = data; + memcpy(&fuzz_data->platform_count, data_ptr, + sizeof(fuzz_data->platform_count)); + // Limit the max number of platforms to avoid allocating too much memory for a vector + if (fuzz_data->platform_count > 1024) { + return -1; + } + data_ptr += sizeof(fuzz_data->platform_count); + + memcpy(&fuzz_data->device_count, data_ptr, sizeof(fuzz_data->device_count)); + data_ptr += sizeof(fuzz_data->device_count); + + memcpy(&fuzz_data->device_type, data_ptr, sizeof(fuzz_data->device_type)); + // Pass only integers which can be a valid device type + if (fuzz_data->device_type > 7) { + return -1; + } + data_ptr += sizeof(fuzz_data->device_type); + + memcpy(&fuzz_data->device_alloc_size, data_ptr, + sizeof(fuzz_data->device_alloc_size)); + // Limit the max size of allocations + if (fuzz_data->device_alloc_size > 1 * 1024 * 1024) { + return -1; + } + data_ptr += sizeof(fuzz_data->device_alloc_size); + + memcpy(&fuzz_data->host_alloc_size, data_ptr, + sizeof(fuzz_data->host_alloc_size)); + // Limit the max size of allocations + if (fuzz_data->host_alloc_size > 1 * 1024 * 1024) { + return -1; + } + data_ptr += sizeof(fuzz_data->host_alloc_size); + + memcpy(&fuzz_data->usm_alloc_info_prop_name, data_ptr, + sizeof(fuzz_data->usm_alloc_info_prop_name)); + // Pass only integers which can be a valid memory alloc info property name + if (fuzz_data->usm_alloc_info_prop_name > 4) { + return -1; + } + data_ptr += sizeof(fuzz_data->usm_alloc_info_prop_name); + + memcpy(&fuzz_data->usm_alloc_info_prop_size, data_ptr, + sizeof(fuzz_data->usm_alloc_info_prop_size)); + // Limit the max size of the allocation + if (fuzz_data->usm_alloc_info_prop_size > UINT16_MAX) { + return -1; + } + // Make sure fuzzer data size is sufficient to store property data + if (size < sizeof(*fuzz_data) + fuzz_data->usm_alloc_info_prop_size) { + return -1; + } + data_ptr += sizeof(fuzz_data->usm_alloc_info_prop_size); + + fuzz_data->usm_alloc_info_prop_value = data_ptr; + data_ptr += fuzz_data->usm_alloc_info_prop_size; + + memcpy(&fuzz_data->num_subdevices, data_ptr, + sizeof(fuzz_data->num_subdevices)); + // Limit the max number of subdevices to avoid allocating too much memory for a vector + if (fuzz_data->num_subdevices > 1024) { + return -1; + } + data_ptr += sizeof(fuzz_data->num_subdevices); + + memcpy(&fuzz_data->context_props.stype, data_ptr, + sizeof(fuzz_data->context_props.stype)); + data_ptr += sizeof(fuzz_data->context_props.stype); + fuzz_data->context_props.pNext = nullptr; + data_ptr += sizeof(fuzz_data->context_props.pNext); + memcpy(&fuzz_data->context_props.flags, data_ptr, + sizeof(fuzz_data->context_props.flags)); + data_ptr += sizeof(fuzz_data->context_props.flags); + + return 0; +} + +extern "C" int LLVMFuzzerTestOneInput(uint8_t *data, size_t size) { + FuzzData fuzz_data; + int ret = ParseData(data, size, &fuzz_data); + if (ret) { + return ret; + } + + //// API calls + ur_result_t res = UR_RESULT_SUCCESS; + + res = urInit(0); + if (res != UR_RESULT_SUCCESS) { + return 0; + } + + // Get valid platforms + std::vector platforms; + uint32_t platformCount = 0; + + res = urPlatformGet(fuzz_data.platform_count, nullptr, &platformCount); + if (res != UR_RESULT_SUCCESS) { + return 0; + } + platformCount = + platformCount ? fuzz_data.platform_count % platformCount : 0; + platforms.resize(platformCount); + res = urPlatformGet(platformCount, platforms.data(), nullptr); + if (res != UR_RESULT_SUCCESS || platformCount == 0) { + return 0; + } + + // Get valid devices of a random platform + ur_platform_handle_t platform = + platforms[fuzz_data.platform_count % platforms.size()]; + std::vector devices; + uint32_t deviceCount = 0; + ur_device_type_t deviceType = + static_cast(fuzz_data.device_type); + + res = urDeviceGet(platform, deviceType, fuzz_data.device_count, nullptr, + &deviceCount); + if (res != UR_RESULT_SUCCESS) { + return 0; + } + deviceCount = deviceCount ? fuzz_data.device_count % deviceCount : 0; + devices.resize(deviceCount); + res = urDeviceGet(platform, deviceType, devices.size(), devices.data(), + nullptr); + if (res != UR_RESULT_SUCCESS || deviceCount == 0) { + return 0; + } + + // Test API + ur_context_handle_t context; + void *host_ptr = nullptr; + void *device_ptr = nullptr; + size_t usm_alloc_info_size = 0; + ur_device_handle_t device = + devices[fuzz_data.device_count % devices.size()]; + ur_usm_type_t device_type = UR_USM_TYPE_UNKNOWN; + const char *msg = nullptr; + int32_t error_code = -1; + ur_usm_pool_handle_t pool; + std::vector subdevices(fuzz_data.num_subdevices); + + urContextCreate(devices.size(), devices.data(), &fuzz_data.context_props, + &context); + urUSMPoolCreate(context, nullptr, &pool); + urUSMHostAlloc(context, nullptr, pool, fuzz_data.host_alloc_size, + &host_ptr); + if (fuzz_data.host_alloc_size != 0) { + memset(host_ptr, 'H', fuzz_data.host_alloc_size); + } else { + assert(host_ptr == nullptr); + } + urUSMDeviceAlloc(context, device, nullptr, pool, + fuzz_data.device_alloc_size, &device_ptr); + + urPlatformGetLastError(platform, &msg, &error_code); + + urDevicePartition(device, nullptr, subdevices.size(), subdevices.data(), + nullptr); + + urUSMFree(context, host_ptr); + urUSMFree(context, device_ptr); + urUSMPoolRelease(pool); + for (auto &device : devices) { + urDeviceRelease(device); + } + urContextRelease(context); + return 0; +} diff --git a/test/conformance/fuzz/urConformanceFuzz.cpp b/test/conformance/fuzz/urConformanceFuzz.cpp new file mode 100644 index 0000000000..301452686d --- /dev/null +++ b/test/conformance/fuzz/urConformanceFuzz.cpp @@ -0,0 +1,126 @@ +// 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 "ur_api.h" + +extern "C" int LLVMFuzzerTestOneInput(uint8_t *data, size_t size) { + enum FuncPtr { + 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, + }; + + std::vector platforms; + std::vector devices; + std::map> pool_host_allocs; + std::vector no_pool_host_allocs; + ur_platform_handle_t platform = nullptr; + ur_context_handle_t context = nullptr; + constexpr ur_device_type_t device_type = UR_DEVICE_TYPE_ALL; + constexpr uint32_t num_entries = 5; + uint32_t num_platforms = 0; + uint32_t num_devices = 0; + + std::random_device rand_device; + std::mt19937 generator(rand_device()); + + ur_result_t ret = urInit(0); + if (ret != UR_RESULT_SUCCESS) { + return -1; + } + + uint8_t *data_ptr = data; + while (data_ptr != data + size) { + switch (*data_ptr++) { + case UR_PLATFORM_GET: + urPlatformGet(num_entries, platforms.data(), &num_platforms); + platforms.resize(num_platforms); + break; + case UR_DEVICE_GET: + if (!platforms.empty()) { + std::uniform_int_distribution distrib( + 0, platforms.size() - 1); + platform = platforms[distrib(generator)]; + } + urDeviceGet(platform, device_type, num_entries, devices.data(), + &num_devices); + devices.resize(num_devices); + break; + case UR_CONTEXT_CREATE: + if (!devices.empty()) { + urContextCreate(devices.size(), devices.data(), nullptr, + &context); + } + break; + case UR_USM_POOL_CREATE: + ur_usm_pool_handle_t pool; + urUSMPoolCreate(context, nullptr, &pool); + pool_host_allocs[pool] = {}; + break; + case UR_USM_POOL_RELEASE: + if (!pool_host_allocs.empty()) { + auto &[pool, allocs] = *pool_host_allocs.begin(); + if (!allocs.empty()) { + for (auto &ptr : allocs) { + urUSMFree(context, ptr); + } + } + urUSMPoolRelease(pool); + pool_host_allocs.erase(pool); + } + break; + case UR_USM_HOST_ALLOC: + void *ptr; + if (!pool_host_allocs.empty()) { + auto &[pool, allocs] = *pool_host_allocs.end(); + urUSMHostAlloc(context, nullptr, pool, 100, &ptr); + pool_host_allocs[pool].push_back(ptr); + } else { + urUSMHostAlloc(context, nullptr, nullptr, 100, &ptr); + no_pool_host_allocs.push_back(ptr); + } + break; + case UR_USM_FREE: + if (!no_pool_host_allocs.empty()) { + urUSMFree(context, no_pool_host_allocs.back()); + no_pool_host_allocs.pop_back(); + } else if (!pool_host_allocs.empty()) { + for (auto pool_it = pool_host_allocs.rbegin(); + pool_it != pool_host_allocs.rend(); ++pool_it) { + auto &[pool, allocs] = *pool_it; + if (!allocs.empty()) { + urUSMFree(context, allocs.back()); + allocs.pop_back(); + break; + } + } + } + break; + } + } + + for (const auto &[pool, allocs] : pool_host_allocs) { + for (auto &alloc : allocs) { + urUSMFree(context, alloc); + } + urUSMPoolRelease(pool); + } + for (auto &alloc : no_pool_host_allocs) { + urUSMFree(context, alloc); + } + for (auto &device : devices) { + urDeviceRelease(device); + } + urContextRelease(context); + return 0; +}