Skip to content

Commit d8058ed

Browse files
Merge pull request #1843 from AllanZyne/review/yang/invalid_arguments
[DeviceSanitizer] Support check invalid kernel argument
2 parents 1445b66 + 4e4b04c commit d8058ed

11 files changed

+292
-28
lines changed

source/loader/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,8 @@ if(UR_ENABLE_SANITIZER)
142142
${CMAKE_CURRENT_SOURCE_DIR}/layers/sanitizer/asan_report.hpp
143143
${CMAKE_CURRENT_SOURCE_DIR}/layers/sanitizer/asan_shadow_setup.cpp
144144
${CMAKE_CURRENT_SOURCE_DIR}/layers/sanitizer/asan_shadow_setup.hpp
145+
${CMAKE_CURRENT_SOURCE_DIR}/layers/sanitizer/asan_validator.cpp
146+
${CMAKE_CURRENT_SOURCE_DIR}/layers/sanitizer/asan_validator.hpp
145147
${CMAKE_CURRENT_SOURCE_DIR}/layers/sanitizer/common.hpp
146148
${CMAKE_CURRENT_SOURCE_DIR}/layers/sanitizer/stacktrace.cpp
147149
${CMAKE_CURRENT_SOURCE_DIR}/layers/sanitizer/stacktrace.hpp

source/loader/layers/sanitizer/asan_interceptor.cpp

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include "asan_quarantine.hpp"
1717
#include "asan_report.hpp"
1818
#include "asan_shadow_setup.hpp"
19+
#include "asan_validator.hpp"
1920
#include "stacktrace.hpp"
2021
#include "ur_sanitizer_utils.hpp"
2122

@@ -615,6 +616,9 @@ SanitizerInterceptor::insertDevice(ur_device_handle_t Device,
615616

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

619+
DI->IsSupportSharedSystemUSM = GetDeviceUSMCapability(
620+
Device, UR_DEVICE_INFO_USM_SYSTEM_SHARED_SUPPORT);
621+
618622
// Query alignment
619623
UR_CALL(getContext()->urDdiTable.Device.pfnGetInfo(
620624
Device, UR_DEVICE_INFO_MEM_BASE_ADDR_ALIGN, sizeof(DI->Alignment),
@@ -683,8 +687,25 @@ ur_result_t SanitizerInterceptor::prepareLaunch(
683687
auto Program = GetProgram(Kernel);
684688

685689
do {
686-
// Set membuffer arguments
687690
auto KernelInfo = getKernelInfo(Kernel);
691+
692+
// Validate pointer arguments
693+
if (Options(logger).DetectKernelArguments) {
694+
for (const auto &[ArgIndex, PtrPair] : KernelInfo->PointerArgs) {
695+
auto Ptr = PtrPair.first;
696+
if (Ptr == nullptr) {
697+
continue;
698+
}
699+
if (auto ValidateResult = ValidateUSMPointer(
700+
Context, DeviceInfo->Handle, (uptr)Ptr)) {
701+
ReportInvalidKernelArgument(Kernel, ArgIndex, (uptr)Ptr,
702+
ValidateResult, PtrPair.second);
703+
exit(1);
704+
}
705+
}
706+
}
707+
708+
// Set membuffer arguments
688709
for (const auto &[ArgIndex, MemBuffer] : KernelInfo->BufferArgs) {
689710
char *ArgPointer = nullptr;
690711
UR_CALL(MemBuffer->getHandle(DeviceInfo->Handle, ArgPointer));

source/loader/layers/sanitizer/asan_interceptor.hpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@ struct DeviceInfo {
4141
uptr ShadowOffset = 0;
4242
uptr ShadowOffsetEnd = 0;
4343

44+
// Device features
45+
bool IsSupportSharedSystemUSM;
46+
4447
ur_mutex Mutex;
4548
std::queue<std::shared_ptr<AllocInfo>> Quarantine;
4649
size_t QuarantineSize = 0;
@@ -78,6 +81,8 @@ struct KernelInfo {
7881
ur_shared_mutex Mutex;
7982
std::atomic<int32_t> RefCount = 1;
8083
std::unordered_map<uint32_t, std::shared_ptr<MemBuffer>> BufferArgs;
84+
std::unordered_map<uint32_t, std::pair<const void *, StackTrace>>
85+
PointerArgs;
8186

8287
// Need preserve the order of local arguments
8388
std::map<uint32_t, LocalArgsInfo> LocalArgs;

source/loader/layers/sanitizer/asan_options.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ struct AsanOptions {
3838
uint32_t MaxQuarantineSizeMB = 0;
3939
bool DetectLocals = true;
4040
bool DetectPrivates = true;
41+
bool DetectKernelArguments = true;
4142

4243
private:
4344
AsanOptions(logger::Logger &logger) {
@@ -93,6 +94,7 @@ struct AsanOptions {
9394
SetBoolOption("debug", Debug);
9495
SetBoolOption("detect_locals", DetectLocals);
9596
SetBoolOption("detect_privates", DetectPrivates);
97+
SetBoolOption("detect_kernel_arguments", DetectKernelArguments);
9698

9799
auto KV = OptionsEnvMap->find("quarantine_size_mb");
98100
if (KV != OptionsEnvMap->end()) {

source/loader/layers/sanitizer/asan_report.cpp

Lines changed: 66 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,32 @@
1111
*/
1212

1313
#include "asan_report.hpp"
14-
#include "asan_options.hpp"
15-
1614
#include "asan_allocator.hpp"
1715
#include "asan_interceptor.hpp"
1816
#include "asan_libdevice.hpp"
17+
#include "asan_options.hpp"
18+
#include "asan_validator.hpp"
1919
#include "ur_sanitizer_layer.hpp"
2020
#include "ur_sanitizer_utils.hpp"
2121

2222
namespace ur_sanitizer_layer {
2323

24+
namespace {
25+
26+
void PrintAllocateInfo(uptr Addr, const AllocInfo *AI) {
27+
getContext()->logger.always("{} is located inside of {} region [{}, {})",
28+
(void *)Addr, ToString(AI->Type),
29+
(void *)AI->UserBegin, (void *)AI->UserEnd);
30+
getContext()->logger.always("allocated here:");
31+
AI->AllocStack.print();
32+
if (AI->IsReleased) {
33+
getContext()->logger.always("freed here:");
34+
AI->ReleaseStack.print();
35+
}
36+
}
37+
38+
} // namespace
39+
2440
void ReportBadFree(uptr Addr, const StackTrace &stack,
2541
const std::shared_ptr<AllocInfo> &AI) {
2642
getContext()->logger.always(
@@ -34,11 +50,7 @@ void ReportBadFree(uptr Addr, const StackTrace &stack,
3450

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

37-
getContext()->logger.always("{} is located inside of {} region [{}, {})",
38-
(void *)Addr, ToString(AI->Type),
39-
(void *)AI->UserBegin, (void *)AI->UserEnd);
40-
getContext()->logger.always("allocated here:");
41-
AI->AllocStack.print();
53+
PrintAllocateInfo(Addr, AI.get());
4254
}
4355

4456
void ReportBadContext(uptr Addr, const StackTrace &stack,
@@ -48,16 +60,7 @@ void ReportBadContext(uptr Addr, const StackTrace &stack,
4860
(void *)Addr);
4961
stack.print();
5062

51-
getContext()->logger.always("{} is located inside of {} region [{}, {})",
52-
(void *)Addr, ToString(AI->Type),
53-
(void *)AI->UserBegin, (void *)AI->UserEnd);
54-
getContext()->logger.always("allocated here:");
55-
AI->AllocStack.print();
56-
57-
if (AI->IsReleased) {
58-
getContext()->logger.always("freed here:");
59-
AI->ReleaseStack.print();
60-
}
63+
PrintAllocateInfo(Addr, AI.get());
6164
}
6265

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

144-
getContext()->logger.always(
145-
"{} is located inside of {} region [{}, {})",
146-
(void *)Report.Address, ToString(AllocInfo->Type),
147-
(void *)AllocInfo->UserBegin, (void *)AllocInfo->UserEnd);
148-
getContext()->logger.always("allocated here:");
149-
AllocInfo->AllocStack.print();
150-
getContext()->logger.always("released here:");
151-
AllocInfo->ReleaseStack.print();
148+
PrintAllocateInfo(Report.Address, AllocInfo.get());
152149
}
153150
} else {
154151
getContext()->logger.always(
@@ -157,4 +154,47 @@ void ReportUseAfterFree(const DeviceSanitizerReport &Report,
157154
}
158155
}
159156

157+
void ReportInvalidKernelArgument(ur_kernel_handle_t Kernel, uint32_t ArgIndex,
158+
uptr Addr, const ValidateUSMResult &VR,
159+
StackTrace Stack) {
160+
getContext()->logger.always("\n====ERROR: DeviceSanitizer: "
161+
"invalid-argument on kernel <{}>",
162+
DemangleName(GetKernelName(Kernel)));
163+
Stack.print();
164+
auto &AI = VR.AI;
165+
switch (VR.Type) {
166+
case ValidateUSMResult::MAYBE_HOST_POINTER:
167+
getContext()->logger.always("The {}th argument {} is not a USM pointer",
168+
ArgIndex + 1, (void *)Addr);
169+
break;
170+
case ValidateUSMResult::RELEASED_POINTER:
171+
getContext()->logger.always(
172+
"The {}th argument {} is a released USM pointer", ArgIndex,
173+
(void *)Addr);
174+
PrintAllocateInfo(Addr, AI.get());
175+
break;
176+
case ValidateUSMResult::BAD_CONTEXT:
177+
getContext()->logger.always(
178+
"The {}th argument {} is allocated in other context", ArgIndex,
179+
(void *)Addr);
180+
PrintAllocateInfo(Addr, AI.get());
181+
break;
182+
case ValidateUSMResult::BAD_DEVICE:
183+
getContext()->logger.always(
184+
"The {}th argument {} is allocated in other device", ArgIndex,
185+
(void *)Addr);
186+
PrintAllocateInfo(Addr, AI.get());
187+
break;
188+
case ValidateUSMResult::OUT_OF_BOUNDS:
189+
getContext()->logger.always(
190+
"The {}th argument {} is located outside of its region [{}, {})",
191+
ArgIndex, (void *)Addr, (void *)AI->UserBegin, (void *)AI->UserEnd);
192+
getContext()->logger.always("allocated here:");
193+
AI->AllocStack.print();
194+
break;
195+
default:
196+
break;
197+
}
198+
}
199+
160200
} // namespace ur_sanitizer_layer

source/loader/layers/sanitizer/asan_report.hpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ namespace ur_sanitizer_layer {
2121
struct DeviceSanitizerReport;
2222
struct AllocInfo;
2323
struct StackTrace;
24+
struct ValidateUSMResult;
2425

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

44+
void ReportInvalidKernelArgument(ur_kernel_handle_t Kernel, uint32_t ArgIndex,
45+
uptr Addr, const ValidateUSMResult &VR,
46+
StackTrace Stack);
47+
4348
} // namespace ur_sanitizer_layer
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
/*
2+
*
3+
* Copyright (C) 2024 Intel Corporation
4+
*
5+
* Part of the Unified-Runtime Project, under the Apache License v2.0 with LLVM Exceptions.
6+
* See LICENSE.TXT
7+
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
8+
*
9+
* @file asan_validator.cpp
10+
*
11+
*/
12+
13+
#include "asan_validator.hpp"
14+
#include "asan_interceptor.hpp"
15+
#include "ur_sanitizer_utils.hpp"
16+
17+
namespace ur_sanitizer_layer {
18+
19+
namespace {
20+
21+
bool IsSameDevice(ur_device_handle_t Device1, ur_device_handle_t Device2) {
22+
if (Device1 == Device2) {
23+
return true;
24+
}
25+
auto RootDevice1 = GetParentDevice(Device1);
26+
RootDevice1 = RootDevice1 ? RootDevice1 : Device1;
27+
auto RootDevice2 = GetParentDevice(Device2);
28+
RootDevice2 = RootDevice2 ? RootDevice2 : Device2;
29+
if (RootDevice1 == RootDevice2) {
30+
return true;
31+
}
32+
return false;
33+
}
34+
35+
} // namespace
36+
37+
ValidateUSMResult ValidateUSMPointer(ur_context_handle_t Context,
38+
ur_device_handle_t Device, uptr Ptr) {
39+
assert(Ptr != 0 && "Don't validate nullptr here");
40+
41+
auto AllocInfoItOp = getContext()->interceptor->findAllocInfoByAddress(Ptr);
42+
if (!AllocInfoItOp) {
43+
auto DI = getContext()->interceptor->getDeviceInfo(Device);
44+
bool IsSupportSharedSystemUSM = DI->IsSupportSharedSystemUSM;
45+
if (IsSupportSharedSystemUSM) {
46+
// maybe it's host pointer
47+
return ValidateUSMResult::success();
48+
}
49+
return ValidateUSMResult::fail(ValidateUSMResult::MAYBE_HOST_POINTER);
50+
}
51+
52+
auto AllocInfo = AllocInfoItOp.value()->second;
53+
54+
if (AllocInfo->Context != Context) {
55+
return ValidateUSMResult::fail(ValidateUSMResult::BAD_CONTEXT,
56+
AllocInfo);
57+
}
58+
59+
if (AllocInfo->Device && !IsSameDevice(AllocInfo->Device, Device)) {
60+
return ValidateUSMResult::fail(ValidateUSMResult::BAD_DEVICE,
61+
AllocInfo);
62+
}
63+
64+
if (AllocInfo->IsReleased) {
65+
return ValidateUSMResult::fail(ValidateUSMResult::RELEASED_POINTER,
66+
AllocInfo);
67+
}
68+
69+
if (Ptr < AllocInfo->UserBegin || Ptr >= AllocInfo->UserEnd) {
70+
return ValidateUSMResult::fail(ValidateUSMResult::OUT_OF_BOUNDS,
71+
AllocInfo);
72+
}
73+
74+
return ValidateUSMResult::success();
75+
}
76+
77+
} // namespace ur_sanitizer_layer
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*
2+
*
3+
* Copyright (C) 2024 Intel Corporation
4+
*
5+
* Part of the Unified-Runtime Project, under the Apache License v2.0 with LLVM Exceptions.
6+
* See LICENSE.TXT
7+
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
8+
*
9+
* @file asan_validator.hpp
10+
*
11+
*/
12+
#pragma once
13+
14+
#include "asan_allocator.hpp"
15+
16+
namespace ur_sanitizer_layer {
17+
18+
struct ValidateUSMResult {
19+
enum ErrorType {
20+
SUCCESS,
21+
NULL_POINTER,
22+
MAYBE_HOST_POINTER,
23+
RELEASED_POINTER,
24+
BAD_CONTEXT,
25+
BAD_DEVICE,
26+
OUT_OF_BOUNDS
27+
};
28+
ErrorType Type;
29+
std::shared_ptr<AllocInfo> AI;
30+
31+
operator bool() { return Type != SUCCESS; }
32+
33+
static ValidateUSMResult success() { return {SUCCESS, nullptr}; }
34+
35+
static ValidateUSMResult fail(ErrorType Type,
36+
const std::shared_ptr<AllocInfo> &AI) {
37+
assert(Type != SUCCESS && "The error type shouldn't be SUCCESS");
38+
return {Type, AI};
39+
}
40+
41+
static ValidateUSMResult fail(ErrorType Type) {
42+
assert(Type != SUCCESS && "The error type shouldn't be SUCCESS");
43+
return {Type, nullptr};
44+
}
45+
};
46+
47+
ValidateUSMResult ValidateUSMPointer(ur_context_handle_t Context,
48+
ur_device_handle_t Device, uptr Ptr);
49+
50+
} // namespace ur_sanitizer_layer

0 commit comments

Comments
 (0)