Skip to content

Commit 9ad2e87

Browse files
authored
[DeviceMSAN] Check use-of-uninitialized value on private memory (#17309)
Support check use-of-uninitialized value on private memory
1 parent 20f9b53 commit 9ad2e87

File tree

15 files changed

+503
-151
lines changed

15 files changed

+503
-151
lines changed

clang/lib/Driver/SanitizerArgs.cpp

+3
Original file line numberDiff line numberDiff line change
@@ -1278,6 +1278,9 @@ void SanitizerArgs::addArgs(const ToolChain &TC, const llvm::opt::ArgList &Args,
12781278

12791279
CmdArgs.push_back("-mllvm");
12801280
CmdArgs.push_back("-msan-eager-checks=1");
1281+
1282+
CmdArgs.push_back("-mllvm");
1283+
CmdArgs.push_back("-msan-poison-stack-with-call=1");
12811284
} else if (Sanitizers.has(SanitizerKind::Thread)) {
12821285
CmdArgs.push_back("-fsanitize=thread");
12831286
// The tsan function entry/exit builtins are used to record stack

libdevice/sanitizer/asan_rtl.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -881,6 +881,8 @@ static __SYCL_CONSTANT__ const char __mem_set_shadow_private_end[] =
881881
static __SYCL_CONSTANT__ const char __mem_set_shadow_private[] =
882882
"[kernel] set_shadow_private(beg=%p, end=%p, val:%02X)\n";
883883

884+
// We outline the function of setting shadow memory of private memory, because
885+
// it may allocate failed on UR
884886
DEVICE_EXTERN_C_NOINLINE void __asan_set_shadow_private(uptr begin, uptr size,
885887
char val) {
886888
if (!__AsanLaunchInfo)

libdevice/sanitizer/msan_rtl.cpp

+121-35
Large diffs are not rendered by default.

llvm/lib/Transforms/Instrumentation/MemorySanitizer.cpp

+214-81
Large diffs are not rendered by default.

llvm/test/Instrumentation/MemorySanitizer/SPIRV/check_large_access_size.ll

-19
This file was deleted.

llvm/test/Instrumentation/MemorySanitizer/SPIRV/check_unsupported_access.ll

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
; RUN: opt < %s -passes=msan -msan-instrumentation-with-call-threshold=0 -msan-eager-checks=1 -S | FileCheck %s
1+
; RUN: opt < %s -passes=msan -msan-instrumentation-with-call-threshold=0 -msan-eager-checks=1 -msan-spir-privates=0 -S | FileCheck %s
22
target datalayout = "e-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-n8:16:32:64-G1"
33
target triple = "spir64-unknown-unknown"
44

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
; RUN: opt < %s -passes=msan -msan-instrumentation-with-call-threshold=0 -msan-eager-checks=1 -msan-spir-privates=1 -msan-poison-stack-with-call=1 -S | FileCheck %s
2+
target datalayout = "e-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-n8:16:32:64-G1"
3+
target triple = "spir64-unknown-unknown"
4+
5+
define spir_kernel void @MyKernel() sanitize_memory {
6+
; CHECK-LABEL: @MyKernel
7+
entry:
8+
%array = alloca [4 x i32], align 4
9+
; CHECK: call void @__msan_poison_stack(ptr %array, i64 16)
10+
ret void
11+
}
12+
13+
%"class.sycl::_V1::range" = type { %"class.sycl::_V1::detail::array" }
14+
%"class.sycl::_V1::detail::array" = type { [1 x i64] }
15+
16+
define spir_func void @ByValFunc(ptr noundef byval(%"class.sycl::_V1::range") align 8 %_arg_array12) sanitize_memory {
17+
; CHECK-LABEL: @ByValFunc
18+
entry:
19+
; CHECK: %0 = ptrtoint ptr %_arg_array12 to i64
20+
; CHECK: %1 = call i64 @__msan_get_shadow(i64 %0, i32 0, ptr addrspace(2) null)
21+
; CHECK: %2 = inttoptr i64 %1 to ptr addrspace(1)
22+
; CHECK: call void @llvm.memset.p1.i64(ptr addrspace(1) align 8 %2, i8 0, i64 8, i1 false)
23+
%_arg_array12.ascast = addrspacecast ptr %_arg_array12 to ptr addrspace(4)
24+
ret void
25+
}
26+
27+
define spir_kernel void @ByValKernel(ptr noundef byval(%"class.sycl::_V1::range") align 8 %_arg_array12) sanitize_memory {
28+
; CHECK-LABEL: @ByValKernel
29+
entry:
30+
; CHECK: %_arg_array12.byval = alloca %"class.sycl::_V1::range", align 8
31+
; CHECK: call void @__msan_unpoison_stack(ptr %_arg_array12.byval, i64 8), !nosanitize
32+
; CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 8 %_arg_array12.byval, ptr align 8 %_arg_array12, i64 8, i1 false), !nosanitize
33+
call void @ByValFunc(ptr %_arg_array12)
34+
ret void
35+
}

llvm/test/Instrumentation/MemorySanitizer/SPIRV/instrument_static_local_mem.ll

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
; RUN: opt < %s -passes=msan -msan-instrumentation-with-call-threshold=0 -msan-eager-checks=1 -msan-spir-locals=1 -S | FileCheck %s
1+
; RUN: opt < %s -passes=msan -msan-instrumentation-with-call-threshold=0 -msan-eager-checks=1 -msan-spir-locals=1 -msan-spir-privates=0 -S | FileCheck %s
22
target datalayout = "e-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-n8:16:32:64-G1"
33
target triple = "spir64-unknown-unknown"
44

sycl/test-e2e/MemorySanitizer/check_buffer.cpp

+2-5
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,17 @@
88

99
#include <sycl/detail/core.hpp>
1010

11-
__attribute__((noinline)) long long foo(int data1, long long data2) {
11+
__attribute__((noinline)) int foo(int data1, int data2) {
1212
return data1 + data2;
1313
}
1414

1515
int main() {
1616
sycl::queue q;
1717

1818
sycl::buffer<int, 1> buf1(sycl::range<1>(1));
19-
sycl::buffer<long long, 1> buf2(sycl::range<1>(1));
2019
q.submit([&](sycl::handler &h) {
2120
auto array1 = buf1.get_access<sycl::access::mode::read_write>(h);
22-
auto array2 = buf2.get_access<sycl::access::mode::read_write>(h);
23-
h.single_task<class MyKernel>(
24-
[=]() { array1[0] = foo(array1[0], array2[0]); });
21+
h.single_task<class MyKernel>([=]() { foo(array1[0], array1[0]); });
2522
}).wait();
2623
// CHECK: use-of-uninitialized-value
2724
// CHECK: kernel <{{.*MyKernel}}>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// REQUIRES: linux, cpu || (gpu && level_zero)
2+
// RUN: %{build} %device_msan_flags -O0 -g -o %t1.out
3+
// RUN: %{run} not %t1.out 2>&1 | FileCheck %s
4+
// RUN: %{build} %device_msan_flags -O1 -g -o %t2.out
5+
// RUN: %{run} not %t2.out 2>&1 | FileCheck %s
6+
// RUN: %{build} %device_msan_flags -O2 -g -o %t3.out
7+
// RUN: %{run} not %t3.out 2>&1 | FileCheck %s
8+
9+
#include <sycl/detail/core.hpp>
10+
#include <sycl/usm.hpp>
11+
12+
__attribute__((noinline)) int check(int p) { return p; }
13+
__attribute__((noinline)) int foo(int *p) { return check(*p); }
14+
// CHECK-NOT: [kernel]
15+
// CHECK: DeviceSanitizer: use-of-uninitialized-value
16+
// CHECK: #0 {{foo.*}} {{.*single_private.cpp}}:[[@LINE-3]]
17+
18+
int main() {
19+
sycl::queue Q;
20+
auto *array = sycl::malloc_device<int>(1, Q);
21+
22+
Q.submit([&](sycl::handler &h) {
23+
h.single_task<class MyKernel>([=]() {
24+
int p[4];
25+
*array += foo(p);
26+
});
27+
});
28+
Q.wait();
29+
30+
sycl::free(array, Q);
31+
return 0;
32+
}

unified-runtime/source/loader/layers/sanitizer/msan/msan_interceptor.cpp

+27-7
Original file line numberDiff line numberDiff line change
@@ -517,10 +517,30 @@ ur_result_t MsanInterceptor::prepareLaunch(
517517
getContext()->logger.warning("Skip checking local memory of kernel <{}> ",
518518
GetKernelName(Kernel));
519519
} else {
520-
getContext()->logger.info("ShadowMemory(Local, WorkGroup={}, {} - {})",
521-
NumWG,
522-
(void *)LaunchInfo.Data->LocalShadowOffset,
523-
(void *)LaunchInfo.Data->LocalShadowOffsetEnd);
520+
getContext()->logger.debug("ShadowMemory(Local, WorkGroup={}, {} - {})",
521+
NumWG,
522+
(void *)LaunchInfo.Data->LocalShadowOffset,
523+
(void *)LaunchInfo.Data->LocalShadowOffsetEnd);
524+
}
525+
}
526+
527+
// Write shadow memory offset for private memory
528+
if (KernelInfo.IsCheckPrivates) {
529+
if (DeviceInfo->Shadow->AllocPrivateShadow(
530+
Queue, NumWG, LaunchInfo.Data->PrivateShadowOffset,
531+
LaunchInfo.Data->PrivateShadowOffsetEnd) != UR_RESULT_SUCCESS) {
532+
getContext()->logger.warning(
533+
"Failed to allocate shadow memory for private "
534+
"memory, maybe the number of workgroup ({}) is too "
535+
"large",
536+
NumWG);
537+
getContext()->logger.warning(
538+
"Skip checking private memory of kernel <{}>", GetKernelName(Kernel));
539+
} else {
540+
getContext()->logger.debug(
541+
"ShadowMemory(Private, WorkGroup={}, {} - {})", NumWG,
542+
(void *)LaunchInfo.Data->PrivateShadowOffset,
543+
(void *)LaunchInfo.Data->PrivateShadowOffsetEnd);
524544
}
525545
// Write local arguments info
526546
if (!KernelInfo.LocalArgs.empty()) {
@@ -535,11 +555,11 @@ ur_result_t MsanInterceptor::prepareLaunch(
535555
}
536556

537557
getContext()->logger.info(
538-
"LaunchInfo {} (GlobalShadow={}, LocalShadow={}, CleanShadow={}, "
539-
"LocalArgs={}, NumLocalArgs={}, "
540-
"Device={}, Debug={})",
558+
"LaunchInfo {} (GlobalShadow={}, LocalShadow={}, PrivateShadow={}, "
559+
"CleanShadow={}, LocalArgs={}, NumLocalArgs={}, Device={}, Debug={})",
541560
(void *)LaunchInfo.Data, (void *)LaunchInfo.Data->GlobalShadowOffset,
542561
(void *)LaunchInfo.Data->LocalShadowOffset,
562+
(void *)LaunchInfo.Data->PrivateShadowOffset,
543563
(void *)LaunchInfo.Data->CleanShadow, (void *)LaunchInfo.Data->LocalArgs,
544564
LaunchInfo.Data->NumLocalArgs, ToString(LaunchInfo.Data->DeviceTy),
545565
LaunchInfo.Data->Debug);

unified-runtime/source/loader/layers/sanitizer/msan/msan_libdevice.hpp

+4
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ struct MsanErrorReport {
3737

3838
uint32_t AccessSize = 0;
3939
ErrorType ErrorTy = ErrorType::UNKNOWN;
40+
uintptr_t Origin;
4041
};
4142

4243
struct MsanLocalArgsInfo {
@@ -50,6 +51,9 @@ struct MsanLaunchInfo {
5051
uintptr_t LocalShadowOffset = 0;
5152
uintptr_t LocalShadowOffsetEnd = 0;
5253

54+
uintptr_t PrivateShadowOffset = 0;
55+
uintptr_t PrivateShadowOffsetEnd = 0;
56+
5357
uintptr_t CleanShadow = 0;
5458

5559
DeviceType DeviceTy = DeviceType::UNKNOWN;

unified-runtime/source/loader/layers/sanitizer/msan/msan_report.cpp

+9-2
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,15 @@ void ReportUsesUninitializedValue(const MsanErrorReport &Report,
3030
// Try to demangle the kernel name
3131
KernelName = DemangleName(KernelName);
3232

33-
getContext()->logger.always(
34-
"====WARNING: DeviceSanitizer: use-of-uninitialized-value");
33+
if (Report.Origin) {
34+
getContext()->logger.always(
35+
"====WARNING: DeviceSanitizer: use-of-uninitialized-value (shadow: {})",
36+
(void *)Report.Origin);
37+
} else {
38+
getContext()->logger.always(
39+
"====WARNING: DeviceSanitizer: use-of-uninitialized-value)");
40+
}
41+
3542
getContext()->logger.always(
3643
"use of size {} at kernel <{}> LID({}, {}, {}) GID({}, "
3744
"{}, {})",

unified-runtime/source/loader/layers/sanitizer/msan/msan_shadow.cpp

+36
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,42 @@ ur_result_t MsanShadowMemoryGPU::AllocLocalShadow(ur_queue_handle_t Queue,
333333
return UR_RESULT_SUCCESS;
334334
}
335335

336+
ur_result_t MsanShadowMemoryGPU::AllocPrivateShadow(ur_queue_handle_t Queue,
337+
uint32_t NumWG, uptr &Begin,
338+
uptr &End) {
339+
const size_t RequiredShadowSize = NumWG * MSAN_PRIVATE_SIZE;
340+
static size_t LastAllocedSize = 0;
341+
if (RequiredShadowSize > LastAllocedSize) {
342+
auto ContextInfo = getMsanInterceptor()->getContextInfo(Context);
343+
if (PrivateShadowOffset) {
344+
UR_CALL(getContext()->urDdiTable.USM.pfnFree(
345+
Context, (void *)PrivateShadowOffset));
346+
PrivateShadowOffset = 0;
347+
LastAllocedSize = 0;
348+
}
349+
350+
UR_CALL(getContext()->urDdiTable.USM.pfnDeviceAlloc(
351+
Context, Device, nullptr, nullptr, RequiredShadowSize,
352+
(void **)&PrivateShadowOffset));
353+
354+
// Initialize shadow memory
355+
ur_result_t URes = EnqueueUSMBlockingSet(Queue, (void *)PrivateShadowOffset,
356+
0, RequiredShadowSize);
357+
if (URes != UR_RESULT_SUCCESS) {
358+
UR_CALL(getContext()->urDdiTable.USM.pfnFree(
359+
Context, (void *)PrivateShadowOffset));
360+
PrivateShadowOffset = 0;
361+
LastAllocedSize = 0;
362+
}
363+
364+
LastAllocedSize = RequiredShadowSize;
365+
}
366+
367+
Begin = PrivateShadowOffset;
368+
End = PrivateShadowOffset + RequiredShadowSize - 1;
369+
return UR_RESULT_SUCCESS;
370+
}
371+
336372
uptr MsanShadowMemoryPVC::MemToShadow(uptr Ptr) {
337373
assert(MsanShadowMemoryPVC::IsDeviceUSM(Ptr) && "Ptr must be device USM");
338374
if (Ptr < ShadowBegin) {

unified-runtime/source/loader/layers/sanitizer/msan/msan_shadow.hpp

+16
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,10 @@ struct MsanShadowMemory {
4646
virtual ur_result_t AllocLocalShadow(ur_queue_handle_t Queue, uint32_t NumWG,
4747
uptr &Begin, uptr &End) = 0;
4848

49+
virtual ur_result_t AllocPrivateShadow(ur_queue_handle_t Queue,
50+
uint32_t NumWG, uptr &Begin,
51+
uptr &End) = 0;
52+
4953
ur_context_handle_t Context{};
5054

5155
ur_device_handle_t Device{};
@@ -95,6 +99,13 @@ struct MsanShadowMemoryCPU final : public MsanShadowMemory {
9599
End = ShadowEnd;
96100
return UR_RESULT_SUCCESS;
97101
}
102+
103+
ur_result_t AllocPrivateShadow(ur_queue_handle_t, uint32_t, uptr &Begin,
104+
uptr &End) override {
105+
Begin = ShadowBegin;
106+
End = ShadowEnd;
107+
return UR_RESULT_SUCCESS;
108+
}
98109
};
99110

100111
struct MsanShadowMemoryGPU : public MsanShadowMemory {
@@ -116,6 +127,9 @@ struct MsanShadowMemoryGPU : public MsanShadowMemory {
116127
ur_result_t AllocLocalShadow(ur_queue_handle_t Queue, uint32_t NumWG,
117128
uptr &Begin, uptr &End) override final;
118129

130+
ur_result_t AllocPrivateShadow(ur_queue_handle_t Queue, uint32_t NumWG,
131+
uptr &Begin, uptr &End) override final;
132+
119133
virtual size_t GetShadowSize() = 0;
120134

121135
virtual uptr GetStartAddress() { return 0; }
@@ -132,6 +146,8 @@ struct MsanShadowMemoryGPU : public MsanShadowMemory {
132146
ur_mutex VirtualMemMapsMutex;
133147

134148
uptr LocalShadowOffset = 0;
149+
150+
uptr PrivateShadowOffset = 0;
135151
};
136152

137153
// clang-format off

0 commit comments

Comments
 (0)