-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[Sanitizer] Make sanitizer passes idempotent #99439
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-attributes --check-globals all --version 5 | ||
; This test checks in the second run, function is not instrumented again. | ||
; RUN: opt < %s -passes=asan,asan -S | FileCheck %s | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is this needed (the patch)? Time to time we had discussion to do exactly like that, but usually it's not hard to make sure that pass is unique in pipeline There is also ConstructorKind::kNone when constructor is not inserted. Inconsistency with other sanitizers is also not nice. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We found this issue when a HIP program is first compiled to bitcode with -fsanitize=address, then the bitcode is compiled to object file with -fsanitize=address again. This lead to calculation of shadow memory twice due to double instrumentation. As you pointed out, ConstructorKind::None can lead to asan.module_ctor to be not present in the module. Do you suggest any other way to check if module is instrumented? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @vitalybuka I think it's a good idea to make all sanitizers idempotent. Your guidance on preferred approaches would be appreciated. With separate compilation, runtime compilation, etc. it seems hard to always "make sure that pass is unique in the pipeline". There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It would be nice to have some generic approach, which will work regardless of constructors. How about about Module::addModuleFlag("nosanitize") which will be set and checked by every sanitizer? I am not sure if this require RFC on discourse. @nikic @efriedma-quic WDYT? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Have updated PR to use "nosanitize" module flag. Could also check the return value for key "nosanitize". Currently its set to "1" at end of asan pass run. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure a generic nosanitize module flag makes sense. Things will break if there are two sanitizers that aren't mutually exclusive and both try to use the flag. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @nikic Could something like below be done so that sanitizers don't confuse with just "nosanitize" flag? Add module flag "nosanitize" with a key that is enum SanitizerKind (with all supported sanitizers listed in it), defined in common header file for sanitizers. So, before running the pass, query the "nosanitize" flag and check that corresponding key value is present, to check if the pass has previously instrumented the IR. M.addModuleFlag(Module::ModFlagBehavior::Override, "nosanitize", SanitizerKind::Address); There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You'd want a separate module flag for each sanitizer. I'm a little skeptical that you pass pipeline is really doing what you want if you end up running sanitizer passes twice; that indicates you're running a bunch of other optimization passes twice without a good reason. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Instrumenting a shadow load practically guarantees an immediate memory fault or other catastrophic behavior. I assume any optimization passes that could cause such trouble are already idempotent. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @nikic We don't have such mixing-compartible instrumenting sanitizers yet, but having unique flags is LGTM @efriedma-quic all previous proposals to add such "re-run" protection ended up with "the pipeline is wrong". But implementation is so trivial, so I don't mind just to have it implemented. |
||
|
||
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" | ||
target triple = "x86_64-unknown-linux-gnu" | ||
|
||
; Function with sanitize_address is instrumented. | ||
; Function Attrs: nounwind uwtable | ||
;. | ||
; CHECK: @___asan_globals_registered = common hidden global i64 0 | ||
; CHECK: @__start_asan_globals = extern_weak hidden global i64 | ||
; CHECK: @__stop_asan_globals = extern_weak hidden global i64 | ||
;. | ||
define void @instr_sa(ptr %a) sanitize_address { | ||
; CHECK: Function Attrs: sanitize_address | ||
; CHECK-LABEL: define void @instr_sa( | ||
; CHECK-SAME: ptr [[A:%.*]]) #[[ATTR0:[0-9]+]] { | ||
; CHECK-NEXT: [[ENTRY:.*:]] | ||
; CHECK-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[A]] to i64 | ||
; CHECK-NEXT: [[TMP1:%.*]] = lshr i64 [[TMP0]], 3 | ||
; CHECK-NEXT: [[TMP2:%.*]] = add i64 [[TMP1]], 2147450880 | ||
; CHECK-NEXT: [[TMP3:%.*]] = inttoptr i64 [[TMP2]] to ptr | ||
; CHECK-NEXT: [[TMP4:%.*]] = load i8, ptr [[TMP3]], align 1 | ||
; CHECK-NEXT: [[TMP5:%.*]] = icmp ne i8 [[TMP4]], 0 | ||
; CHECK-NEXT: br i1 [[TMP5]], label %[[BB6:.*]], label %[[BB12:.*]], !prof [[PROF1:![0-9]+]] | ||
; CHECK: [[BB6]]: | ||
; CHECK-NEXT: [[TMP7:%.*]] = and i64 [[TMP0]], 7 | ||
; CHECK-NEXT: [[TMP8:%.*]] = add i64 [[TMP7]], 3 | ||
; CHECK-NEXT: [[TMP9:%.*]] = trunc i64 [[TMP8]] to i8 | ||
; CHECK-NEXT: [[TMP10:%.*]] = icmp sge i8 [[TMP9]], [[TMP4]] | ||
; CHECK-NEXT: br i1 [[TMP10]], label %[[BB11:.*]], label %[[BB12]] | ||
; CHECK: [[BB11]]: | ||
; CHECK-NEXT: call void @__asan_report_load4(i64 [[TMP0]]) #[[ATTR2:[0-9]+]] | ||
; CHECK-NEXT: unreachable | ||
; CHECK: [[BB12]]: | ||
; CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[A]], align 4 | ||
; CHECK-NEXT: [[TMP2:%.*]] = add i32 [[TMP1]], 1 | ||
; CHECK-NEXT: store i32 [[TMP2]], ptr [[A]], align 4 | ||
; CHECK-NEXT: ret void | ||
; | ||
entry: | ||
%tmp1 = load i32, ptr %a, align 4 | ||
%tmp2 = add i32 %tmp1, 1 | ||
store i32 %tmp2, ptr %a, align 4 | ||
ret void | ||
} | ||
;. | ||
; CHECK: attributes #[[ATTR0]] = { sanitize_address } | ||
; CHECK: attributes #[[ATTR1:[0-9]+]] = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } | ||
; CHECK: attributes #[[ATTR2]] = { nomerge } | ||
;. | ||
; CHECK: [[META0:![0-9]+]] = !{i32 4, !"nosanitize_address", i32 1} | ||
; CHECK: [[PROF1]] = !{!"branch_weights", i32 1, i32 1048575} | ||
;. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
; RUN: opt < %s -passes=dfsan,dfsan -S | FileCheck %s | ||
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" | ||
target triple = "x86_64-unknown-linux-gnu" | ||
|
||
define i8 @add(i8 %a, i8 %b) { | ||
; CHECK: @add.dfsan | ||
; CHECK-DAG: %[[#ALABEL:]] = load i8, ptr @__dfsan_arg_tls, align [[ALIGN:2]] | ||
; CHECK-DAG: %[[#BLABEL:]] = load i8, ptr inttoptr (i64 add (i64 ptrtoint (ptr @__dfsan_arg_tls to i64), i64 2) to ptr), align [[ALIGN]] | ||
; CHECK: %[[#UNION:]] = or i8 %[[#ALABEL]], %[[#BLABEL]] | ||
; CHECK: %c = add i8 %a, %b | ||
; CHECK: store i8 %[[#UNION]], ptr @__dfsan_retval_tls, align [[ALIGN]] | ||
; CHECK: ret i8 %c | ||
%c = add i8 %a, %b | ||
ret i8 %c | ||
} | ||
|
||
; CHECK: [[META0:![0-9]+]] = !{i32 4, !"nosanitize_dataflow", i32 1} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-attributes --check-globals --version 2 | ||
; Test basic address sanitizer instrumentation. | ||
; | ||
; RUN: opt < %s -passes=hwasan,hwasan -S | FileCheck %s | ||
|
||
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" | ||
target triple = "x86_64-unknown-linux-gnu" | ||
|
||
;. | ||
; CHECK: @llvm.used = appending global [1 x ptr] [ptr @hwasan.module_ctor], section "llvm.metadata" | ||
; CHECK: @llvm.global_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 0, ptr @hwasan.module_ctor, ptr @hwasan.module_ctor }] | ||
; CHECK: @__start_hwasan_globals = external hidden constant [0 x i8] | ||
; CHECK: @__stop_hwasan_globals = external hidden constant [0 x i8] | ||
; 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 | ||
; CHECK: @hwasan.dummy.global = private constant [0 x i8] zeroinitializer, section "hwasan_globals", comdat($hwasan.module_ctor), !associated [[META0:![0-9]+]] | ||
; CHECK: @__hwasan_tls = external thread_local(initialexec) global i64 | ||
; CHECK: @llvm.compiler.used = appending global [3 x ptr] [ptr @hwasan.note, ptr @hwasan.dummy.global, ptr @__hwasan_tls], section "llvm.metadata" | ||
; CHECK: @__hwasan_shadow = external global [0 x i8] | ||
;. | ||
define i8 @test_load8(ptr %a) sanitize_hwaddress { | ||
; CHECK: Function Attrs: sanitize_hwaddress | ||
; CHECK-LABEL: define i8 @test_load8 | ||
; CHECK-SAME: (ptr [[A:%.*]]) #[[ATTR0:[0-9]+]] { | ||
; CHECK-NEXT: entry: | ||
; CHECK-NEXT: [[DOTHWASAN_SHADOW:%.*]] = call ptr asm "", "=r,0"(ptr null) | ||
; CHECK-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[A]] to i64 | ||
; CHECK-NEXT: call void @__hwasan_load1(i64 [[TMP0]]) | ||
; CHECK-NEXT: [[B:%.*]] = load i8, ptr [[A]], align 4 | ||
; CHECK-NEXT: ret i8 [[B]] | ||
; | ||
entry: | ||
%b = load i8, ptr %a, align 4 | ||
ret i8 %b | ||
} | ||
;. | ||
; CHECK: attributes #[[ATTR0]] = { sanitize_hwaddress } | ||
; CHECK: attributes #[[ATTR1:[0-9]+]] = { nounwind } | ||
;. | ||
; CHECK: [[META0]] = !{ptr @hwasan.note} | ||
; CHECK: [[META1:![0-9]+]] = !{i32 4, !"nosanitize_hwaddress", i32 1} | ||
;. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-attributes --check-globals all --version 5 | ||
; This test checks in the second run, function is not instrumented again. | ||
; RUN: opt < %s -msan-check-access-address=0 -S -passes=msan,msan | FileCheck %s | ||
|
||
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" | ||
target triple = "x86_64-unknown-linux-gnu" | ||
|
||
;. | ||
; CHECK: @llvm.used = appending global [1 x ptr] [ptr @msan.module_ctor], section "llvm.metadata" | ||
; CHECK: @llvm.global_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 0, ptr @msan.module_ctor, ptr null }] | ||
; CHECK: @__msan_retval_tls = external thread_local(initialexec) global [100 x i64] | ||
; CHECK: @__msan_retval_origin_tls = external thread_local(initialexec) global i32 | ||
; CHECK: @__msan_param_tls = external thread_local(initialexec) global [100 x i64] | ||
; CHECK: @__msan_param_origin_tls = external thread_local(initialexec) global [200 x i32] | ||
; CHECK: @__msan_va_arg_tls = external thread_local(initialexec) global [100 x i64] | ||
; CHECK: @__msan_va_arg_origin_tls = external thread_local(initialexec) global [200 x i32] | ||
; CHECK: @__msan_va_arg_overflow_size_tls = external thread_local(initialexec) global i64 | ||
;. | ||
define void @array() sanitize_memory { | ||
; CHECK: Function Attrs: sanitize_memory | ||
; CHECK-LABEL: define void @array( | ||
; CHECK-SAME: ) #[[ATTR0:[0-9]+]] { | ||
; CHECK-NEXT: [[ENTRY:.*:]] | ||
; CHECK-NEXT: call void @llvm.donothing() | ||
; CHECK-NEXT: [[X:%.*]] = alloca i32, i64 5, align 4 | ||
; CHECK-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[X]] to i64 | ||
; CHECK-NEXT: [[TMP1:%.*]] = xor i64 [[TMP0]], 87960930222080 | ||
; CHECK-NEXT: [[TMP2:%.*]] = inttoptr i64 [[TMP1]] to ptr | ||
; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP2]], i8 -1, i64 20, i1 false) | ||
; CHECK-NEXT: ret void | ||
; | ||
entry: | ||
%x = alloca i32, i64 5, align 4 | ||
ret void | ||
} | ||
;. | ||
; CHECK: attributes #[[ATTR0]] = { sanitize_memory } | ||
; CHECK: attributes #[[ATTR1:[0-9]+]] = { nounwind } | ||
; CHECK: attributes #[[ATTR2:[0-9]+]] = { nocallback nofree nosync nounwind willreturn memory(none) } | ||
; CHECK: attributes #[[ATTR3:[0-9]+]] = { nocallback nofree nounwind willreturn memory(argmem: write) } | ||
;. | ||
; CHECK: [[META0:![0-9]+]] = !{i32 4, !"nosanitize_memory", i32 1} | ||
;. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-attributes --check-globals --version 2 | ||
; RUN: opt < %s -passes=tsan-module,tsan-module -S | FileCheck %s | ||
|
||
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" | ||
|
||
declare void @can_throw() | ||
declare void @cannot_throw() nounwind | ||
|
||
;. | ||
; CHECK: @llvm.used = appending global [1 x ptr] [ptr @tsan.module_ctor], section "llvm.metadata" | ||
; CHECK: @llvm.global_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 0, ptr @tsan.module_ctor, ptr null }] | ||
;. | ||
define i32 @func1() sanitize_thread { | ||
; CHECK: Function Attrs: sanitize_thread | ||
; CHECK-LABEL: define i32 @func1 | ||
; CHECK-SAME: () #[[ATTR1:[0-9]+]] { | ||
; CHECK-NEXT: call void @can_throw() | ||
; CHECK-NEXT: ret i32 0 | ||
; | ||
call void @can_throw() | ||
ret i32 0 | ||
} | ||
;. | ||
; CHECK: attributes #[[ATTR0:[0-9]+]] = { nounwind } | ||
; CHECK: attributes #[[ATTR1]] = { sanitize_thread } | ||
;. | ||
; CHECK: [[META0:![0-9]+]] = !{i32 4, !"nosanitize_thread", i32 1} | ||
;. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Early return please and DS_Error?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the suggestion. Have updated the patch in the latest commit.
Using DS_Error, causes second run of the pass to abort with error. So retained DS_Warning, so this returns true without abort and sanitizer passes return without doing second instrumentation.