Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions source/loader/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,8 @@ if(UR_ENABLE_SANITIZER)
${CMAKE_CURRENT_SOURCE_DIR}/layers/sanitizer/asan_report.hpp
${CMAKE_CURRENT_SOURCE_DIR}/layers/sanitizer/asan_shadow_setup.cpp
${CMAKE_CURRENT_SOURCE_DIR}/layers/sanitizer/asan_shadow_setup.hpp
${CMAKE_CURRENT_SOURCE_DIR}/layers/sanitizer/asan_validator.cpp
${CMAKE_CURRENT_SOURCE_DIR}/layers/sanitizer/asan_validator.hpp
${CMAKE_CURRENT_SOURCE_DIR}/layers/sanitizer/common.hpp
${CMAKE_CURRENT_SOURCE_DIR}/layers/sanitizer/stacktrace.cpp
${CMAKE_CURRENT_SOURCE_DIR}/layers/sanitizer/stacktrace.hpp
Expand Down
21 changes: 20 additions & 1 deletion source/loader/layers/sanitizer/asan_interceptor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "asan_quarantine.hpp"
#include "asan_report.hpp"
#include "asan_shadow_setup.hpp"
#include "asan_validator.hpp"
#include "stacktrace.hpp"
#include "ur_sanitizer_utils.hpp"

Expand Down Expand Up @@ -586,6 +587,9 @@ SanitizerInterceptor::insertDevice(ur_device_handle_t Device,

DI = std::make_shared<ur_sanitizer_layer::DeviceInfo>(Device);

DI->IsSupportSharedSystemUSM = GetDeviceUSMCapability(
Device, UR_DEVICE_INFO_USM_SYSTEM_SHARED_SUPPORT);

// Query device type
DI->Type = GetDeviceType(Device);
if (DI->Type == DeviceType::UNKNOWN) {
Expand All @@ -597,6 +601,9 @@ SanitizerInterceptor::insertDevice(ur_device_handle_t Device,
Device, UR_DEVICE_INFO_MEM_BASE_ADDR_ALIGN, sizeof(DI->Alignment),
&DI->Alignment, nullptr));

getContext()->logger.info("DeviceInfo {} (IsSupportSharedSystemUSM={})",
(void *)Device, DI->IsSupportSharedSystemUSM);

// Don't move DI, since it's a return value as well
m_DeviceMap.emplace(Device, DI);

Expand Down Expand Up @@ -660,8 +667,20 @@ ur_result_t SanitizerInterceptor::prepareLaunch(
auto Program = GetProgram(Kernel);

do {
// Set membuffer arguments
auto KernelInfo = getKernelInfo(Kernel);

// Validate pointer arguments
for (const auto &[ArgIndex, PtrPair] : KernelInfo->PointerArgs) {
auto Ptr = PtrPair.first;
if (auto ValidateResult = ValidateUSMPointer(
Context, DeviceInfo->Handle, (uptr)Ptr)) {
ReportInvalidKernelArgument(Kernel, ArgIndex, (uptr)Ptr,
ValidateResult, PtrPair.second);
exit(1);
}
}

// Set membuffer arguments
for (const auto &[ArgIndex, MemBuffer] : KernelInfo->BufferArgs) {
char *ArgPointer = nullptr;
UR_CALL(MemBuffer->getHandle(DeviceInfo->Handle, ArgPointer));
Expand Down
5 changes: 5 additions & 0 deletions source/loader/layers/sanitizer/asan_interceptor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ struct DeviceInfo {
uptr ShadowOffset = 0;
uptr ShadowOffsetEnd = 0;

// Device features
bool IsSupportSharedSystemUSM;

ur_mutex Mutex;
std::queue<std::shared_ptr<AllocInfo>> Quarantine;
size_t QuarantineSize = 0;
Expand Down Expand Up @@ -85,6 +88,8 @@ struct KernelInfo {
ur_shared_mutex Mutex;
std::atomic<int32_t> RefCount = 1;
std::unordered_map<uint32_t, std::shared_ptr<MemBuffer>> BufferArgs;
std::unordered_map<uint32_t, std::pair<const void *, StackTrace>>
PointerArgs;

// Need preserve the order of local arguments
std::map<uint32_t, LocalArgsInfo> LocalArgs;
Expand Down
92 changes: 66 additions & 26 deletions source/loader/layers/sanitizer/asan_report.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,32 @@
*/

#include "asan_report.hpp"
#include "asan_options.hpp"

#include "asan_allocator.hpp"
#include "asan_interceptor.hpp"
#include "asan_libdevice.hpp"
#include "asan_options.hpp"
#include "asan_validator.hpp"
#include "ur_sanitizer_layer.hpp"
#include "ur_sanitizer_utils.hpp"

namespace ur_sanitizer_layer {

namespace {

void PrintAllocateInfo(uptr Addr, const AllocInfo *AI) {
getContext()->logger.always("{} is located inside of {} region [{}, {})",
(void *)Addr, ToString(AI->Type),
(void *)AI->UserBegin, (void *)AI->UserEnd);
getContext()->logger.always("allocated here:");
AI->AllocStack.print();
if (AI->IsReleased) {
getContext()->logger.always("freed here:");
AI->ReleaseStack.print();
}
}

} // namespace

void ReportBadFree(uptr Addr, const StackTrace &stack,
const std::shared_ptr<AllocInfo> &AI) {
getContext()->logger.always(
Expand All @@ -34,11 +50,7 @@ void ReportBadFree(uptr Addr, const StackTrace &stack,

assert(!AI->IsReleased && "Chunk must be not released");

getContext()->logger.always("{} is located inside of {} region [{}, {})",
(void *)Addr, ToString(AI->Type),
(void *)AI->UserBegin, (void *)AI->UserEnd);
getContext()->logger.always("allocated here:");
AI->AllocStack.print();
PrintAllocateInfo(Addr, AI.get());
}

void ReportBadContext(uptr Addr, const StackTrace &stack,
Expand All @@ -48,16 +60,7 @@ void ReportBadContext(uptr Addr, const StackTrace &stack,
(void *)Addr);
stack.print();

getContext()->logger.always("{} is located inside of {} region [{}, {})",
(void *)Addr, ToString(AI->Type),
(void *)AI->UserBegin, (void *)AI->UserEnd);
getContext()->logger.always("allocated here:");
AI->AllocStack.print();

if (AI->IsReleased) {
getContext()->logger.always("freed here:");
AI->ReleaseStack.print();
}
PrintAllocateInfo(Addr, AI.get());
}

void ReportDoubleFree(uptr Addr, const StackTrace &Stack,
Expand Down Expand Up @@ -139,16 +142,10 @@ void ReportUseAfterFree(const DeviceSanitizerReport &Report,
"Failed to find which chunck {} is allocated",
(void *)Report.Address);
}
assert(AllocInfo->IsReleased);
assert(AllocInfo->IsReleased &&
"It must be released since it's use-after-free");

getContext()->logger.always(
"{} is located inside of {} region [{}, {})",
(void *)Report.Address, ToString(AllocInfo->Type),
(void *)AllocInfo->UserBegin, (void *)AllocInfo->UserEnd);
getContext()->logger.always("allocated here:");
AllocInfo->AllocStack.print();
getContext()->logger.always("released here:");
AllocInfo->ReleaseStack.print();
PrintAllocateInfo(Report.Address, AllocInfo.get());
}
} else {
getContext()->logger.always(
Expand All @@ -157,4 +154,47 @@ void ReportUseAfterFree(const DeviceSanitizerReport &Report,
}
}

void ReportInvalidKernelArgument(ur_kernel_handle_t Kernel, uint32_t ArgIndex,
uptr Addr, const ValidateUSMResult &VR,
StackTrace Stack) {
getContext()->logger.always("\n====ERROR: DeviceSanitizer: "
"invalid-argument on kernel <{}>",
DemangleName(GetKernelName(Kernel)));
Stack.print();
auto &AI = VR.AI;
switch (VR.Type) {
case ValidateUSMResult::MAYBE_HOST_POINTER:
getContext()->logger.always("The {}th argument {} is not a USM pointer",
Copy link
Contributor

@pbalcer pbalcer Jul 25, 2024

Choose a reason for hiding this comment

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for reminding me :)
I fall into programmer's mindset.

ArgIndex, (void *)Addr);
break;
case ValidateUSMResult::RELEASED_POINTER:
getContext()->logger.always(
"The {}th argument {} is a released USM pointer", ArgIndex,
(void *)Addr);
PrintAllocateInfo(Addr, AI.get());
break;
case ValidateUSMResult::BAD_CONTEXT:
getContext()->logger.always(
"The {}th argument {} is allocated in other context", ArgIndex,
(void *)Addr);
PrintAllocateInfo(Addr, AI.get());
break;
case ValidateUSMResult::BAD_DEVICE:
getContext()->logger.always(
"The {}th argument {} is allocated in other device", ArgIndex,
(void *)Addr);
PrintAllocateInfo(Addr, AI.get());
break;
case ValidateUSMResult::OUT_OF_BOUNDS:
getContext()->logger.always(
"The {}th argument {} is located outside of its region [{}, {})",
ArgIndex, (void *)Addr, (void *)AI->UserBegin, (void *)AI->UserEnd);
getContext()->logger.always("allocated here:");
AI->AllocStack.print();
break;
default:
break;
}
}

} // namespace ur_sanitizer_layer
5 changes: 5 additions & 0 deletions source/loader/layers/sanitizer/asan_report.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ namespace ur_sanitizer_layer {
struct DeviceSanitizerReport;
struct AllocInfo;
struct StackTrace;
struct ValidateUSMResult;

void ReportBadFree(uptr Addr, const StackTrace &stack,
const std::shared_ptr<AllocInfo> &AllocInfo);
Expand All @@ -40,4 +41,8 @@ void ReportGenericError(const DeviceSanitizerReport &Report,
void ReportUseAfterFree(const DeviceSanitizerReport &Report,
ur_kernel_handle_t Kernel, ur_context_handle_t Context);

void ReportInvalidKernelArgument(ur_kernel_handle_t Kernel, uint32_t ArgIndex,
uptr Addr, const ValidateUSMResult &VR,
StackTrace Stack);

} // namespace ur_sanitizer_layer
77 changes: 77 additions & 0 deletions source/loader/layers/sanitizer/asan_validator.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*
*
* 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_validator.cpp
*
*/

#include "asan_validator.hpp"
#include "asan_interceptor.hpp"
#include "ur_sanitizer_utils.hpp"

namespace ur_sanitizer_layer {

namespace {

bool IsSameDevice(ur_device_handle_t Device1, ur_device_handle_t Device2) {
if (Device1 == Device2) {
return true;
}
auto RootDevice1 = GetParentDevice(Device1);
RootDevice1 = RootDevice1 ? RootDevice1 : Device1;
auto RootDevice2 = GetParentDevice(Device2);
RootDevice2 = RootDevice2 ? RootDevice2 : Device2;
if (RootDevice1 == RootDevice2) {
return true;
}
return false;
}

} // namespace

ValidateUSMResult ValidateUSMPointer(ur_context_handle_t Context,
ur_device_handle_t Device, uptr Ptr) {
assert(Ptr != 0 && "Don't validate nullptr here");

auto AllocInfoItOp = getContext()->interceptor->findAllocInfoByAddress(Ptr);
if (!AllocInfoItOp) {
auto DI = getContext()->interceptor->getDeviceInfo(Device);
bool IsSupportSharedSystemUSM = DI->IsSupportSharedSystemUSM;
if (IsSupportSharedSystemUSM) {
// maybe it's host pointer
return ValidateUSMResult::success();
}
return ValidateUSMResult::fail(ValidateUSMResult::MAYBE_HOST_POINTER);
}

auto AllocInfo = AllocInfoItOp.value()->second;

if (AllocInfo->Context != Context) {
return ValidateUSMResult::fail(ValidateUSMResult::BAD_CONTEXT,
AllocInfo);
}

if (!IsSameDevice(AllocInfo->Device, Device)) {
return ValidateUSMResult::fail(ValidateUSMResult::BAD_DEVICE,
AllocInfo);
}

if (AllocInfo->IsReleased) {
return ValidateUSMResult::fail(ValidateUSMResult::RELEASED_POINTER,
AllocInfo);
}

if (Ptr < AllocInfo->UserBegin || Ptr >= AllocInfo->UserEnd) {
return ValidateUSMResult::fail(ValidateUSMResult::OUT_OF_BOUNDS,
AllocInfo);
}

return ValidateUSMResult::success();
}

} // namespace ur_sanitizer_layer
50 changes: 50 additions & 0 deletions source/loader/layers/sanitizer/asan_validator.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
*
* 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_validator.hpp
*
*/
#pragma once

#include "asan_allocator.hpp"

namespace ur_sanitizer_layer {

struct ValidateUSMResult {
enum ErrorType {
SUCCESS,
NULL_POINTER,
MAYBE_HOST_POINTER,
RELEASED_POINTER,
BAD_CONTEXT,
BAD_DEVICE,
OUT_OF_BOUNDS
};
ErrorType Type;
std::shared_ptr<AllocInfo> AI;

operator bool() { return Type != SUCCESS; }

static ValidateUSMResult success() { return {SUCCESS, nullptr}; }

static ValidateUSMResult fail(ErrorType Type,
const std::shared_ptr<AllocInfo> &AI) {
assert(Type != SUCCESS && "The error type shouldn't be SUCCESS");
return {Type, AI};
}

static ValidateUSMResult fail(ErrorType Type) {
assert(Type != SUCCESS && "The error type shouldn't be SUCCESS");
return {Type, nullptr};
}
};

ValidateUSMResult ValidateUSMPointer(ur_context_handle_t Context,
ur_device_handle_t Device, uptr Ptr);

} // namespace ur_sanitizer_layer
Loading