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

Conversation

gbMattN
Copy link
Contributor

@gbMattN gbMattN commented Jul 22, 2024

Added the ability to skip runtime checks with ASan, while maintaining shadow memory coherency. You can then dynamically re-enable the checks.
There is a discussion on discourse: https://discourse.llvm.org/t/rfc-adding-a-dormant-mode-to-addresssanitizer/80278

Copy link

Thank you for submitting a Pull Request (PR) to the LLVM Project!

This PR will be automatically labeled and the relevant teams will be
notified.

If you wish to, you can add reviewers by using the "Reviewers" section on this page.

If this is not working for you, it is probably because you do not have write
permissions for the repository. In which case you can instead tag reviewers by
name in a comment by using @ followed by their GitHub username.

If you have received no comments on your PR for a week, you can request a review
by "ping"ing the PR by adding a comment “Ping”. The common courtesy "ping" rate
is once a week. Please remember that you are asking for valuable time from other developers.

If you have further questions, they may be answered by the LLVM GitHub User Guide.

You can also ask questions in a comment on this PR, on the LLVM Discord or on the forums.

@llvmbot
Copy link
Collaborator

llvmbot commented Jul 22, 2024

@llvm/pr-subscribers-llvm-transforms

@llvm/pr-subscribers-compiler-rt-sanitizer

Author: None (gbMattN)

Changes

Added the ability to skip runtime checks with ASan, while maintaining shadow memory coherency. You can then dynamically re-enable the checks.
There is a discussion on discourse: https://discourse.llvm.org/t/rfc-adding-a-dormant-mode-to-addresssanitizer/80278


Full diff: https://github.com/llvm/llvm-project/pull/99857.diff

5 Files Affected:

  • (modified) compiler-rt/include/sanitizer/asan_interface.h (+7)
  • (modified) compiler-rt/lib/asan/asan_interface.inc (+1)
  • (modified) compiler-rt/lib/asan/asan_interface_internal.h (+6)
  • (modified) compiler-rt/lib/asan/asan_rtl.cpp (+3)
  • (modified) llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp (+43-5)
diff --git a/compiler-rt/include/sanitizer/asan_interface.h b/compiler-rt/include/sanitizer/asan_interface.h
index 37b6d08f4db19..f6ca4acb03e9d 100644
--- a/compiler-rt/include/sanitizer/asan_interface.h
+++ b/compiler-rt/include/sanitizer/asan_interface.h
@@ -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);
+
 /// User-provided callback on ASan errors.
 ///
 /// You can provide a function that would be called immediately when ASan
diff --git a/compiler-rt/lib/asan/asan_interface.inc b/compiler-rt/lib/asan/asan_interface.inc
index bfc44b4619623..499014fcc4a49 100644
--- a/compiler-rt/lib/asan/asan_interface.inc
+++ b/compiler-rt/lib/asan/asan_interface.inc
@@ -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)
diff --git a/compiler-rt/lib/asan/asan_interface_internal.h b/compiler-rt/lib/asan/asan_interface_internal.h
index a998263780221..9b2752ce7e05b 100644
--- a/compiler-rt/lib/asan/asan_interface_internal.h
+++ b/compiler-rt/lib/asan/asan_interface_internal.h
@@ -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();
 
@@ -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;
+
   SANITIZER_INTERFACE_ATTRIBUTE void __asan_load1(uptr p);
   SANITIZER_INTERFACE_ATTRIBUTE void __asan_load2(uptr p);
   SANITIZER_INTERFACE_ATTRIBUTE void __asan_load4(uptr p);
diff --git a/compiler-rt/lib/asan/asan_rtl.cpp b/compiler-rt/lib/asan/asan_rtl.cpp
index c484e086a5ef7..56da1180cfc28 100644
--- a/compiler-rt/lib/asan/asan_rtl.cpp
+++ b/compiler-rt/lib/asan/asan_rtl.cpp
@@ -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;
+
+void __asan_set_dormant(int dormancy) { __asan_is_dormant = dormancy; }
 
 namespace __asan {
 
diff --git a/llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp b/llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp
index adf77f20cb1c7..774820ae68924 100644
--- a/llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp
+++ b/llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp
@@ -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;
 
@@ -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",
+                                   cl::desc("Makes asan dormant"), cl::Hidden,
+                                   cl::init(false));
+
 STATISTIC(NumInstrumentedReads, "Number of instrumented reads");
 STATISTIC(NumInstrumentedWrites, "Number of instrumented writes");
 STATISTIC(NumOptimizedAccessesToGlobalVar,
@@ -852,6 +858,7 @@ struct AddressSanitizer {
 
   FunctionCallee AsanMemmove, AsanMemcpy, AsanMemset;
   Value *LocalDynamicShadow = nullptr;
+  Constant *DormantAsanFlag;
   const StackSafetyGlobalInfo *SSGI;
   DenseMap<const AllocaInst *, bool> ProcessedAllocas;
 
@@ -1579,6 +1586,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));
+
   FunctionCallee F = isa<ICmpInst>(I) ? AsanPtrCmpFunction : AsanPtrSubFunction;
   Value *Param[2] = {I->getOperand(0), I->getOperand(1)};
   for (Value *&i : Param) {
@@ -1593,9 +1606,19 @@ static void doInstrumentAddress(AddressSanitizer *Pass, Instruction *I,
                                 MaybeAlign Alignment, unsigned Granularity,
                                 TypeSize TypeStoreSize, bool IsWrite,
                                 Value *SizeArgument, bool UseCalls,
-                                uint32_t Exp, RuntimeCallInserter &RTCI) {
+                                uint32_t Exp, RuntimeCallInserter &RTCI,
+                                Value *DormantAsanFlag) {
   // Instrument a 1-, 2-, 4-, 8-, or 16- byte access with one check
   // if the data is properly aligned.
+
+  Instruction *InsertPoint = InsertBefore;
+  if (CLAsanDormant) {
+    InstrumentationIRBuilder IRB(InsertPoint);
+    InsertPoint = SplitBlockAndInsertIfThen(
+        IRB.CreateNot(IRB.CreateLoad(IRB.getInt1Ty(), DormantAsanFlag)),
+        InsertBefore, false);
+  }
+
   if (!TypeStoreSize.isScalable()) {
     const auto FixedSize = TypeStoreSize.getFixedValue();
     switch (FixedSize) {
@@ -1606,12 +1629,12 @@ static void doInstrumentAddress(AddressSanitizer *Pass, Instruction *I,
     case 128:
       if (!Alignment || *Alignment >= Granularity ||
           *Alignment >= FixedSize / 8)
-        return Pass->instrumentAddress(I, InsertBefore, Addr, Alignment,
+        return Pass->instrumentAddress(I, InsertPoint, Addr, Alignment,
                                        FixedSize, IsWrite, nullptr, UseCalls,
                                        Exp, RTCI);
     }
   }
-  Pass->instrumentUnusualSizeOrAlignment(I, InsertBefore, Addr, TypeStoreSize,
+  Pass->instrumentUnusualSizeOrAlignment(I, InsertPoint, Addr, TypeStoreSize,
                                          IsWrite, nullptr, UseCalls, Exp, RTCI);
 }
 
@@ -1627,6 +1650,14 @@ void AddressSanitizer::instrumentMaskedLoadOrStore(
 
   IRBuilder IB(I);
   Instruction *LoopInsertBefore = I;
+
+  if (CLAsanDormant) {
+    LoopInsertBefore = SplitBlockAndInsertIfThen(
+        IB.CreateNot(IB.CreateLoad(IB.getInt1Ty(), DormantAsanFlag)),
+        LoopInsertBefore, false);
+    IB.SetInsertPoint(LoopInsertBefore);
+  }
+
   if (EVL) {
     // The end argument of SplitBlockAndInsertForLane is assumed bigger
     // than zero, so we should check whether EVL is zero here.
@@ -1677,7 +1708,7 @@ void AddressSanitizer::instrumentMaskedLoadOrStore(
     }
     doInstrumentAddress(Pass, I, &*IRB.GetInsertPoint(), InstrumentedAddress,
                         Alignment, Granularity, ElemTypeSize, IsWrite,
-                        SizeArgument, UseCalls, Exp, RTCI);
+                        SizeArgument, UseCalls, Exp, RTCI, DormantAsanFlag);
   });
 }
 
@@ -1734,7 +1765,7 @@ void AddressSanitizer::instrumentMop(ObjectSizeOffsetVisitor &ObjSizeVis,
   } else {
     doInstrumentAddress(this, O.getInsn(), O.getInsn(), Addr, O.Alignment,
                         Granularity, O.TypeStoreSize, O.IsWrite, nullptr,
-                        UseCalls, Exp, RTCI);
+                        UseCalls, Exp, RTCI, DormantAsanFlag);
   }
 }
 
@@ -1846,6 +1877,11 @@ void AddressSanitizer::instrumentAddress(Instruction *OrigIns,
   }
 
   InstrumentationIRBuilder IRB(InsertBefore);
+  if (CLAsanDormant)
+    IRB.SetInsertPoint(SplitBlockAndInsertIfThen(
+        IRB.CreateNot(IRB.CreateLoad(IRB.getInt1Ty(), DormantAsanFlag)),
+        InsertBefore, false));
+
   size_t AccessSizeIndex = TypeStoreSizeToSizeIndex(TypeStoreSize);
   const ASanAccessInfo AccessInfo(IsWrite, CompileKernel, AccessSizeIndex);
 
@@ -2856,6 +2892,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) {

@gbMattN gbMattN marked this pull request as draft July 22, 2024 10:41
@gbMattN
Copy link
Contributor Author

gbMattN commented Jul 22, 2024

@kcc ((manually add reviewers))

Copy link
Collaborator

@vitalybuka vitalybuka left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Technically you didn't request review, but it's not on my dashboard.
To hide it, I'll pretend requesting changing.

Please "re-request" review when needed.

@gbMattN gbMattN force-pushed the users/gbmatt/implement_dormant_asan branch from 46af41f to 960c610 Compare August 8, 2024 09:49
@gbMattN gbMattN force-pushed the users/gbmatt/implement_dormant_asan branch from 6a0f997 to f99eebe Compare August 12, 2024 12:22
@gbMattN gbMattN force-pushed the users/gbmatt/implement_dormant_asan branch from f99eebe to 2d89edd Compare August 13, 2024 09:26
@gbMattN gbMattN marked this pull request as ready for review August 13, 2024 09:27
@gbMattN gbMattN force-pushed the users/gbmatt/implement_dormant_asan branch from 0a86a04 to df2f76f Compare August 14, 2024 11:00
@gbMattN gbMattN force-pushed the users/gbmatt/implement_dormant_asan branch from df2f76f to 0cae8ad Compare August 14, 2024 11:02
@gbMattN
Copy link
Contributor Author

gbMattN commented Aug 15, 2024

@vitalybuka ready for review! I've put performance stats on the discourse page. There was an lldb test failing (functionalities/single-thread-step/TestSingleThreadStepTimeout.py timed out) but I didn't touch any lldb code. I've sent a blank commit to rerun the tests in case it was being flakey.

Copy link

⚠️ C/C++ code formatter, clang-format found issues in your code. ⚠️

You can test this locally with the following command:
git-clang-format --diff 8107810cad24d41fe43c6777370c7b81ca83ad84 86205e3e453dd4d54b3bd7852e12aa9189d9c888 --extensions inc,h,cpp -- compiler-rt/test/asan/TestCases/dormant.cpp compiler-rt/include/sanitizer/asan_interface.h compiler-rt/lib/asan/asan_interface.inc compiler-rt/lib/asan/asan_interface_internal.h compiler-rt/lib/asan/asan_rtl.cpp llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp
View the diff from clang-format here.
diff --git a/compiler-rt/test/asan/TestCases/dormant.cpp b/compiler-rt/test/asan/TestCases/dormant.cpp
index 5efa2f9836..04c0c140b2 100644
--- a/compiler-rt/test/asan/TestCases/dormant.cpp
+++ b/compiler-rt/test/asan/TestCases/dormant.cpp
@@ -3,14 +3,14 @@
 // REQUIRES: stable-runtime
 
 #include <sanitizer/asan_interface.h>
-int readHeapAfterFree(){
-  int * volatile x = new int[10];
+int readHeapAfterFree() {
+  int *volatile x = new int[10];
   delete[] x;
   return x[5];
 }
 
-static int * volatile y;
-int writeHeapAfterFree(){
+static int *volatile y;
+int writeHeapAfterFree() {
   y = new int[10];
   delete[] y;
   return y[5] = 413;
diff --git a/llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp b/llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp
index 3665930f9c..a34ec2e82d 100644
--- a/llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp
+++ b/llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp
@@ -1742,9 +1742,9 @@ void AddressSanitizer::instrumentMop(ObjectSizeOffsetVisitor &ObjSizeVis,
     NumInstrumentedWrites++;
   else
     NumInstrumentedReads++;
-  
-  Instruction* InsertPoint = O.getInsn();
-  if(CLAsanDormant){
+
+  Instruction *InsertPoint = O.getInsn();
+  if (CLAsanDormant) {
     InstrumentationIRBuilder IRB(InsertPoint);
     InsertPoint = SplitBlockAndInsertIfThen(
         IRB.CreateNot(IRB.CreateLoad(IRB.getInt1Ty(), DormantAsanFlag)),

@gbMattN gbMattN force-pushed the users/gbmatt/implement_dormant_asan branch from 3ccccdd to 954c739 Compare August 23, 2024 13:00
@gbMattN gbMattN requested a review from melver August 23, 2024 13:06
Copy link
Collaborator

@vitalybuka vitalybuka left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't have consensus on https://discourse.llvm.org/t/rfc-adding-a-dormant-mode-to-addresssanitizer/80278 about usefulness of the feature.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants