Skip to content

Commit 247e5e0

Browse files
AllanZynezhaomaosukbenzie
authored
[SYCL][DeviceSanitizer] Checking out-of-bounds error on sycl::local_accessor (#13503)
UR: oneapi-src/unified-runtime#1532 To check sycl::local_accessor(aka, dynamic local memory), we need to extend a new argument in spir kernel, this is because: - ASan needs to know some size information of local buffer, like its size and size with redzone, so that it can poison its shadow memory - By using this new argument, we can also pass some per-launch information (that is, it is different in each launch of kernel). One obvious example is SanitizerReport, which saves the error message, so that we can store and print multiple error reports for one kernel with different arguments. Another example is the shadow memory of local memory, this should be different per-launch as well, since one kernel can be launched multiple times and executed in parallel. I named this argument as "__asan_launch", which is a pointer pointed to "LaunchInfo" structure and allocated it in shared USM. To make this pointer can be used in spir_func w/o extending their argument, I created a global external local memory (external, so that it can be shared with other translation units, and its instance is defined in libdevice), and save the "__asan_launch" into this local memory immediately at the entry of kernel. UR can't check the name of kernel arguments, so it can't know if the kernel has "__asan_launch". So I assume the "__asan_launch" is always there, and added a check to prevent DAE pass from removing it. --------- Co-authored-by: Maosu Zhao <[email protected]> Co-authored-by: Kenneth Benzie (Benie) <[email protected]>
1 parent c541c22 commit 247e5e0

File tree

19 files changed

+638
-249
lines changed

19 files changed

+638
-249
lines changed

libdevice/cmake/modules/SYCLLibdevice.cmake

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ set(imf_obj_deps device_imf.hpp imf_half.hpp imf_bf16.hpp imf_rounding_op.hpp im
159159
set(itt_obj_deps device_itt.h spirv_vars.h device.h sycl-compiler)
160160
set(bfloat16_obj_deps sycl-headers sycl-compiler)
161161
if (NOT MSVC)
162-
set(sanitizer_obj_deps device.h atomic.hpp spirv_vars.h include/sanitizer_device_utils.hpp include/spir_global_var.hpp sycl-compiler)
162+
set(sanitizer_obj_deps device.h atomic.hpp spirv_vars.h include/sanitizer_utils.hpp include/spir_global_var.hpp sycl-compiler)
163163
endif()
164164

165165
add_devicelib(libsycl-itt-stubs SRC itt_stubs.cpp DEP ${itt_obj_deps})

libdevice/include/asan_libdevice.hpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,6 @@ struct DeviceSanitizerReport {
5959
};
6060

6161
struct LocalArgsInfo {
62-
uint32_t ArgIndex = 0;
6362
uint64_t Size = 0;
6463
uint64_t SizeWithRedZone = 0;
6564
};

libdevice/include/sanitizer_device_utils.hpp renamed to libdevice/include/sanitizer_utils.hpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,10 @@
1010
#include "spir_global_var.hpp"
1111
#include <cstdint>
1212

13+
// Treat this header as system one to workaround frontend's restriction
14+
#pragma clang system_header
15+
1316
enum DeviceType : uint64_t { UNKNOWN, CPU, GPU_PVC, GPU_DG2 };
17+
18+
extern SPIR_GLOBAL_VAR __SYCL_GLOBAL__ uint64_t *__SYCL_LOCAL__
19+
__AsanLaunchInfo;

libdevice/include/spir_global_var.hpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,3 +42,11 @@ class
4242
#define __SYCL_LOCAL__ __attribute__((opencl_local))
4343
#define __SYCL_PRIVATE__ __attribute__((opencl_private))
4444
#define __SYCL_CONSTANT__ __attribute__((opencl_constant))
45+
46+
#ifndef SPIR_GLOBAL_VAR
47+
#ifdef __SYCL_DEVICE_ONLY__
48+
#define SPIR_GLOBAL_VAR __attribute__((sycl_global_var))
49+
#else
50+
#define SPIR_GLOBAL_VAR
51+
#endif
52+
#endif

libdevice/sanitizer_utils.cpp

Lines changed: 120 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,7 @@
1111
#include "spirv_vars.h"
1212

1313
#include "include/asan_libdevice.hpp"
14-
#include "include/sanitizer_device_utils.hpp"
15-
#include <cstddef>
16-
#include <cstdint>
14+
#include "include/sanitizer_utils.hpp"
1715

1816
using uptr = uintptr_t;
1917
using s8 = char;
@@ -23,11 +21,10 @@ using u16 = unsigned short;
2321

2422
DeviceGlobal<uptr> __AsanShadowMemoryGlobalStart;
2523
DeviceGlobal<uptr> __AsanShadowMemoryGlobalEnd;
26-
DeviceGlobal<uptr> __AsanShadowMemoryLocalStart;
27-
DeviceGlobal<uptr> __AsanShadowMemoryLocalEnd;
2824
DeviceGlobal<DeviceType> __DeviceType;
2925
DeviceGlobal<uint64_t> __AsanDebug;
30-
DeviceGlobal<DeviceSanitizerReport> __DeviceSanitizerReportMem;
26+
// Save the pointer to LaunchInfo
27+
__SYCL_GLOBAL__ uptr *__SYCL_LOCAL__ __AsanLaunchInfo;
3128

3229
#if defined(__SPIR__) || defined(__SPIRV__)
3330

@@ -134,6 +131,16 @@ inline uptr MemToShadow_DG2(uptr addr, uint32_t as) {
134131
return shadow_ptr;
135132
}
136133

134+
static __SYCL_CONSTANT__ const char __mem_launch_info[] =
135+
"[kernel] launch_info: %p (local_shadow=%p~%p, numLocalArgs=%d, "
136+
"localArgs=%p)\n";
137+
138+
static __SYCL_CONSTANT__ const char __generic_to[] =
139+
"[kernel] %p(4) - %p(%d)\n";
140+
141+
static __SYCL_CONSTANT__ const char __generic_to_fail[] =
142+
"[kernel] %p(4) - unknown address space\n";
143+
137144
inline uptr MemToShadow_PVC(uptr addr, uint32_t as) {
138145

139146
if (as == ADDRESS_SPACE_GENERIC) {
@@ -172,9 +179,6 @@ inline uptr MemToShadow_PVC(uptr addr, uint32_t as) {
172179
}
173180
return shadow_ptr;
174181
} else if (as == ADDRESS_SPACE_LOCAL) { // local
175-
if (__AsanShadowMemoryLocalStart == 0) {
176-
return 0;
177-
}
178182
// The size of SLM is 128KB on PVC
179183
constexpr unsigned SLM_SIZE = 128 * 1024;
180184
// work-group linear id
@@ -184,14 +188,28 @@ inline uptr MemToShadow_PVC(uptr addr, uint32_t as) {
184188
__spirv_BuiltInWorkgroupId.y * __spirv_BuiltInNumWorkgroups.z +
185189
__spirv_BuiltInWorkgroupId.z;
186190

187-
uptr shadow_ptr = __AsanShadowMemoryLocalStart +
191+
auto launch_info = (__SYCL_GLOBAL__ const LaunchInfo *)__AsanLaunchInfo;
192+
const auto shadow_offset = launch_info->LocalShadowOffset;
193+
const auto shadow_offset_end = launch_info->LocalShadowOffsetEnd;
194+
195+
if (shadow_offset == 0) {
196+
return 0;
197+
}
198+
199+
if (__AsanDebug)
200+
__spirv_ocl_printf(__mem_launch_info, launch_info,
201+
launch_info->LocalShadowOffset,
202+
launch_info->LocalShadowOffsetEnd,
203+
launch_info->NumLocalArgs, launch_info->LocalArgs);
204+
205+
uptr shadow_ptr = shadow_offset +
188206
((wg_lid * SLM_SIZE) >> ASAN_SHADOW_SCALE) +
189207
((addr & (SLM_SIZE - 1)) >> 3);
190208

191-
if (shadow_ptr > __AsanShadowMemoryLocalEnd) {
209+
if (shadow_ptr > shadow_offset_end) {
192210
if (__asan_report_out_of_shadow_bounds() && __AsanDebug) {
193211
__spirv_ocl_printf(__local_shadow_out_of_bound, addr, shadow_ptr,
194-
wg_lid, (uptr)__AsanShadowMemoryLocalStart);
212+
wg_lid, (uptr)shadow_offset);
195213
}
196214
return 0;
197215
}
@@ -268,22 +286,18 @@ bool MemIsZero(__SYCL_GLOBAL__ const char *beg, uptr size) {
268286
bool __asan_internal_report_save(DeviceSanitizerErrorType error_type) {
269287
const int Expected = ASAN_REPORT_NONE;
270288
int Desired = ASAN_REPORT_START;
271-
if (atomicCompareAndSet(&__DeviceSanitizerReportMem.get().Flag, Desired,
272-
Expected) == Expected) {
273-
__DeviceSanitizerReportMem.get().ErrorType = error_type;
289+
auto &SanitizerReport =
290+
((__SYCL_GLOBAL__ LaunchInfo *)__AsanLaunchInfo)->SanitizerReport;
291+
if (atomicCompareAndSet(&SanitizerReport.Flag, Desired, Expected) ==
292+
Expected) {
293+
SanitizerReport.ErrorType = error_type;
274294
// Show we've done copying
275-
atomicStore(&__DeviceSanitizerReportMem.get().Flag, ASAN_REPORT_FINISH);
295+
atomicStore(&SanitizerReport.Flag, ASAN_REPORT_FINISH);
276296
return true;
277297
}
278298
return false;
279299
}
280300

281-
#ifdef __SYCL_DEVICE_ONLY__
282-
#define __DEVICE_SANITIZER_REPORT_ACCESSOR __DeviceSanitizerReportMem.get()
283-
#else // __SYCL_DEVICE_ONLY__
284-
#define __DEVICE_SANITIZER_REPORT_ACCESSOR
285-
#endif // __SYCL_DEVICE_ONLY__
286-
287301
bool __asan_internal_report_save(
288302
uptr ptr, uint32_t as, const char __SYCL_CONSTANT__ *file, uint32_t line,
289303
const char __SYCL_CONSTANT__ *func, bool is_write, uint32_t access_size,
@@ -292,8 +306,20 @@ bool __asan_internal_report_save(
292306

293307
const int Expected = ASAN_REPORT_NONE;
294308
int Desired = ASAN_REPORT_START;
295-
if (atomicCompareAndSet(&__DEVICE_SANITIZER_REPORT_ACCESSOR.Flag, Desired,
296-
Expected) == Expected) {
309+
310+
if (__AsanDebug) {
311+
auto *launch_info = (__SYCL_GLOBAL__ LaunchInfo *)__AsanLaunchInfo;
312+
__spirv_ocl_printf(__mem_launch_info, launch_info,
313+
launch_info->LocalShadowOffset,
314+
launch_info->LocalShadowOffsetEnd,
315+
launch_info->NumLocalArgs, launch_info->LocalArgs);
316+
}
317+
318+
auto &SanitizerReport =
319+
((__SYCL_GLOBAL__ LaunchInfo *)__AsanLaunchInfo)->SanitizerReport;
320+
321+
if (atomicCompareAndSet(&SanitizerReport.Flag, Desired, Expected) ==
322+
Expected) {
297323

298324
int FileLength = 0;
299325
int FuncLength = 0;
@@ -305,39 +331,40 @@ bool __asan_internal_report_save(
305331
for (auto *C = func; *C != '\0'; ++C, ++FuncLength)
306332
;
307333

308-
int MaxFileIdx = sizeof(__DEVICE_SANITIZER_REPORT_ACCESSOR.File) - 1;
309-
int MaxFuncIdx = sizeof(__DEVICE_SANITIZER_REPORT_ACCESSOR.Func) - 1;
334+
int MaxFileIdx = sizeof(SanitizerReport.File) - 1;
335+
int MaxFuncIdx = sizeof(SanitizerReport.Func) - 1;
310336

311337
if (FileLength < MaxFileIdx)
312338
MaxFileIdx = FileLength;
313339
if (FuncLength < MaxFuncIdx)
314340
MaxFuncIdx = FuncLength;
315341

316342
for (int Idx = 0; Idx < MaxFileIdx; ++Idx)
317-
__DEVICE_SANITIZER_REPORT_ACCESSOR.File[Idx] = file[Idx];
318-
__DEVICE_SANITIZER_REPORT_ACCESSOR.File[MaxFileIdx] = '\0';
343+
SanitizerReport.File[Idx] = file[Idx];
344+
SanitizerReport.File[MaxFileIdx] = '\0';
319345

320346
for (int Idx = 0; Idx < MaxFuncIdx; ++Idx)
321-
__DEVICE_SANITIZER_REPORT_ACCESSOR.Func[Idx] = func[Idx];
322-
__DEVICE_SANITIZER_REPORT_ACCESSOR.Func[MaxFuncIdx] = '\0';
323-
324-
__DEVICE_SANITIZER_REPORT_ACCESSOR.Line = line;
325-
__DEVICE_SANITIZER_REPORT_ACCESSOR.GID0 = __spirv_GlobalInvocationId_x();
326-
__DEVICE_SANITIZER_REPORT_ACCESSOR.GID1 = __spirv_GlobalInvocationId_y();
327-
__DEVICE_SANITIZER_REPORT_ACCESSOR.GID2 = __spirv_GlobalInvocationId_z();
328-
__DEVICE_SANITIZER_REPORT_ACCESSOR.LID0 = __spirv_LocalInvocationId_x();
329-
__DEVICE_SANITIZER_REPORT_ACCESSOR.LID1 = __spirv_LocalInvocationId_y();
330-
__DEVICE_SANITIZER_REPORT_ACCESSOR.LID2 = __spirv_LocalInvocationId_z();
331-
332-
__DEVICE_SANITIZER_REPORT_ACCESSOR.Address = ptr;
333-
__DEVICE_SANITIZER_REPORT_ACCESSOR.IsWrite = is_write;
334-
__DEVICE_SANITIZER_REPORT_ACCESSOR.AccessSize = access_size;
335-
__DEVICE_SANITIZER_REPORT_ACCESSOR.ErrorType = error_type;
336-
__DEVICE_SANITIZER_REPORT_ACCESSOR.MemoryType = memory_type;
337-
__DEVICE_SANITIZER_REPORT_ACCESSOR.IsRecover = is_recover;
347+
SanitizerReport.Func[Idx] = func[Idx];
348+
SanitizerReport.Func[MaxFuncIdx] = '\0';
349+
350+
SanitizerReport.Line = line;
351+
SanitizerReport.GID0 = __spirv_GlobalInvocationId_x();
352+
SanitizerReport.GID1 = __spirv_GlobalInvocationId_y();
353+
SanitizerReport.GID2 = __spirv_GlobalInvocationId_z();
354+
SanitizerReport.LID0 = __spirv_LocalInvocationId_x();
355+
SanitizerReport.LID1 = __spirv_LocalInvocationId_y();
356+
SanitizerReport.LID2 = __spirv_LocalInvocationId_z();
357+
358+
SanitizerReport.Address = ptr;
359+
SanitizerReport.IsWrite = is_write;
360+
SanitizerReport.AccessSize = access_size;
361+
SanitizerReport.ErrorType = error_type;
362+
SanitizerReport.MemoryType = memory_type;
363+
SanitizerReport.IsRecover = is_recover;
338364

339365
// Show we've done copying
340-
atomicStore(&__DEVICE_SANITIZER_REPORT_ACCESSOR.Flag, ASAN_REPORT_FINISH);
366+
atomicStore(&SanitizerReport.Flag, ASAN_REPORT_FINISH);
367+
return true;
341368
}
342369
return false;
343370
}
@@ -545,7 +572,7 @@ ASAN_REPORT_ERROR(store, true, 4)
545572
DEVICE_EXTERN_C_NOINLINE void __asan_##type##size( \
546573
uptr addr, uint32_t as, const char __SYCL_CONSTANT__ *file, \
547574
uint32_t line, const char __SYCL_CONSTANT__ *func) { \
548-
u##size *shadow_address = (u##size *)MemToShadow(addr, as); \
575+
auto *shadow_address = (__SYCL_GLOBAL__ u##size *)MemToShadow(addr, as); \
549576
if (shadow_address && *shadow_address) { \
550577
__asan_report_access_error(addr, as, size, is_write, addr, file, line, \
551578
func); \
@@ -554,7 +581,7 @@ ASAN_REPORT_ERROR(store, true, 4)
554581
DEVICE_EXTERN_C_NOINLINE void __asan_##type##size##_noabort( \
555582
uptr addr, uint32_t as, const char __SYCL_CONSTANT__ *file, \
556583
uint32_t line, const char __SYCL_CONSTANT__ *func) { \
557-
u##size *shadow_address = (u##size *)MemToShadow(addr, as); \
584+
auto *shadow_address = (__SYCL_GLOBAL__ u##size *)MemToShadow(addr, as); \
558585
if (shadow_address && *shadow_address) { \
559586
__asan_report_access_error(addr, as, size, is_write, addr, file, line, \
560587
func, true); \
@@ -595,7 +622,7 @@ static __SYCL_CONSTANT__ const char __mem_set_shadow_local[] =
595622
"[kernel] set_shadow_local(beg=%p, end=%p, val:%02X)\n";
596623

597624
DEVICE_EXTERN_C_NOINLINE void
598-
__asan_set_shadow_local_memory(uptr ptr, size_t size,
625+
__asan_set_shadow_static_local(uptr ptr, size_t size,
599626
size_t size_with_redzone) {
600627
// Since ptr is aligned to ASAN_SHADOW_GRANULARITY,
601628
// if size != aligned_size, then the buffer tail of ptr is not aligned
@@ -638,4 +665,48 @@ __asan_set_shadow_local_memory(uptr ptr, size_t size,
638665
}
639666
}
640667

668+
static __SYCL_CONSTANT__ const char __mem_local_arg[] =
669+
"[kernel] local_arg(index=%d, size=%d, size_rz=%d)\n";
670+
671+
static __SYCL_CONSTANT__ const char __mem_set_shadow_dynamic_local_begin[] =
672+
"[kernel] BEGIN __asan_set_shadow_dynamic_local\n";
673+
static __SYCL_CONSTANT__ const char __mem_set_shadow_dynamic_local_end[] =
674+
"[kernel] END __asan_set_shadow_dynamic_local\n";
675+
static __SYCL_CONSTANT__ const char __mem_report_arg_count_incorrect[] =
676+
"[kernel] ERROR: The number of local args is incorrect, expect %d, actual "
677+
"%d\n";
678+
679+
DEVICE_EXTERN_C_NOINLINE void
680+
__asan_set_shadow_dynamic_local(uptr ptr, uint32_t num_args) {
681+
if (__AsanDebug)
682+
__spirv_ocl_printf(__mem_set_shadow_dynamic_local_begin);
683+
684+
auto *launch_info = (__SYCL_GLOBAL__ const LaunchInfo *)__AsanLaunchInfo;
685+
if (num_args != launch_info->NumLocalArgs) {
686+
__spirv_ocl_printf(__mem_report_arg_count_incorrect, num_args,
687+
launch_info->NumLocalArgs);
688+
return;
689+
}
690+
691+
uptr *args = (uptr *)ptr;
692+
if (__AsanDebug)
693+
__spirv_ocl_printf(__mem_launch_info, launch_info,
694+
launch_info->LocalShadowOffset,
695+
launch_info->LocalShadowOffsetEnd,
696+
launch_info->NumLocalArgs, launch_info->LocalArgs);
697+
698+
for (uint32_t i = 0; i < num_args; ++i) {
699+
auto *local_arg = &launch_info->LocalArgs[i];
700+
if (__AsanDebug)
701+
__spirv_ocl_printf(__mem_local_arg, i, local_arg->Size,
702+
local_arg->SizeWithRedZone);
703+
704+
__asan_set_shadow_static_local(args[i], local_arg->Size,
705+
local_arg->SizeWithRedZone);
706+
}
707+
708+
if (__AsanDebug)
709+
__spirv_ocl_printf(__mem_set_shadow_dynamic_local_end);
710+
}
711+
641712
#endif // __SPIR__ || __SPIRV__

llvm/lib/Transforms/IPO/DeadArgumentElimination.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -540,6 +540,14 @@ void DeadArgumentEliminationPass::surveyFunction(const Function &F) {
540540
return;
541541
}
542542

543+
// Don't touch sanitized functions. The "__asan_launch" argument needs to be
544+
// present at all times, even if it's not used.
545+
if (F.getCallingConv() == CallingConv::SPIR_KERNEL &&
546+
F.hasFnAttribute(Attribute::SanitizeAddress)) {
547+
markLive(F);
548+
return;
549+
}
550+
543551
unsigned RetCount = numRetVals(&F);
544552

545553
// Assume all return values are dead

0 commit comments

Comments
 (0)