[llvm] Rtsan/blocking 2 llvm pass (PR #109543)

via llvm-commits llvm-commits at lists.llvm.org
Sat Sep 21 11:58:55 PDT 2024


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-llvm-transforms

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

Author: None (davidtrevelyan)

<details>
<summary>Changes</summary>

# Pass update for sanitize_realtime_unsafe

Follows https://github.com/llvm/llvm-project/pull/106754

## Motivation

Calls to system library functions such as malloc are easy for RealtimeSanitizer to intercept. If such a call is made in a `[[clang::nonblocking]]` function (a real-time context), RealtimeSanitizer will error. Real-time programmers also write their own blocking (real-time unsafe) functions that may or may not call intercepted functions. We wish to introduce a mechanism whereby RealtimeSanitizer can error on calls to these, too, if called within a real-time context.

At the same time as introducing `[[clang::nonblocking]]`, the `[[clang::blocking]]` attribute was also introduced. With the function effects warnings (as errors) activated, blocking functions cannot be called from non-blocking functions, and this is enforced at compile time. The purpose of this series of PRs is to introduce similar functionality into RealtimeSanitizer, so that it can make the equivalent check at run time.

## Implementation

We recently merged the `sanitize_realtime_unsafe` LLVM function attribute into `main`. Our next steps are to:

1. when `sanitize_realtime_unsafe` is detected, update RealtimeSanitizer's LLVM pass to insert a call to `__rtsan_notify_blocking_call`, and
2. switch on the feature by updating Clang's CodeGen to actually add the `sanitize_realtime_unsafe` attribute to the IR for functions attributed with `[[clang::blocking]]`.

Once the feature is switched on, RealtimeSanitizer will error if any calls to functions attributed with `[[clang::blocking]]` are made from `[[clang::nonblocking]]` functions.

## Integration Roadmap

The above functionality is currently split into three patches.

- [x] Introduction of the sanitize_realtime_unsafe LLVM attribute (previous PR), and
- [ ] An update to RealtimeSanitizer's LLVM pass to use it (this PR)
- [ ] An update to Clang's CodeGen to add the `sanitize_realtime_unsafe` attribute to functions attributed with `[[clang::blocking]]` (next PR)


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


2 Files Affected:

- (modified) llvm/lib/Transforms/Instrumentation/RealtimeSanitizer.cpp (+26-3) 
- (added) llvm/test/Instrumentation/RealtimeSanitizer/rtsan_unsafe.ll (+19) 


``````````diff
diff --git a/llvm/lib/Transforms/Instrumentation/RealtimeSanitizer.cpp b/llvm/lib/Transforms/Instrumentation/RealtimeSanitizer.cpp
index 7854cf4f2c625f..f27cf83cd3cf3d 100644
--- a/llvm/lib/Transforms/Instrumentation/RealtimeSanitizer.cpp
+++ b/llvm/lib/Transforms/Instrumentation/RealtimeSanitizer.cpp
@@ -17,6 +17,7 @@
 #include "llvm/IR/IRBuilder.h"
 #include "llvm/IR/Module.h"
 
+#include "llvm/Demangle/Demangle.h"
 #include "llvm/Transforms/Instrumentation/RealtimeSanitizer.h"
 
 using namespace llvm;
@@ -45,6 +46,26 @@ static void insertCallAtAllFunctionExitPoints(Function &Fn,
         insertCallBeforeInstruction(Fn, I, InsertFnName);
 }
 
+static PreservedAnalyses rtsanPreservedCFGAnalyses() {
+  PreservedAnalyses PA;
+  PA.preserveSet<CFGAnalyses>();
+  return PA;
+}
+
+static void insertNotifyBlockingCallAtFunctionEntryPoint(Function &F) {
+  IRBuilder<> Builder(&F.front().front());
+  Value *NameArg = Builder.CreateGlobalString(demangle(F.getName()));
+
+  FunctionType *FuncType =
+      FunctionType::get(Type::getVoidTy(F.getContext()),
+                        {PointerType::getUnqual(F.getContext())}, false);
+
+  FunctionCallee Func = F.getParent()->getOrInsertFunction(
+      "__rtsan_notify_blocking_call", FuncType);
+
+  Builder.CreateCall(Func, {NameArg});
+}
+
 RealtimeSanitizerPass::RealtimeSanitizerPass(
     const RealtimeSanitizerOptions &Options) {}
 
@@ -53,10 +74,12 @@ PreservedAnalyses RealtimeSanitizerPass::run(Function &F,
   if (F.hasFnAttribute(Attribute::SanitizeRealtime)) {
     insertCallAtFunctionEntryPoint(F, "__rtsan_realtime_enter");
     insertCallAtAllFunctionExitPoints(F, "__rtsan_realtime_exit");
+    return rtsanPreservedCFGAnalyses();
+  }
 
-    PreservedAnalyses PA;
-    PA.preserveSet<CFGAnalyses>();
-    return PA;
+  if (F.hasFnAttribute(Attribute::SanitizeRealtimeUnsafe)) {
+    insertNotifyBlockingCallAtFunctionEntryPoint(F);
+    return rtsanPreservedCFGAnalyses();
   }
 
   return PreservedAnalyses::all();
diff --git a/llvm/test/Instrumentation/RealtimeSanitizer/rtsan_unsafe.ll b/llvm/test/Instrumentation/RealtimeSanitizer/rtsan_unsafe.ll
new file mode 100644
index 00000000000000..fe9f13778e35af
--- /dev/null
+++ b/llvm/test/Instrumentation/RealtimeSanitizer/rtsan_unsafe.ll
@@ -0,0 +1,19 @@
+; RUN: opt < %s -passes=rtsan -S | FileCheck %s
+
+define void @_Z17blocking_functionv() #0 {
+  ret void
+}
+
+define noundef i32 @main() #2 {
+  call void @_Z17blocking_functionv() #4
+  ret i32 0
+}
+
+attributes #0 = { mustprogress noinline sanitize_realtime_unsafe optnone ssp uwtable(sync) }
+
+; RealtimeSanitizer pass should create the demangled function name as a global string, and
+; pass it as input to an inserted __rtsan_expect_not_realtime call at the function entrypoint
+; CHECK: [[GLOBAL_STR:@[a-zA-Z0-9\.]+]]
+; CHECK-SAME: c"blocking_function()\00"
+; CHECK-LABEL: @_Z17blocking_functionv()
+; CHECK-NEXT: call{{.*}}@__rtsan_notify_blocking_call(ptr{{.*}}[[GLOBAL_STR]])

``````````

</details>


https://github.com/llvm/llvm-project/pull/109543


More information about the llvm-commits mailing list