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

Rtsan/blocking 2 llvm pass #109543

Open
wants to merge 4 commits into
base: main
Choose a base branch
from

Conversation

davidtrevelyan
Copy link
Contributor

Pass update for sanitize_realtime_unsafe

Follows #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.

  • 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)

@llvmbot
Copy link
Collaborator

llvmbot commented Sep 21, 2024

@llvm/pr-subscribers-llvm-transforms

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

Author: None (davidtrevelyan)

Changes

Pass update for sanitize_realtime_unsafe

Follows #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.

  • 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 --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]])

@davidtrevelyan
Copy link
Contributor Author

@cjappl for review

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.

2 participants