Skip to content

Commit 62ced81

Browse files
authored
[Sanitizer] Make sanitizer passes idempotent (#99439)
This PR changes the sanitizer passes to be idempotent. When any sanitizer pass is run after it has already been run before, double instrumentation is seen in the resulting IR. This happens because there is no check in the pass, to verify if IR has been instrumented before. This PR checks if "nosanitize_*" module flag is already present and if true, return early without running the pass again.
1 parent 435654b commit 62ced81

File tree

14 files changed

+244
-2
lines changed

14 files changed

+244
-2
lines changed

clang/test/CodeGenObjC/no-sanitize.m

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
@interface I0 @end
44
@implementation I0
5-
// CHECK-NOT: sanitize_address
5+
// CHECK-NOT: Function Attrs: sanitize_address
66
- (void) im0: (int) a0 __attribute__((no_sanitize("address"))) {
77
int (^blockName)(void) = ^int(void) { return 0; };
88
}

llvm/include/llvm/Transforms/Instrumentation.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ class Triple;
3030
class OptimizationRemarkEmitter;
3131
class Comdat;
3232
class CallBase;
33+
class Module;
34+
35+
/// Check if module has flag attached, if not add the flag.
36+
bool checkIfAlreadyInstrumented(Module &M, StringRef Flag);
3337

3438
/// Instrumentation passes often insert conditional checks into entry blocks.
3539
/// Call this function before splitting the entry block to move instructions

llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1251,6 +1251,11 @@ AddressSanitizerPass::AddressSanitizerPass(
12511251

12521252
PreservedAnalyses AddressSanitizerPass::run(Module &M,
12531253
ModuleAnalysisManager &MAM) {
1254+
// Return early if nosanitize_address module flag is present for the module.
1255+
// This implies that asan pass has already run before.
1256+
if (checkIfAlreadyInstrumented(M, "nosanitize_address"))
1257+
return PreservedAnalyses::all();
1258+
12541259
ModuleAddressSanitizer ModuleSanitizer(
12551260
M, Options.InsertVersionCheck, Options.CompileKernel, Options.Recover,
12561261
UseGlobalGC, UseOdrIndicator, DestructorKind, ConstructorKind);

llvm/lib/Transforms/Instrumentation/DataFlowSanitizer.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3473,6 +3473,9 @@ void DFSanVisitor::visitPHINode(PHINode &PN) {
34733473

34743474
PreservedAnalyses DataFlowSanitizerPass::run(Module &M,
34753475
ModuleAnalysisManager &AM) {
3476+
// Return early if nosanitize_dataflow module flag is present for the module.
3477+
if (checkIfAlreadyInstrumented(M, "nosanitize_dataflow"))
3478+
return PreservedAnalyses::all();
34763479
auto GetTLI = [&](Function &F) -> TargetLibraryInfo & {
34773480
auto &FAM =
34783481
AM.getResult<FunctionAnalysisManagerModuleProxy>(M).getManager();

llvm/lib/Transforms/Instrumentation/HWAddressSanitizer.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
#include "llvm/Support/RandomNumberGenerator.h"
5858
#include "llvm/Support/raw_ostream.h"
5959
#include "llvm/TargetParser/Triple.h"
60+
#include "llvm/Transforms/Instrumentation.h"
6061
#include "llvm/Transforms/Instrumentation/AddressSanitizerCommon.h"
6162
#include "llvm/Transforms/Utils/BasicBlockUtils.h"
6263
#include "llvm/Transforms/Utils/Local.h"
@@ -455,6 +456,9 @@ class HWAddressSanitizer {
455456

456457
PreservedAnalyses HWAddressSanitizerPass::run(Module &M,
457458
ModuleAnalysisManager &MAM) {
459+
// Return early if nosanitize_hwaddress module flag is present for the module.
460+
if (checkIfAlreadyInstrumented(M, "nosanitize_hwaddress"))
461+
return PreservedAnalyses::all();
458462
const StackSafetyGlobalInfo *SSI = nullptr;
459463
auto TargetTriple = llvm::Triple(M.getTargetTriple());
460464
if (shouldUseStackSafetyAnalysis(TargetTriple, Options.DisableOptimization))

llvm/lib/Transforms/Instrumentation/Instrumentation.cpp

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,47 @@
1212
//===----------------------------------------------------------------------===//
1313

1414
#include "llvm/Transforms/Instrumentation.h"
15+
#include "llvm/IR/DiagnosticInfo.h"
16+
#include "llvm/IR/DiagnosticPrinter.h"
1517
#include "llvm/IR/IntrinsicInst.h"
1618
#include "llvm/IR/Module.h"
1719
#include "llvm/TargetParser/Triple.h"
1820

1921
using namespace llvm;
2022

23+
static cl::opt<bool> ClIgnoreRedundantInstrumentation(
24+
"ignore-redundant-instrumentation",
25+
cl::desc("Ignore redundant instrumentation"), cl::Hidden, cl::init(false));
26+
27+
namespace {
28+
/// Diagnostic information for IR instrumentation reporting.
29+
class DiagnosticInfoInstrumentation : public DiagnosticInfo {
30+
const Twine &Msg;
31+
32+
public:
33+
DiagnosticInfoInstrumentation(const Twine &DiagMsg,
34+
DiagnosticSeverity Severity = DS_Warning)
35+
: DiagnosticInfo(DK_Linker, Severity), Msg(DiagMsg) {}
36+
void print(DiagnosticPrinter &DP) const override { DP << Msg; }
37+
};
38+
} // namespace
39+
40+
/// Check if module has flag attached, if not add the flag.
41+
bool llvm::checkIfAlreadyInstrumented(Module &M, StringRef Flag) {
42+
if (!M.getModuleFlag(Flag)) {
43+
M.addModuleFlag(Module::ModFlagBehavior::Override, Flag, 1);
44+
return false;
45+
}
46+
if (ClIgnoreRedundantInstrumentation)
47+
return true;
48+
std::string diagInfo =
49+
"Redundant instrumentation detected, with module flag: " +
50+
std::string(Flag);
51+
M.getContext().diagnose(
52+
DiagnosticInfoInstrumentation(diagInfo, DiagnosticSeverity::DS_Warning));
53+
return true;
54+
}
55+
2156
/// Moves I before IP. Returns new insert point.
2257
static BasicBlock::iterator moveBeforeInsertPoint(BasicBlock::iterator I, BasicBlock::iterator IP) {
2358
// If I is IP, move the insert point down.

llvm/lib/Transforms/Instrumentation/MemorySanitizer.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,7 @@
198198
#include "llvm/Support/MathExtras.h"
199199
#include "llvm/Support/raw_ostream.h"
200200
#include "llvm/TargetParser/Triple.h"
201+
#include "llvm/Transforms/Instrumentation.h"
201202
#include "llvm/Transforms/Utils/BasicBlockUtils.h"
202203
#include "llvm/Transforms/Utils/Local.h"
203204
#include "llvm/Transforms/Utils/ModuleUtils.h"
@@ -706,6 +707,9 @@ MemorySanitizerOptions::MemorySanitizerOptions(int TO, bool R, bool K,
706707

707708
PreservedAnalyses MemorySanitizerPass::run(Module &M,
708709
ModuleAnalysisManager &AM) {
710+
// Return early if nosanitize_memory module flag is present for the module.
711+
if (checkIfAlreadyInstrumented(M, "nosanitize_memory"))
712+
return PreservedAnalyses::all();
709713
bool Modified = false;
710714
if (!Options.Kernel) {
711715
insertModuleCtor(M);

llvm/lib/Transforms/Instrumentation/ThreadSanitizer.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,9 @@ PreservedAnalyses ThreadSanitizerPass::run(Function &F,
191191

192192
PreservedAnalyses ModuleThreadSanitizerPass::run(Module &M,
193193
ModuleAnalysisManager &MAM) {
194+
// Return early if nosanitize_thread module flag is present for the module.
195+
if (checkIfAlreadyInstrumented(M, "nosanitize_thread"))
196+
return PreservedAnalyses::all();
194197
insertModuleCtor(M);
195198
return PreservedAnalyses::none();
196199
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-attributes --check-globals all --version 5
2+
; This test checks in the second run, function is not instrumented again.
3+
; RUN: opt < %s -passes=asan,asan -S | FileCheck %s
4+
5+
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
6+
target triple = "x86_64-unknown-linux-gnu"
7+
8+
; Function with sanitize_address is instrumented.
9+
; Function Attrs: nounwind uwtable
10+
;.
11+
; CHECK: @___asan_globals_registered = common hidden global i64 0
12+
; CHECK: @__start_asan_globals = extern_weak hidden global i64
13+
; CHECK: @__stop_asan_globals = extern_weak hidden global i64
14+
;.
15+
define void @instr_sa(ptr %a) sanitize_address {
16+
; CHECK: Function Attrs: sanitize_address
17+
; CHECK-LABEL: define void @instr_sa(
18+
; CHECK-SAME: ptr [[A:%.*]]) #[[ATTR0:[0-9]+]] {
19+
; CHECK-NEXT: [[ENTRY:.*:]]
20+
; CHECK-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[A]] to i64
21+
; CHECK-NEXT: [[TMP1:%.*]] = lshr i64 [[TMP0]], 3
22+
; CHECK-NEXT: [[TMP2:%.*]] = add i64 [[TMP1]], 2147450880
23+
; CHECK-NEXT: [[TMP3:%.*]] = inttoptr i64 [[TMP2]] to ptr
24+
; CHECK-NEXT: [[TMP4:%.*]] = load i8, ptr [[TMP3]], align 1
25+
; CHECK-NEXT: [[TMP5:%.*]] = icmp ne i8 [[TMP4]], 0
26+
; CHECK-NEXT: br i1 [[TMP5]], label %[[BB6:.*]], label %[[BB12:.*]], !prof [[PROF1:![0-9]+]]
27+
; CHECK: [[BB6]]:
28+
; CHECK-NEXT: [[TMP7:%.*]] = and i64 [[TMP0]], 7
29+
; CHECK-NEXT: [[TMP8:%.*]] = add i64 [[TMP7]], 3
30+
; CHECK-NEXT: [[TMP9:%.*]] = trunc i64 [[TMP8]] to i8
31+
; CHECK-NEXT: [[TMP10:%.*]] = icmp sge i8 [[TMP9]], [[TMP4]]
32+
; CHECK-NEXT: br i1 [[TMP10]], label %[[BB11:.*]], label %[[BB12]]
33+
; CHECK: [[BB11]]:
34+
; CHECK-NEXT: call void @__asan_report_load4(i64 [[TMP0]]) #[[ATTR2:[0-9]+]]
35+
; CHECK-NEXT: unreachable
36+
; CHECK: [[BB12]]:
37+
; CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[A]], align 4
38+
; CHECK-NEXT: [[TMP2:%.*]] = add i32 [[TMP1]], 1
39+
; CHECK-NEXT: store i32 [[TMP2]], ptr [[A]], align 4
40+
; CHECK-NEXT: ret void
41+
;
42+
entry:
43+
%tmp1 = load i32, ptr %a, align 4
44+
%tmp2 = add i32 %tmp1, 1
45+
store i32 %tmp2, ptr %a, align 4
46+
ret void
47+
}
48+
;.
49+
; CHECK: attributes #[[ATTR0]] = { sanitize_address }
50+
; CHECK: attributes #[[ATTR1:[0-9]+]] = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
51+
; CHECK: attributes #[[ATTR2]] = { nomerge }
52+
;.
53+
; CHECK: [[META0:![0-9]+]] = !{i32 4, !"nosanitize_address", i32 1}
54+
; CHECK: [[PROF1]] = !{!"branch_weights", i32 1, i32 1048575}
55+
;.

llvm/test/Instrumentation/AddressSanitizer/missing_dbg.ll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,4 @@ entry:
3434
!4 = !DISubroutineType(types: !5)
3535
!5 = !{}
3636

37-
; CHECK: [[DBG]] = !DILocation(line: 0, scope: !3)
37+
; CHECK: [[DBG]] = !DILocation(line: 0, scope: !4)
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
; RUN: opt < %s -passes=dfsan,dfsan -S | FileCheck %s
2+
target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64-S128"
3+
target triple = "x86_64-unknown-linux-gnu"
4+
5+
define i8 @add(i8 %a, i8 %b) {
6+
; CHECK: @add.dfsan
7+
; CHECK-DAG: %[[#ALABEL:]] = load i8, ptr @__dfsan_arg_tls, align [[ALIGN:2]]
8+
; CHECK-DAG: %[[#BLABEL:]] = load i8, ptr inttoptr (i64 add (i64 ptrtoint (ptr @__dfsan_arg_tls to i64), i64 2) to ptr), align [[ALIGN]]
9+
; CHECK: %[[#UNION:]] = or i8 %[[#ALABEL]], %[[#BLABEL]]
10+
; CHECK: %c = add i8 %a, %b
11+
; CHECK: store i8 %[[#UNION]], ptr @__dfsan_retval_tls, align [[ALIGN]]
12+
; CHECK: ret i8 %c
13+
%c = add i8 %a, %b
14+
ret i8 %c
15+
}
16+
17+
; CHECK: [[META0:![0-9]+]] = !{i32 4, !"nosanitize_dataflow", i32 1}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-attributes --check-globals --version 2
2+
; Test basic address sanitizer instrumentation.
3+
;
4+
; RUN: opt < %s -passes=hwasan,hwasan -S | FileCheck %s
5+
6+
target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64-S128"
7+
target triple = "x86_64-unknown-linux-gnu"
8+
9+
;.
10+
; CHECK: @llvm.used = appending global [1 x ptr] [ptr @hwasan.module_ctor], section "llvm.metadata"
11+
; CHECK: @llvm.global_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 0, ptr @hwasan.module_ctor, ptr @hwasan.module_ctor }]
12+
; CHECK: @__start_hwasan_globals = external hidden constant [0 x i8]
13+
; CHECK: @__stop_hwasan_globals = external hidden constant [0 x i8]
14+
; CHECK: @hwasan.note = private constant { i32, i32, i32, [8 x i8], i32, i32 } { i32 8, i32 8, i32 3, [8 x i8] c"LLVM\00\00\00\00", i32 trunc (i64 sub (i64 ptrtoint (ptr @__start_hwasan_globals to i64), i64 ptrtoint (ptr @hwasan.note to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (ptr @__stop_hwasan_globals to i64), i64 ptrtoint (ptr @hwasan.note to i64)) to i32) }, section ".note.hwasan.globals", comdat($hwasan.module_ctor), align 4
15+
; CHECK: @hwasan.dummy.global = private constant [0 x i8] zeroinitializer, section "hwasan_globals", comdat($hwasan.module_ctor), !associated [[META0:![0-9]+]]
16+
; CHECK: @__hwasan_tls = external thread_local(initialexec) global i64
17+
; CHECK: @llvm.compiler.used = appending global [3 x ptr] [ptr @hwasan.note, ptr @hwasan.dummy.global, ptr @__hwasan_tls], section "llvm.metadata"
18+
; CHECK: @__hwasan_shadow = external global [0 x i8]
19+
;.
20+
define i8 @test_load8(ptr %a) sanitize_hwaddress {
21+
; CHECK: Function Attrs: sanitize_hwaddress
22+
; CHECK-LABEL: define i8 @test_load8
23+
; CHECK-SAME: (ptr [[A:%.*]]) #[[ATTR0:[0-9]+]] {
24+
; CHECK-NEXT: entry:
25+
; CHECK-NEXT: [[DOTHWASAN_SHADOW:%.*]] = call ptr asm "", "=r,0"(ptr null)
26+
; CHECK-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[A]] to i64
27+
; CHECK-NEXT: call void @__hwasan_load1(i64 [[TMP0]])
28+
; CHECK-NEXT: [[B:%.*]] = load i8, ptr [[A]], align 4
29+
; CHECK-NEXT: ret i8 [[B]]
30+
;
31+
entry:
32+
%b = load i8, ptr %a, align 4
33+
ret i8 %b
34+
}
35+
;.
36+
; CHECK: attributes #[[ATTR0]] = { sanitize_hwaddress }
37+
; CHECK: attributes #[[ATTR1:[0-9]+]] = { nounwind }
38+
;.
39+
; CHECK: [[META0]] = !{ptr @hwasan.note}
40+
; CHECK: [[META1:![0-9]+]] = !{i32 4, !"nosanitize_hwaddress", i32 1}
41+
;.
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-attributes --check-globals all --version 5
2+
; This test checks in the second run, function is not instrumented again.
3+
; RUN: opt < %s -msan-check-access-address=0 -S -passes=msan,msan | FileCheck %s
4+
5+
target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64-S128"
6+
target triple = "x86_64-unknown-linux-gnu"
7+
8+
;.
9+
; CHECK: @llvm.used = appending global [1 x ptr] [ptr @msan.module_ctor], section "llvm.metadata"
10+
; CHECK: @llvm.global_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 0, ptr @msan.module_ctor, ptr null }]
11+
; CHECK: @__msan_retval_tls = external thread_local(initialexec) global [100 x i64]
12+
; CHECK: @__msan_retval_origin_tls = external thread_local(initialexec) global i32
13+
; CHECK: @__msan_param_tls = external thread_local(initialexec) global [100 x i64]
14+
; CHECK: @__msan_param_origin_tls = external thread_local(initialexec) global [200 x i32]
15+
; CHECK: @__msan_va_arg_tls = external thread_local(initialexec) global [100 x i64]
16+
; CHECK: @__msan_va_arg_origin_tls = external thread_local(initialexec) global [200 x i32]
17+
; CHECK: @__msan_va_arg_overflow_size_tls = external thread_local(initialexec) global i64
18+
;.
19+
define void @array() sanitize_memory {
20+
; CHECK: Function Attrs: sanitize_memory
21+
; CHECK-LABEL: define void @array(
22+
; CHECK-SAME: ) #[[ATTR0:[0-9]+]] {
23+
; CHECK-NEXT: [[ENTRY:.*:]]
24+
; CHECK-NEXT: call void @llvm.donothing()
25+
; CHECK-NEXT: [[X:%.*]] = alloca i32, i64 5, align 4
26+
; CHECK-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[X]] to i64
27+
; CHECK-NEXT: [[TMP1:%.*]] = xor i64 [[TMP0]], 87960930222080
28+
; CHECK-NEXT: [[TMP2:%.*]] = inttoptr i64 [[TMP1]] to ptr
29+
; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP2]], i8 -1, i64 20, i1 false)
30+
; CHECK-NEXT: ret void
31+
;
32+
entry:
33+
%x = alloca i32, i64 5, align 4
34+
ret void
35+
}
36+
;.
37+
; CHECK: attributes #[[ATTR0]] = { sanitize_memory }
38+
; CHECK: attributes #[[ATTR1:[0-9]+]] = { nounwind }
39+
; CHECK: attributes #[[ATTR2:[0-9]+]] = { nocallback nofree nosync nounwind willreturn memory(none) }
40+
; CHECK: attributes #[[ATTR3:[0-9]+]] = { nocallback nofree nounwind willreturn memory(argmem: write) }
41+
;.
42+
; CHECK: [[META0:![0-9]+]] = !{i32 4, !"nosanitize_memory", i32 1}
43+
;.
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-attributes --check-globals --version 2
2+
; RUN: opt < %s -passes=tsan-module,tsan-module -S | FileCheck %s
3+
4+
target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64-S128"
5+
6+
declare void @can_throw()
7+
declare void @cannot_throw() nounwind
8+
9+
;.
10+
; CHECK: @llvm.used = appending global [1 x ptr] [ptr @tsan.module_ctor], section "llvm.metadata"
11+
; CHECK: @llvm.global_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 0, ptr @tsan.module_ctor, ptr null }]
12+
;.
13+
define i32 @func1() sanitize_thread {
14+
; CHECK: Function Attrs: sanitize_thread
15+
; CHECK-LABEL: define i32 @func1
16+
; CHECK-SAME: () #[[ATTR1:[0-9]+]] {
17+
; CHECK-NEXT: call void @can_throw()
18+
; CHECK-NEXT: ret i32 0
19+
;
20+
call void @can_throw()
21+
ret i32 0
22+
}
23+
;.
24+
; CHECK: attributes #[[ATTR0:[0-9]+]] = { nounwind }
25+
; CHECK: attributes #[[ATTR1]] = { sanitize_thread }
26+
;.
27+
; CHECK: [[META0:![0-9]+]] = !{i32 4, !"nosanitize_thread", i32 1}
28+
;.

0 commit comments

Comments
 (0)