1//===-- ubsan_offload_hsa_interceptors.cpp ----------------------*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include <dlfcn.h>
10
11#include "interception/interception.h"
12#include "sanitizer_common/sanitizer_atomic.h"
13#include "sanitizer_common/sanitizer_common.h"
14#include "sanitizer_common/sanitizer_libc.h"
15#include "sanitizer_common/sanitizer_mutex.h"
16#include "sanitizer_common/sanitizer_offload.h"
17#include "sanitizer_common/sanitizer_platform.h"
18#include "ubsan_diag.h"
19#include "ubsan_offload.h"
20
21#if !SANITIZER_LINUX
22#error "Offload UBSan reporting is supported on Linux only"
23#endif
24
25using namespace __sanitizer;
26using namespace __ubsan;
27
28namespace __ubsan {
29
30static StaticSpinMutex InitMutex;
31static atomic_uint8_t Initialized;
32
33void Initialize() {
34 if (LIKELY(atomic_load(&Initialized, memory_order_acquire)))
35 return;
36 SpinMutexLock L(&InitMutex);
37 if (atomic_load(a: &Initialized, mo: memory_order_relaxed))
38 return;
39 SanitizerToolName = "UndefinedBehaviorSanitizer";
40 __ubsan_set_offload_symbolize(
41 Fn: [](uptr PC) { return Offload::Get().Symbolize(PC); });
42 Offload::Get().RegisterHandler(Fn: HandleOffloadReport);
43 Atexit(function: [] { Offload::Get().UntrackImages(); });
44 AddDieCallback(callback: [] { Offload::Get().UntrackImages(); });
45 atomic_store(a: &Initialized, v: 1, mo: memory_order_release);
46}
47
48} // namespace __ubsan
49
50// The shared runtime exports these to every program, act as if HSA is absent
51// when it is not loaded.
52#define UBSAN_HSA_ENTER(name) \
53 Initialize(); \
54 if (UNLIKELY(!REAL(name))) { \
55 INTERCEPT_FUNCTION(name); \
56 if (UNLIKELY(!REAL(name))) { \
57 VReport(1, "%s: cannot find %s in this process\n", SanitizerToolName, \
58 #name); \
59 return HSA_STATUS_ERROR; \
60 } \
61 }
62
63#define UBSAN_HSA_FORWARD(name, ...) \
64 UBSAN_HSA_ENTER(name); \
65 if (UNLIKELY(!Offload::Get().Ready())) \
66 return REAL(name)(__VA_ARGS__);
67
68static bool FromHsa(void *P) {
69 Dl_info Info = {};
70 if (!dladdr(address: P, info: &Info) || !Info.dli_fname)
71 return false;
72 return internal_strstr(haystack: Info.dli_fname, SANITIZER_HSA_LIBRARY);
73}
74
75// Callers bind to whichever 'hsa_init' comes first, if HSA was loaded before
76// the runtime the interceptors are bypassed.
77static void CheckInterposed() {
78 void *Sym = dlsym(RTLD_DEFAULT, name: "hsa_init");
79 if (!Sym || !FromHsa(P: Sym))
80 return;
81 Report(format: "WARNING: %s: the runtime is loaded too late to intercept HSA, GPU "
82 "errors will not be reported. Link the runtime first or use "
83 "LD_PRELOAD.\n",
84 SanitizerToolName);
85}
86
87INTERCEPTOR(hsa_status_t, hsa_init, void) {
88 UBSAN_HSA_ENTER(hsa_init);
89
90 hsa_status_t Status = REAL(hsa_init)();
91 if (Status != HSA_STATUS_SUCCESS)
92 return Status;
93
94 Offload::Get().Init();
95 return Status;
96}
97
98INTERCEPTOR(hsa_status_t, hsa_shut_down, void) {
99 UBSAN_HSA_ENTER(hsa_shut_down);
100
101 Offload::Get().Shutdown();
102 return REAL(hsa_shut_down)();
103}
104
105INTERCEPTOR(hsa_status_t, hsa_executable_freeze, hsa_executable_t Executable,
106 const char *Options) {
107 UBSAN_HSA_FORWARD(hsa_executable_freeze, Executable, Options);
108
109 hsa_status_t Status = REAL(hsa_executable_freeze)(Executable, Options);
110 if (Status == HSA_STATUS_SUCCESS)
111 Offload::Get().TrackExecutable(Exec: Executable);
112 return Status;
113}
114
115INTERCEPTOR(hsa_status_t, hsa_executable_destroy, hsa_executable_t Executable) {
116 UBSAN_HSA_FORWARD(hsa_executable_destroy, Executable);
117
118 Offload::Get().UntrackExecutable(Exec: Executable);
119 return REAL(hsa_executable_destroy)(Executable);
120}
121
122extern "C" void __ubsan_offload_init() { __ubsan::Initialize(); }
123
124__attribute__((constructor(0))) static void UbsanOffloadDynInit() {
125 __ubsan_offload_init();
126 CheckInterposed();
127}
128