Skip to content
Merged
12 changes: 12 additions & 0 deletions source/loader/layers/sanitizer/asan_interceptor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,18 @@ SanitizerInterceptor::~SanitizerInterceptor() {
DestroyShadowMemoryOnCPU();
DestroyShadowMemoryOnPVC();
DestroyShadowMemoryOnDG2();

// We must release these objects before releasing adapters, since
// they may use the adapter in their destructor
m_Quarantine = nullptr;
m_MemBufferMap.clear();
m_AllocationMap.clear();
m_KernelMap.clear();
m_ContextMap.clear();

for (auto Adapter : m_Adapters) {
getContext()->urDdiTable.Global.pfnAdapterRelease(Adapter);
}
}

/// The memory chunk allocated from the underlying allocator looks like this:
Expand Down
19 changes: 16 additions & 3 deletions source/loader/layers/sanitizer/asan_interceptor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include <optional>
#include <queue>
#include <unordered_map>
#include <unordered_set>
#include <vector>

namespace ur_sanitizer_layer {
Expand All @@ -48,9 +49,8 @@ struct DeviceInfo {
std::queue<std::shared_ptr<AllocInfo>> Quarantine;
size_t QuarantineSize = 0;

// TODO: re-enable retaining and releasing device handles in DeviceInfo
// constructor/destructor. See PR
// https://github.com/oneapi-src/unified-runtime/pull/1883
// Device handles are special and alive in the whole process lifetime,
// so we needn't retain&release here.
explicit DeviceInfo(ur_device_handle_t Device) : Handle(Device) {}

ur_result_t allocShadowMemory(ur_context_handle_t Context);
Expand Down Expand Up @@ -199,6 +199,16 @@ class SanitizerInterceptor {
ur_result_t eraseMemBuffer(ur_mem_handle_t MemHandle);
std::shared_ptr<MemBuffer> getMemBuffer(ur_mem_handle_t MemHandle);

ur_result_t holdAdapter(ur_adapter_handle_t Adapter) {
std::scoped_lock<ur_shared_mutex> Guard(m_AdaptersMutex);
if (m_Adapters.find(Adapter) != m_Adapters.end()) {
return UR_RESULT_SUCCESS;
}
UR_CALL(getContext()->urDdiTable.Global.pfnAdapterRetain(Adapter));
m_Adapters.insert(Adapter);
return UR_RESULT_SUCCESS;
}

std::optional<AllocationIterator> findAllocInfoByAddress(uptr Address);

std::shared_ptr<ContextInfo> getContextInfo(ur_context_handle_t Context) {
Expand Down Expand Up @@ -260,6 +270,9 @@ class SanitizerInterceptor {

std::unique_ptr<Quarantine> m_Quarantine;
logger::Logger &logger;

std::unordered_set<ur_adapter_handle_t> m_Adapters;
ur_shared_mutex m_AdaptersMutex;
};

} // namespace ur_sanitizer_layer
67 changes: 67 additions & 0 deletions source/loader/layers/sanitizer/ur_sanddi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,38 @@ ur_result_t setupContext(ur_context_handle_t Context, uint32_t numDevices,

} // namespace

///////////////////////////////////////////////////////////////////////////////
/// @brief Intercept function for urAdapterGet
__urdlllocal ur_result_t UR_APICALL urAdapterGet(
uint32_t
NumEntries, ///< [in] the number of adapters to be added to phAdapters.
///< If phAdapters is not NULL, then NumEntries should be greater than
///< zero, otherwise ::UR_RESULT_ERROR_INVALID_SIZE,
///< will be returned.
ur_adapter_handle_t *
phAdapters, ///< [out][optional][range(0, NumEntries)] array of handle of adapters.
///< If NumEntries is less than the number of adapters available, then
///< ::urAdapterGet shall only retrieve that number of platforms.
uint32_t *
pNumAdapters ///< [out][optional] returns the total number of adapters available.
) {
auto pfnAdapterGet = getContext()->urDdiTable.Global.pfnAdapterGet;

if (nullptr == pfnAdapterGet) {
return UR_RESULT_ERROR_UNSUPPORTED_FEATURE;
}

ur_result_t result = pfnAdapterGet(NumEntries, phAdapters, pNumAdapters);
if (result == UR_RESULT_SUCCESS && phAdapters) {
const uint32_t NumAdapters = pNumAdapters ? *pNumAdapters : NumEntries;
for (uint32_t i = 0; i < NumAdapters; ++i) {
UR_CALL(getContext()->interceptor->holdAdapter(phAdapters[i]));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If urAdapterGet() get called multiple times, then we would have duplicated handles.

Copy link
Contributor Author

@AllanZyne AllanZyne Aug 2, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe we needn't hold the handle of adapter, it seems ur loader has already hold this.
I'll investigate this.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

every time you call urAdapterGet, an internal reference count is incremented and you need to decrement it using urAdapterRelease.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

}
}

return result;
}

///////////////////////////////////////////////////////////////////////////////
/// @brief Intercept function for urUSMHostAlloc
__urdlllocal ur_result_t UR_APICALL urUSMHostAlloc(
Expand Down Expand Up @@ -1324,6 +1356,36 @@ __urdlllocal ur_result_t UR_APICALL urKernelSetArgPointer(
return result;
}

///////////////////////////////////////////////////////////////////////////////
/// @brief Exported function for filling application's Global table
/// with current process' addresses
///
/// @returns
/// - ::UR_RESULT_SUCCESS
/// - ::UR_RESULT_ERROR_INVALID_NULL_POINTER
/// - ::UR_RESULT_ERROR_UNSUPPORTED_VERSION
__urdlllocal ur_result_t UR_APICALL urGetGlobalProcAddrTable(
ur_api_version_t version, ///< [in] API version requested
ur_global_dditable_t
*pDdiTable ///< [in,out] pointer to table of DDI function pointers
) {
if (nullptr == pDdiTable) {
return UR_RESULT_ERROR_INVALID_NULL_POINTER;
}

if (UR_MAJOR_VERSION(ur_sanitizer_layer::getContext()->version) !=
UR_MAJOR_VERSION(version) ||
UR_MINOR_VERSION(ur_sanitizer_layer::getContext()->version) >
UR_MINOR_VERSION(version)) {
return UR_RESULT_ERROR_UNSUPPORTED_VERSION;
}

ur_result_t result = UR_RESULT_SUCCESS;

pDdiTable->pfnAdapterGet = ur_sanitizer_layer::urAdapterGet;

return result;
}
///////////////////////////////////////////////////////////////////////////////
/// @brief Exported function for filling application's Context table
/// with current process' addresses
Expand Down Expand Up @@ -1597,6 +1659,11 @@ ur_result_t context_t::init(ur_dditable_t *dditable,

urDdiTable = *dditable;

if (UR_RESULT_SUCCESS == result) {
result = ur_sanitizer_layer::urGetGlobalProcAddrTable(
UR_API_VERSION_CURRENT, &dditable->Global);
}

if (UR_RESULT_SUCCESS == result) {
result = ur_sanitizer_layer::urGetContextProcAddrTable(
UR_API_VERSION_CURRENT, &dditable->Context);
Expand Down
3 changes: 2 additions & 1 deletion source/loader/ur_lib.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ void context_t::initLayers() const {
}

void context_t::tearDownLayers() const {
for (auto &[layer, destroy] : layers) {
for (auto it = layers.rbegin(); it != layers.rend(); ++it) {
auto [layer, destroy] = *it;
layer->tearDown();
destroy();
}
Expand Down
4 changes: 4 additions & 0 deletions test/layers/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,7 @@ add_subdirectory(validation)
if(UR_ENABLE_TRACING)
add_subdirectory(tracing)
endif()

if(UR_ENABLE_SANITIZER)
add_subdirectory(sanitizer)
endif()
37 changes: 37 additions & 0 deletions test/layers/sanitizer/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Copyright (C) 2023-2024 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

set(UR_SANITIZER_TEST_DIR ${CMAKE_CURRENT_SOURCE_DIR})
set(SAN_TEST_PREFIX sanitizer_test)

function(add_sanitizer_test_executable name)
add_ur_executable(${SAN_TEST_PREFIX}-${name}
${ARGN})
target_link_libraries(${SAN_TEST_PREFIX}-${name}
PRIVATE
${PROJECT_NAME}::loader
${PROJECT_NAME}::headers
${PROJECT_NAME}::testing
${PROJECT_NAME}::mock
GTest::gtest_main)
endfunction()

function(set_sanitizer_test_properties name)
set_tests_properties(${name} PROPERTIES LABELS "sanitizer")
set_property(TEST ${name} PROPERTY ENVIRONMENT
"UR_LOG_SANITIZER=level:debug\;flush:debug\;output:stdout")
endfunction()

function(add_sanitizer_test name)
add_sanitizer_test_executable(${name} ${ARGN})

add_test(NAME ${name}
COMMAND ${SAN_TEST_PREFIX}-${name}
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})

set_sanitizer_test_properties(${name})
endfunction()

add_sanitizer_test(asan asan.cpp)
58 changes: 58 additions & 0 deletions test/layers/sanitizer/asan.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
*
* Copyright (C) 2024 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
*
* @file asan.cpp
*
*/

#include <gtest/gtest.h>
#include <ur_api.h>

TEST(DeviceAsan, Initialization) {
ur_result_t status;

ur_loader_config_handle_t loaderConfig;
status = urLoaderConfigCreate(&loaderConfig);
ASSERT_EQ(status, UR_RESULT_SUCCESS);
status = urLoaderConfigEnableLayer(loaderConfig, "UR_LAYER_ASAN");
ASSERT_EQ(status, UR_RESULT_SUCCESS);

status = urLoaderInit(0, loaderConfig);
ASSERT_EQ(status, UR_RESULT_SUCCESS);

ur_adapter_handle_t adapter;
status = urAdapterGet(1, &adapter, nullptr);
ASSERT_EQ(status, UR_RESULT_SUCCESS);

ur_platform_handle_t platform;
status = urPlatformGet(&adapter, 1, 1, &platform, nullptr);
ASSERT_EQ(status, UR_RESULT_SUCCESS);

ur_device_handle_t device;
status = urDeviceGet(platform, UR_DEVICE_TYPE_DEFAULT, 1, &device, nullptr);
ASSERT_EQ(status, UR_RESULT_SUCCESS);

ur_context_handle_t context;
status = urContextCreate(1, &device, nullptr, &context);
ASSERT_EQ(status, UR_RESULT_SUCCESS);

status = urContextRelease(context);
ASSERT_EQ(status, UR_RESULT_SUCCESS);

status = urDeviceRelease(device);
ASSERT_EQ(status, UR_RESULT_SUCCESS);

status = urAdapterRelease(adapter);
ASSERT_EQ(status, UR_RESULT_SUCCESS);

status = urLoaderTearDown();
ASSERT_EQ(status, UR_RESULT_SUCCESS);

status = urLoaderConfigRelease(loaderConfig);
ASSERT_EQ(status, UR_RESULT_SUCCESS);
}