Skip to content

Commit 1ba8017

Browse files
committed
[Offload] Sanitize "standalone" unreachable instructions
If an unreachable is reached, the execution state is invalid. If the sanitizer is enabled, we stop and report it to the user.
1 parent c130a25 commit 1ba8017

File tree

6 files changed

+103
-1
lines changed

6 files changed

+103
-1
lines changed

llvm/lib/Transforms/Instrumentation/OffloadSanitizer.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ class OffloadSanitizerImpl final {
4242
bool shouldInstrumentFunction(Function &Fn);
4343
bool instrumentFunction(Function &Fn);
4444
bool instrumentTrapInstructions(SmallVectorImpl<IntrinsicInst *> &TrapCalls);
45+
bool instrumentUnreachableInstructions(
46+
SmallVectorImpl<UnreachableInst *> &UnreachableInsts);
4547

4648
FunctionCallee getOrCreateFn(FunctionCallee &FC, StringRef Name, Type *RetTy,
4749
ArrayRef<Type *> ArgTys) {
@@ -59,6 +61,13 @@ class OffloadSanitizerImpl final {
5961
{/*PC*/ Int64Ty});
6062
}
6163

64+
/// void __offload_san_unreachable_info(Int64Ty);
65+
FunctionCallee UnreachableInfoFn;
66+
FunctionCallee getUnreachableInfoFn() {
67+
return getOrCreateFn(UnreachableInfoFn, "__offload_san_unreachable_info",
68+
VoidTy, {/*PC*/ Int64Ty});
69+
}
70+
6271
CallInst *createCall(IRBuilder<> &IRB, FunctionCallee Callee,
6372
ArrayRef<Value *> Args = std::nullopt,
6473
const Twine &Name = "") {
@@ -107,15 +116,34 @@ bool OffloadSanitizerImpl::instrumentTrapInstructions(
107116
return Changed;
108117
}
109118

119+
bool OffloadSanitizerImpl::instrumentUnreachableInstructions(
120+
SmallVectorImpl<UnreachableInst *> &UnreachableInsts) {
121+
bool Changed = false;
122+
for (auto *II : UnreachableInsts) {
123+
// Skip unreachables after traps since we instrument those as well.
124+
if (&II->getParent()->front() != II)
125+
if (auto *CI = dyn_cast<CallInst>(II->getPrevNode()))
126+
if (CI->getIntrinsicID() == Intrinsic::trap)
127+
continue;
128+
IRBuilder<> IRB(II);
129+
createCall(IRB, getUnreachableInfoFn(), {getPC(IRB)});
130+
}
131+
return Changed;
132+
}
133+
110134
bool OffloadSanitizerImpl::instrumentFunction(Function &Fn) {
111135
if (!shouldInstrumentFunction(Fn))
112136
return false;
113137

138+
SmallVector<UnreachableInst *> UnreachableInsts;
114139
SmallVector<IntrinsicInst *> TrapCalls;
115140

116141
bool Changed = false;
117142
for (auto &I : instructions(Fn)) {
118143
switch (I.getOpcode()) {
144+
case Instruction::Unreachable:
145+
UnreachableInsts.push_back(cast<UnreachableInst>(&I));
146+
break;
119147
case Instruction::Call: {
120148
auto &CI = cast<CallInst>(I);
121149
if (auto *II = dyn_cast<IntrinsicInst>(&CI))
@@ -129,6 +157,7 @@ bool OffloadSanitizerImpl::instrumentFunction(Function &Fn) {
129157
}
130158

131159
Changed |= instrumentTrapInstructions(TrapCalls);
160+
Changed |= instrumentUnreachableInstructions(UnreachableInsts);
132161

133162
return Changed;
134163
}

llvm/test/Instrumentation/OffloadSanitizer/basic.ll

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,3 +54,32 @@ t:
5454
f:
5555
ret void
5656
}
57+
58+
define void @test_unreachable1() {
59+
; CHECK-LABEL: define void @test_unreachable1() {
60+
; CHECK-NEXT: [[PC:%.*]] = call i64 @llvm.amdgcn.s.getpc()
61+
; CHECK-NEXT: call void @__offload_san_unreachable_info(i64 [[PC]])
62+
; CHECK-NEXT: unreachable
63+
;
64+
unreachable
65+
}
66+
67+
define void @test_unreachable2(i1 %c) {
68+
; CHECK-LABEL: define void @test_unreachable2(
69+
; CHECK-SAME: i1 [[C:%.*]]) {
70+
; CHECK-NEXT: [[ENTRY:.*:]]
71+
; CHECK-NEXT: br i1 [[C]], label %[[T:.*]], label %[[F:.*]]
72+
; CHECK: [[T]]:
73+
; CHECK-NEXT: [[PC:%.*]] = call i64 @llvm.amdgcn.s.getpc()
74+
; CHECK-NEXT: call void @__offload_san_unreachable_info(i64 [[PC]])
75+
; CHECK-NEXT: unreachable
76+
; CHECK: [[F]]:
77+
; CHECK-NEXT: ret void
78+
;
79+
entry:
80+
br i1 %c, label %t ,label %f
81+
t:
82+
unreachable
83+
f:
84+
ret void
85+
}

offload/DeviceRTL/src/Sanitizer.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,10 @@ extern "C" {
9090
_SAN_ENTRY_ATTRS void __offload_san_trap_info(uint64_t PC) {
9191
raiseExecutionError(SanitizerEnvironmentTy::TRAP, PC);
9292
}
93+
94+
_SAN_ENTRY_ATTRS void __offload_san_unreachable_info(uint64_t PC) {
95+
raiseExecutionError(SanitizerEnvironmentTy::UNREACHABLE, PC);
96+
}
9397
}
9498

9599
#pragma omp end declare target

offload/include/Shared/Environment.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,8 @@ struct SanitizerEnvironmentTy {
111111
enum ErrorCodeTy : uint8_t {
112112
NONE = 0,
113113
TRAP,
114-
LAST = TRAP,
114+
UNREACHABLE,
115+
LAST = UNREACHABLE,
115116
} ErrorCode;
116117

117118
/// Flag to indicate the environment has been initialized fully.

offload/plugins-nextgen/common/include/ErrorReporting.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,10 @@ class ErrorReporter {
281281
case SanitizerEnvironmentTy::TRAP:
282282
reportError("execution interrupted by hardware trap instruction");
283283
break;
284+
case SanitizerEnvironmentTy::UNREACHABLE:
285+
reportError("execution reached an \"unreachable\" state (likely caused "
286+
"by undefined behavior)");
287+
break;
284288
default:
285289
reportError(
286290
"execution stopped, reason is unknown due to invalid error code");
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
2+
// clang-format off
3+
// RUN: %libomptarget-compile-generic -g -mllvm -amdgpu-enable-offload-sanitizer
4+
// RUN: %not --crash env -u LLVM_DISABLE_SYMBOLIZATION OFFLOAD_TRACK_NUM_KERNEL_LAUNCH_TRACES=1 %libomptarget-run-generic 2>&1 | %fcheck-generic --check-prefixes=SANIT
5+
// RUN: %not --crash %libomptarget-run-generic 2>&1 | %fcheck-generic --check-prefixes=SANIT
6+
// RUN: %libomptarget-compileopt-generic -g -mllvm -amdgpu-enable-offload-sanitizer
7+
// RUN: %not --crash env -u LLVM_DISABLE_SYMBOLIZATION OFFLOAD_TRACK_NUM_KERNEL_LAUNCH_TRACES=1 %libomptarget-run-generic 2>&1 | %fcheck-generic --check-prefixes=SANIT
8+
// RUN: %not --crash %libomptarget-run-generic 2>&1 | %fcheck-generic --check-prefixes=SANIT
9+
10+
// UNSUPPORTED: nvptx64-nvidia-cuda
11+
// UNSUPPORTED: nvptx64-nvidia-cuda-LTO
12+
// UNSUPPORTED: aarch64-unknown-linux-gnu
13+
// UNSUPPORTED: aarch64-unknown-linux-gnu-LTO
14+
// UNSUPPORTED: x86_64-pc-linux-gnu
15+
// UNSUPPORTED: x86_64-pc-linux-gnu-LTO
16+
// UNSUPPORTED: s390x-ibm-linux-gnu
17+
// UNSUPPORTED: s390x-ibm-linux-gnu-LTO
18+
19+
#include <omp.h>
20+
21+
__attribute__((noinline)) void unreachable(volatile int *GoodPtr) {
22+
*GoodPtr = 1;
23+
__builtin_unreachable();
24+
}
25+
26+
int main(void) {
27+
#pragma omp target
28+
{
29+
volatile int A = 0;
30+
unreachable(&A);
31+
}
32+
}
33+
// SANIT: OFFLOAD ERROR: Kernel {{.*}} (__omp_offloading_{{.*}}_main_l27)
34+
// SANIT: OFFLOAD ERROR: execution reached an "unreachable" state (likely caused by undefined behavior)
35+
// SANIT: Triggered by thread <{{.*}},0,0> block <{{.*}},0,0> PC 0x{{.*}}

0 commit comments

Comments
 (0)