Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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 @@ -142,6 +142,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
23 changes: 22 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 @@ -615,6 +616,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 alignment
UR_CALL(getContext()->urDdiTable.Device.pfnGetInfo(
Device, UR_DEVICE_INFO_MEM_BASE_ADDR_ALIGN, sizeof(DI->Alignment),
Expand Down Expand Up @@ -683,8 +687,25 @@ ur_result_t SanitizerInterceptor::prepareLaunch(
auto Program = GetProgram(Kernel);

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

// Validate pointer arguments
if (Options(logger).DetectKernelArguments) {
for (const auto &[ArgIndex, PtrPair] : KernelInfo->PointerArgs) {
auto Ptr = PtrPair.first;
if (Ptr == nullptr) {
continue;
}
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
2 changes: 2 additions & 0 deletions source/loader/layers/sanitizer/asan_options.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ struct AsanOptions {
uint32_t MaxQuarantineSizeMB = 0;
bool DetectLocals = true;
bool DetectPrivates = true;
bool DetectKernelArguments = true;

private:
AsanOptions(logger::Logger &logger) {
Expand Down Expand Up @@ -93,6 +94,7 @@ struct AsanOptions {
SetBoolOption("debug", Debug);
SetBoolOption("detect_locals", DetectLocals);
SetBoolOption("detect_privates", DetectPrivates);
SetBoolOption("detect_kernel_arguments", DetectKernelArguments);

auto KV = OptionsEnvMap->find("quarantine_size_mb");
if (KV != OptionsEnvMap->end()) {
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 + 1, (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 (AllocInfo->Device && !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