Skip to content

Commit 45c3429

Browse files
authored
Merge pull request #1637 from yingcong-wu/yc/devicesan/fix-free-problems-0517
[DeviceSanitizer] refactor options handling and fix use-after-free related problems
2 parents b854c86 + 8bf5596 commit 45c3429

File tree

4 files changed

+188
-88
lines changed

4 files changed

+188
-88
lines changed

source/loader/layers/sanitizer/asan_interceptor.cpp

Lines changed: 15 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
*/
1313

1414
#include "asan_interceptor.hpp"
15+
#include "asan_options.hpp"
1516
#include "asan_quarantine.hpp"
1617
#include "asan_report.hpp"
1718
#include "asan_shadow_setup.hpp"
@@ -143,65 +144,9 @@ ur_result_t enqueueMemSetShadow(ur_context_handle_t Context,
143144
} // namespace
144145

145146
SanitizerInterceptor::SanitizerInterceptor() {
146-
auto Options = getenv_to_map("UR_LAYER_ASAN_OPTIONS");
147-
if (!Options.has_value()) {
148-
return;
149-
}
150-
151-
auto KV = Options->find("debug");
152-
if (KV != Options->end()) {
153-
auto Value = KV->second.front();
154-
cl_Debug = Value == "1" || Value == "true" ? 1 : 0;
155-
}
156-
157-
KV = Options->find("redzone");
158-
if (KV != Options->end()) {
159-
auto Value = KV->second.front();
160-
try {
161-
cl_MinRZSize = std::stoul(Value);
162-
if (cl_MinRZSize < 16) {
163-
cl_MinRZSize = 16;
164-
context.logger.warning("Trying to set redzone size to a value "
165-
"less than 16 is ignored");
166-
}
167-
} catch (...) {
168-
die("<SANITIZER>[ERROR]: \"redzone\" should be an integer");
169-
}
170-
}
171-
KV = Options->find("max_redzone");
172-
if (KV != Options->end()) {
173-
auto Value = KV->second.front();
174-
try {
175-
cl_MaxRZSize = std::stoul(Value);
176-
if (cl_MaxRZSize > 2048) {
177-
cl_MaxRZSize = 2048;
178-
context.logger.warning("Trying to set max redzone size to a "
179-
"value greater than 2048 is ignored");
180-
}
181-
} catch (...) {
182-
die("<SANITIZER>[ERROR]: \"max_redzone\" should be an integer");
183-
}
184-
}
185-
186-
KV = Options->find("quarantine_size_mb");
187-
if (KV != Options->end()) {
188-
auto Value = KV->second.front();
189-
try {
190-
cl_MaxQuarantineSizeMB = std::stoul(Value);
191-
} catch (...) {
192-
die("<SANITIZER>[ERROR]: \"cl_MaxQuarantineSizeMB\" should be an "
193-
"integer");
194-
}
195-
}
196-
if (cl_MaxQuarantineSizeMB) {
197-
m_Quarantine =
198-
std::make_unique<Quarantine>(cl_MaxQuarantineSizeMB * 1024 * 1024);
199-
}
200-
201-
KV = Options->find("detect_locals");
202-
if (KV != Options->end()) {
203-
auto Value = KV->second.front();
204-
cl_DetectLocals = Value == "1" || Value == "true" ? true : false;
147+
if (Options().MaxQuarantineSizeMB) {
148+
m_Quarantine = std::make_unique<Quarantine>(
149+
static_cast<uint64_t>(Options().MaxQuarantineSizeMB) * 1024 * 1024);
205150
}
206151
}
207152

@@ -241,7 +186,7 @@ ur_result_t SanitizerInterceptor::allocateMemory(
241186
Alignment = MinAlignment;
242187
}
243188

244-
uptr RZLog = ComputeRZLog(Size, cl_MinRZSize, cl_MaxRZSize);
189+
uptr RZLog = ComputeRZLog(Size, Options().MinRZSize, Options().MaxRZSize);
245190
uptr RZSize = RZLog2Size(RZLog);
246191
uptr RoundedSize = RoundUpTo(Size, Alignment);
247192
uptr NeededSize = RoundedSize + RZSize * 2;
@@ -746,7 +691,9 @@ ur_result_t SanitizerInterceptor::prepareLaunch(
746691
};
747692

748693
// Write debug
749-
EnqueueWriteGlobal(kSPIR_AsanDebug, &cl_Debug, sizeof(cl_Debug));
694+
// We use "uint64_t" here because EnqueueWriteGlobal will fail when it's "uint32_t"
695+
uint64_t Debug = Options().Debug ? 1 : 0;
696+
EnqueueWriteGlobal(kSPIR_AsanDebug, &Debug, sizeof(Debug));
750697

751698
// Write shadow memory offset for global memory
752699
EnqueueWriteGlobal(kSPIR_AsanShadowMemoryGlobalStart,
@@ -806,7 +753,7 @@ ur_result_t SanitizerInterceptor::prepareLaunch(
806753
};
807754

808755
// Write shadow memory offset for local memory
809-
if (cl_DetectLocals) {
756+
if (Options().DetectLocals) {
810757
// CPU needn't this
811758
if (DeviceInfo->Type == DeviceType::GPU_PVC) {
812759
size_t LocalMemorySize = GetLocalMemorySize(DeviceInfo->Handle);
@@ -843,7 +790,12 @@ SanitizerInterceptor::findAllocInfoByAddress(uptr Address) {
843790
if (It == m_AllocationMap.begin()) {
844791
return std::optional<AllocationIterator>{};
845792
}
846-
return --It;
793+
--It;
794+
// Make sure we got the right AllocInfo
795+
assert(Address >= It->second->AllocBegin &&
796+
Address < It->second->AllocBegin + It->second->AllocSize &&
797+
"Wrong AllocInfo for the address");
798+
return It;
847799
}
848800

849801
ur_result_t USMLaunchInfo::initialize() {

source/loader/layers/sanitizer/asan_interceptor.hpp

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -260,13 +260,6 @@ class SanitizerInterceptor {
260260
AllocationMap m_AllocationMap;
261261
ur_shared_mutex m_AllocationMapMutex;
262262

263-
// We use "uint64_t" here because EnqueueWriteGlobal will fail when it's "uint32_t"
264-
uint64_t cl_Debug = 0;
265-
uint64_t cl_MinRZSize = 16;
266-
uint64_t cl_MaxRZSize = 2048;
267-
uint32_t cl_MaxQuarantineSizeMB = 0;
268-
bool cl_DetectLocals = true;
269-
270263
std::unique_ptr<Quarantine> m_Quarantine;
271264
};
272265

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
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_options.hpp
10+
*
11+
*/
12+
13+
#pragma once
14+
15+
#include "common/ur_util.hpp"
16+
#include "ur/ur.hpp"
17+
#include "ur_sanitizer_layer.hpp"
18+
19+
#include <algorithm>
20+
#include <cstring>
21+
#include <stdexcept>
22+
23+
namespace ur_sanitizer_layer {
24+
25+
struct AsanOptions {
26+
public:
27+
AsanOptions(AsanOptions &other) = delete;
28+
void operator=(const AsanOptions &) = delete;
29+
30+
static AsanOptions &getInstance() {
31+
static AsanOptions instance;
32+
return instance;
33+
}
34+
35+
bool Debug = false;
36+
uint64_t MinRZSize = 16;
37+
uint64_t MaxRZSize = 2048;
38+
uint32_t MaxQuarantineSizeMB = 0;
39+
bool DetectLocals = true;
40+
41+
private:
42+
AsanOptions() {
43+
auto OptionsEnvMap = getenv_to_map("UR_LAYER_ASAN_OPTIONS");
44+
if (!OptionsEnvMap.has_value()) {
45+
return;
46+
}
47+
48+
const char *TrueStrings[] = {"1", "true"};
49+
const char *FalseStrings[] = {"0", "false"};
50+
51+
auto InplaceToLower = [](std::string &S) {
52+
std::transform(S.begin(), S.end(), S.begin(),
53+
[](unsigned char C) { return std::tolower(C); });
54+
};
55+
auto IsTrue = [&](const std::string &S) {
56+
return std::any_of(std::begin(TrueStrings), std::end(TrueStrings),
57+
[&](const char *CS) { return S == CS; });
58+
};
59+
auto IsFalse = [&](const std::string &S) {
60+
return std::any_of(std::begin(FalseStrings), std::end(FalseStrings),
61+
[&](const char *CS) { return S == CS; });
62+
};
63+
64+
auto SetBoolOption = [&](const std::string &Name, bool &Opt) {
65+
auto KV = OptionsEnvMap->find(Name);
66+
if (KV != OptionsEnvMap->end()) {
67+
auto Value = KV->second.front();
68+
InplaceToLower(Value);
69+
if (IsTrue(Value)) {
70+
Opt = true;
71+
} else if (IsFalse(Value)) {
72+
Opt = false;
73+
} else {
74+
std::stringstream SS;
75+
SS << "<SANITIZER>[ERROR]: \"" << Name << "\" is set to \""
76+
<< Value << "\", which is not an valid setting. ";
77+
SS << "Acceptable input are: for enable, use:";
78+
for (auto &S : TrueStrings) {
79+
SS << " \"" << S << "\"";
80+
}
81+
SS << "; ";
82+
SS << "for disable, use:";
83+
for (auto &S : FalseStrings) {
84+
SS << " \"" << S << "\"";
85+
}
86+
SS << ".";
87+
die(SS.str().c_str());
88+
}
89+
}
90+
};
91+
92+
SetBoolOption("debug", Debug);
93+
SetBoolOption("detect_locals", DetectLocals);
94+
95+
auto KV = OptionsEnvMap->find("quarantine_size_mb");
96+
if (KV != OptionsEnvMap->end()) {
97+
auto Value = KV->second.front();
98+
try {
99+
auto temp_long = std::stoul(Value);
100+
if (temp_long > UINT32_MAX) {
101+
throw std::out_of_range("");
102+
}
103+
MaxQuarantineSizeMB = temp_long;
104+
} catch (...) {
105+
die("<SANITIZER>[ERROR]: \"quarantine_size_mb\" should be "
106+
"an positive integer that smaller than or equal to "
107+
"4294967295.");
108+
}
109+
}
110+
111+
KV = OptionsEnvMap->find("redzone");
112+
if (KV != OptionsEnvMap->end()) {
113+
auto Value = KV->second.front();
114+
try {
115+
MinRZSize = std::stoul(Value);
116+
if (MinRZSize < 16) {
117+
MinRZSize = 16;
118+
context.logger.warning("Trying to set redzone size to a "
119+
"value less than 16 is ignored");
120+
}
121+
} catch (...) {
122+
die("<SANITIZER>[ERROR]: \"redzone\" should be an integer");
123+
}
124+
}
125+
126+
KV = OptionsEnvMap->find("max_redzone");
127+
if (KV != OptionsEnvMap->end()) {
128+
auto Value = KV->second.front();
129+
try {
130+
MaxRZSize = std::stoul(Value);
131+
if (MaxRZSize > 2048) {
132+
MaxRZSize = 2048;
133+
context.logger.warning(
134+
"Trying to set max redzone size to a "
135+
"value greater than 2048 is ignored");
136+
}
137+
} catch (...) {
138+
die("<SANITIZER>[ERROR]: \"max_redzone\" should be an integer");
139+
}
140+
}
141+
}
142+
};
143+
144+
inline const AsanOptions &Options() { return AsanOptions::getInstance(); }
145+
146+
} // namespace ur_sanitizer_layer

source/loader/layers/sanitizer/asan_report.cpp

Lines changed: 27 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
*/
1212

1313
#include "asan_report.hpp"
14+
#include "asan_options.hpp"
1415

1516
#include "asan_allocator.hpp"
1617
#include "asan_interceptor.hpp"
@@ -131,27 +132,35 @@ void ReportUseAfterFree(const DeviceSanitizerReport &Report,
131132
context.logger.always(" #0 {} {}:{}", Func, File, Report.Line);
132133
context.logger.always("");
133134

134-
auto AllocInfoItOp =
135-
context.interceptor->findAllocInfoByAddress(Report.Address);
136-
if (!AllocInfoItOp) {
137-
context.logger.always("Failed to find which chunck {} is allocated",
138-
(void *)Report.Address);
139-
} else {
140-
auto &AllocInfo = (*AllocInfoItOp)->second;
141-
if (AllocInfo->Context != Context) {
135+
if (Options().MaxQuarantineSizeMB > 0) {
136+
auto AllocInfoItOp =
137+
context.interceptor->findAllocInfoByAddress(Report.Address);
138+
139+
if (!AllocInfoItOp) {
142140
context.logger.always("Failed to find which chunck {} is allocated",
143141
(void *)Report.Address);
142+
} else {
143+
auto &AllocInfo = (*AllocInfoItOp)->second;
144+
if (AllocInfo->Context != Context) {
145+
context.logger.always(
146+
"Failed to find which chunck {} is allocated",
147+
(void *)Report.Address);
148+
}
149+
assert(AllocInfo->IsReleased);
150+
151+
context.logger.always(
152+
"{} is located inside of {} region [{}, {})",
153+
(void *)Report.Address, ToString(AllocInfo->Type),
154+
(void *)AllocInfo->UserBegin, (void *)AllocInfo->UserEnd);
155+
context.logger.always("allocated here:");
156+
AllocInfo->AllocStack.print();
157+
context.logger.always("released here:");
158+
AllocInfo->ReleaseStack.print();
144159
}
145-
assert(AllocInfo->IsReleased);
146-
147-
context.logger.always("{} is located inside of {} region [{}, {})",
148-
(void *)Report.Address, ToString(AllocInfo->Type),
149-
(void *)AllocInfo->UserBegin,
150-
(void *)AllocInfo->UserEnd);
151-
context.logger.always("allocated here:");
152-
AllocInfo->AllocStack.print();
153-
context.logger.always("released here:");
154-
AllocInfo->ReleaseStack.print();
160+
} else {
161+
context.logger.always(
162+
"Please enable quarantine to get more information like memory "
163+
"chunck's kind and where the chunck was allocated and released.");
155164
}
156165

157166
exit(1);

0 commit comments

Comments
 (0)