Skip to content
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

[ASan][compiler-rt] Implemented Dormant Mode in ASan #99857

Open
wants to merge 18 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 17 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions compiler-rt/include/sanitizer/asan_interface.h
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,13 @@ void SANITIZER_CDECL __asan_set_death_callback(void (*callback)(void));
void SANITIZER_CDECL
__asan_set_error_report_callback(void (*callback)(const char *));

/// Sets whether ASan should be dormant or not. If 0 is passed, ASan resumes
/// checking memory accesses for errors. If a non-0 value is passed, these
/// checks will be skipped.
///
/// \param dormancy A boolean to control if ASan is dormant or not.
void SANITIZER_CDECL __asan_set_dormant(int dormancy);
melver marked this conversation as resolved.
Show resolved Hide resolved

/// User-provided callback on ASan errors.
///
/// You can provide a function that would be called immediately when ASan
Expand Down
1 change: 1 addition & 0 deletions compiler-rt/lib/asan/asan_interface.inc
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ INTERFACE_FUNCTION(__asan_report_store16_noabort)
INTERFACE_FUNCTION(__asan_report_store_n_noabort)
INTERFACE_FUNCTION(__asan_set_death_callback)
INTERFACE_FUNCTION(__asan_set_error_report_callback)
INTERFACE_FUNCTION(__asan_set_dormant)
INTERFACE_FUNCTION(__asan_set_shadow_00)
INTERFACE_FUNCTION(__asan_set_shadow_01)
INTERFACE_FUNCTION(__asan_set_shadow_02)
Expand Down
6 changes: 6 additions & 0 deletions compiler-rt/lib/asan/asan_interface_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,9 @@ extern "C" {
SANITIZER_INTERFACE_ATTRIBUTE
void __asan_set_error_report_callback(void (*callback)(const char*));

SANITIZER_INTERFACE_ATTRIBUTE
void __asan_set_dormant(int dormancy);

SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
void __asan_on_error();

Expand All @@ -202,6 +205,9 @@ extern "C" {
SANITIZER_INTERFACE_ATTRIBUTE
extern uptr *__asan_test_only_reported_buggy_pointer;

SANITIZER_INTERFACE_ATTRIBUTE
extern bool __asan_is_dormant;
melver marked this conversation as resolved.
Show resolved Hide resolved

SANITIZER_INTERFACE_ATTRIBUTE void __asan_load1(uptr p);
SANITIZER_INTERFACE_ATTRIBUTE void __asan_load2(uptr p);
SANITIZER_INTERFACE_ATTRIBUTE void __asan_load4(uptr p);
Expand Down
3 changes: 3 additions & 0 deletions compiler-rt/lib/asan/asan_rtl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@
uptr __asan_shadow_memory_dynamic_address; // Global interface symbol.
int __asan_option_detect_stack_use_after_return; // Global interface symbol.
uptr *__asan_test_only_reported_buggy_pointer; // Used only for testing asan.
bool __asan_is_dormant = true; // Used only if compiling with dormant asan

void __asan_set_dormant(int dormancy) { __asan_is_dormant = dormancy; }

namespace __asan {

Expand Down
37 changes: 37 additions & 0 deletions compiler-rt/test/asan/TestCases/dormant.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// RUN: %clangxx_asan -DREAD -O0 -mllvm -asan-dormant %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-READ
// RUN: %clangxx_asan -O0 -mllvm -asan-dormant %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-WRITE
// REQUIRES: stable-runtime

#include <sanitizer/asan_interface.h>
int readHeapAfterFree(){
int * volatile x = new int[10];
delete[] x;
return x[5];
}

static int * volatile y;
int writeHeapAfterFree(){
y = new int[10];
delete[] y;
return y[5] = 413;
}

int main() {
#ifdef READ
readHeapAfterFree();
// CHECK-READCHECK-NOT {{.*ERROR: AddressSanitizer: heap-use-after-free on address}}
__asan_set_dormant(false);
readHeapAfterFree();
// CHECK-READ: {{.*ERROR: AddressSanitizer: heap-use-after-free on address}}

#else

writeHeapAfterFree();
// CHECK-WRITE-NOT {{.*ERROR: AddressSanitizer: heap-use-after-free on address}}
__asan_set_dormant(false);
writeHeapAfterFree();
// CHECK-WRITE: {{.*ERROR: AddressSanitizer: heap-use-after-free on address}}
#endif

return 0;
}
27 changes: 25 additions & 2 deletions llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,8 @@ const char kAMDGPUAddressPrivateName[] = "llvm.amdgcn.is.private";
const char kAMDGPUBallotName[] = "llvm.amdgcn.ballot.i64";
const char kAMDGPUUnreachableName[] = "llvm.amdgcn.unreachable";

const char kAsanDormancyVarName[] = "__asan_is_dormant";

// Accesses sizes are powers of two: 1, 2, 4, 8, 16.
static const size_t kNumberOfAccessSizes = 5;

Expand Down Expand Up @@ -452,6 +454,10 @@ static cl::opt<int> ClDebugMin("asan-debug-min", cl::desc("Debug min inst"),
static cl::opt<int> ClDebugMax("asan-debug-max", cl::desc("Debug max inst"),
cl::Hidden, cl::init(-1));

static cl::opt<bool> CLAsanDormant("asan-dormant",
melver marked this conversation as resolved.
Show resolved Hide resolved
cl::desc("Makes asan dormant"), cl::Hidden,
cl::init(false));

STATISTIC(NumInstrumentedReads, "Number of instrumented reads");
STATISTIC(NumInstrumentedWrites, "Number of instrumented writes");
STATISTIC(NumOptimizedAccessesToGlobalVar,
Expand Down Expand Up @@ -852,6 +858,7 @@ struct AddressSanitizer {

FunctionCallee AsanMemmove, AsanMemcpy, AsanMemset;
Value *LocalDynamicShadow = nullptr;
Constant *DormantAsanFlag;
const StackSafetyGlobalInfo *SSGI;
DenseMap<const AllocaInst *, bool> ProcessedAllocas;

Expand Down Expand Up @@ -1584,6 +1591,12 @@ bool AddressSanitizer::GlobalIsLinkerInitialized(GlobalVariable *G) {
void AddressSanitizer::instrumentPointerComparisonOrSubtraction(
Instruction *I, RuntimeCallInserter &RTCI) {
IRBuilder<> IRB(I);

if (CLAsanDormant)
IRB.SetInsertPoint(SplitBlockAndInsertIfThen(
IRB.CreateNot(IRB.CreateLoad(IRB.getInt1Ty(), DormantAsanFlag)), I,
false, MDBuilder(*C).createUnlikelyBranchWeights()));

FunctionCallee F = isa<ICmpInst>(I) ? AsanPtrCmpFunction : AsanPtrSubFunction;
Value *Param[2] = {I->getOperand(0), I->getOperand(1)};
for (Value *&i : Param) {
Expand Down Expand Up @@ -1729,15 +1742,23 @@ void AddressSanitizer::instrumentMop(ObjectSizeOffsetVisitor &ObjSizeVis,
NumInstrumentedWrites++;
else
NumInstrumentedReads++;

Instruction* InsertPoint = O.getInsn();
if(CLAsanDormant){
InstrumentationIRBuilder IRB(InsertPoint);
InsertPoint = SplitBlockAndInsertIfThen(
IRB.CreateNot(IRB.CreateLoad(IRB.getInt1Ty(), DormantAsanFlag)),
InsertPoint, false, MDBuilder(*C).createUnlikelyBranchWeights());
}

unsigned Granularity = 1 << Mapping.Scale;
if (O.MaybeMask) {
instrumentMaskedLoadOrStore(this, DL, IntptrTy, O.MaybeMask, O.MaybeEVL,
O.MaybeStride, O.getInsn(), Addr, O.Alignment,
O.MaybeStride, InsertPoint, Addr, O.Alignment,
Granularity, O.OpType, O.IsWrite, nullptr,
UseCalls, Exp, RTCI);
} else {
doInstrumentAddress(this, O.getInsn(), O.getInsn(), Addr, O.Alignment,
doInstrumentAddress(this, InsertPoint, InsertPoint, Addr, O.Alignment,
Granularity, O.TypeStoreSize, O.IsWrite, nullptr,
UseCalls, Exp, RTCI);
}
Expand Down Expand Up @@ -2860,6 +2881,8 @@ void AddressSanitizer::initializeCallbacks(Module &M, const TargetLibraryInfo *T
M.getOrInsertFunction(kAMDGPUAddressSharedName, IRB.getInt1Ty(), PtrTy);
AMDGPUAddressPrivate =
M.getOrInsertFunction(kAMDGPUAddressPrivateName, IRB.getInt1Ty(), PtrTy);

DormantAsanFlag = M.getOrInsertGlobal(kAsanDormancyVarName, IRB.getInt1Ty());
}

bool AddressSanitizer::maybeInsertAsanInitAtFunctionEntry(Function &F) {
Expand Down
88 changes: 88 additions & 0 deletions llvm/test/Instrumentation/AddressSanitizer/dormant.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
; RUN: opt < %s -passes=asan -asan-dormant -S | FileCheck --check-prefixes=CHECK %s

target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"

; Function Attrs: mustprogress noinline nounwind optnone uwtable
define dso_local i32 @test(ptr %a) sanitize_address {
; CHECK-LABEL: define dso_local i32 @test(
; CHECK-SAME: ptr [[A:%.*]]) #[[ATTR0:[0-9]+]] {
; CHECK-NEXT: [[ENTRY:.*:]]
; CHECK-NEXT: [[A_ADDR:%.*]] = alloca ptr, align 8
; CHECK-NEXT: store ptr [[A]], ptr [[A_ADDR]], align 8
; CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[A_ADDR]], align 8
; CHECK-NEXT: [[TMP1:%.*]] = load i1, ptr @__asan_is_dormant, align 1
; CHECK-NEXT: [[TMP2:%.*]] = xor i1 [[TMP1]], true
; CHECK-NEXT: br i1 [[TMP2]], label %[[BB3:.*]], label %[[BB17:.*]], !prof [[PROF7:![0-9]+]]
; CHECK: [[BB3]]:
; CHECK-NEXT: [[TMP4:%.*]] = ptrtoint ptr [[TMP0]] to i64
; CHECK-NEXT: [[TMP5:%.*]] = lshr i64 [[TMP4]], 3
; CHECK-NEXT: [[TMP6:%.*]] = add i64 [[TMP5]], 2147450880
; CHECK-NEXT: [[TMP7:%.*]] = inttoptr i64 [[TMP6]] to ptr
; CHECK-NEXT: [[TMP8:%.*]] = load i8, ptr [[TMP7]], align 1
; CHECK-NEXT: [[TMP9:%.*]] = icmp ne i8 [[TMP8]], 0
; CHECK-NEXT: br i1 [[TMP9]], label %[[BB10:.*]], label %[[BB16:.*]], !prof [[PROF7]]
; CHECK: [[BB10]]:
; CHECK-NEXT: [[TMP11:%.*]] = and i64 [[TMP4]], 7
; CHECK-NEXT: [[TMP12:%.*]] = add i64 [[TMP11]], 3
; CHECK-NEXT: [[TMP13:%.*]] = trunc i64 [[TMP12]] to i8
; CHECK-NEXT: [[TMP14:%.*]] = icmp sge i8 [[TMP13]], [[TMP8]]
; CHECK-NEXT: br i1 [[TMP14]], label %[[BB15:.*]], label %[[BB16]]
; CHECK: [[BB15]]:
; CHECK-NEXT: call void @__asan_report_store4(i64 [[TMP4]]) #[[ATTR3:[0-9]+]]
; CHECK-NEXT: unreachable
; CHECK: [[BB16]]:
; CHECK-NEXT: br label %[[BB17]]
; CHECK: [[BB17]]:
; CHECK-NEXT: store i32 5, ptr [[TMP0]], align 4
; CHECK-NEXT: [[TMP18:%.*]] = load ptr, ptr [[A_ADDR]], align 8
; CHECK-NEXT: [[TMP19:%.*]] = load i1, ptr @__asan_is_dormant, align 1
; CHECK-NEXT: [[TMP20:%.*]] = xor i1 [[TMP19]], true
; CHECK-NEXT: br i1 [[TMP20]], label %[[BB21:.*]], label %[[BB35:.*]], !prof [[PROF7]]
; CHECK: [[BB21]]:
; CHECK-NEXT: [[TMP22:%.*]] = ptrtoint ptr [[TMP18]] to i64
; CHECK-NEXT: [[TMP23:%.*]] = lshr i64 [[TMP22]], 3
; CHECK-NEXT: [[TMP24:%.*]] = add i64 [[TMP23]], 2147450880
; CHECK-NEXT: [[TMP25:%.*]] = inttoptr i64 [[TMP24]] to ptr
; CHECK-NEXT: [[TMP26:%.*]] = load i8, ptr [[TMP25]], align 1
; CHECK-NEXT: [[TMP27:%.*]] = icmp ne i8 [[TMP26]], 0
; CHECK-NEXT: br i1 [[TMP27]], label %[[BB28:.*]], label %[[BB34:.*]], !prof [[PROF7]]
; CHECK: [[BB28]]:
; CHECK-NEXT: [[TMP29:%.*]] = and i64 [[TMP22]], 7
; CHECK-NEXT: [[TMP30:%.*]] = add i64 [[TMP29]], 3
; CHECK-NEXT: [[TMP31:%.*]] = trunc i64 [[TMP30]] to i8
; CHECK-NEXT: [[TMP32:%.*]] = icmp sge i8 [[TMP31]], [[TMP26]]
; CHECK-NEXT: br i1 [[TMP32]], label %[[BB33:.*]], label %[[BB34]]
; CHECK: [[BB33]]:
; CHECK-NEXT: call void @__asan_report_load4(i64 [[TMP22]]) #[[ATTR3]]
; CHECK-NEXT: unreachable
; CHECK: [[BB34]]:
; CHECK-NEXT: br label %[[BB35]]
; CHECK: [[BB35]]:
; CHECK-NEXT: [[TMP36:%.*]] = load i32, ptr [[TMP18]], align 4
; CHECK-NEXT: ret i32 [[TMP36]]
;
entry:
%a.addr = alloca ptr, align 8
store ptr %a, ptr %a.addr, align 8
%0 = load ptr, ptr %a.addr, align 8
store i32 5, ptr %0, align 4
%1 = load ptr, ptr %a.addr, align 8
%2 = load i32, ptr %1, align 4
ret i32 %2

}

!llvm.module.flags = !{!0, !1, !2, !3, !4}
!llvm.ident = !{!5}

!0 = !{i32 1, !"wchar_size", i32 4}
!1 = !{i32 8, !"PIC Level", i32 2}
!2 = !{i32 7, !"PIE Level", i32 2}
!3 = !{i32 7, !"uwtable", i32 2}
!4 = !{i32 7, !"frame-pointer", i32 2}
!5 = !{!"clang version 20.0.0git (https://github.com/gbMattN/llvm-project.git 3d0736cd0a60f7f0f78a14982091e5687e2be7da)"}
;.
; CHECK: [[PROF7]] = !{!"branch_weights", i32 1, i32 1048575}
;.
Loading