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

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


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

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


>From 3353988fdd49fad26afa12f423f39183a6194495 Mon Sep 17 00:00:00 2001
From: David Trevelyan <david.trevelyan at gmail.com>
Date: Fri, 30 Aug 2024 15:14:24 +0100
Subject: [PATCH 1/4] Implement sanitize_realtime_unsafe Pass

---
 .../Instrumentation/RealtimeSanitizer.cpp     | 28 +++++++++++++++++--
 .../RealtimeSanitizer/rtsan_unsafe.ll         | 16 +++++++++++
 2 files changed, 41 insertions(+), 3 deletions(-)
 create mode 100644 llvm/test/Instrumentation/RealtimeSanitizer/rtsan_unsafe.ll

diff --git a/llvm/lib/Transforms/Instrumentation/RealtimeSanitizer.cpp b/llvm/lib/Transforms/Instrumentation/RealtimeSanitizer.cpp
index 7854cf4f2c625f..ae60122c13b079 100644
--- a/llvm/lib/Transforms/Instrumentation/RealtimeSanitizer.cpp
+++ b/llvm/lib/Transforms/Instrumentation/RealtimeSanitizer.cpp
@@ -45,6 +45,26 @@ static void insertCallAtAllFunctionExitPoints(Function &Fn,
         insertCallBeforeInstruction(Fn, I, InsertFnName);
 }
 
+static PreservedAnalyses rtsanPreservedAnalyses() {
+  PreservedAnalyses PA;
+  PA.preserveSet<CFGAnalyses>();
+  return PA;
+}
+
+static void transformRealtimeUnsafeFunction(Function &F) {
+  IRBuilder<> Builder(&F.front().front());
+  Value *NameArg = Builder.CreateGlobalString(F.getName());
+
+  FunctionType *FuncType =
+      FunctionType::get(Type::getVoidTy(F.getContext()),
+                        {PointerType::getUnqual(F.getContext())}, false);
+
+  FunctionCallee Func = F.getParent()->getOrInsertFunction(
+      "__rtsan_expect_not_realtime", FuncType);
+
+  Builder.CreateCall(Func, {NameArg});
+}
+
 RealtimeSanitizerPass::RealtimeSanitizerPass(
     const RealtimeSanitizerOptions &Options) {}
 
@@ -53,10 +73,12 @@ PreservedAnalyses RealtimeSanitizerPass::run(Function &F,
   if (F.hasFnAttribute(Attribute::SanitizeRealtime)) {
     insertCallAtFunctionEntryPoint(F, "__rtsan_realtime_enter");
     insertCallAtAllFunctionExitPoints(F, "__rtsan_realtime_exit");
+    return rtsanPreservedAnalyses();
+  }
 
-    PreservedAnalyses PA;
-    PA.preserveSet<CFGAnalyses>();
-    return PA;
+  if (F.hasFnAttribute(Attribute::SanitizeRealtimeUnsafe)) {
+    transformRealtimeUnsafeFunction(F);
+    return rtsanPreservedAnalyses();
   }
 
   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..646a9a0e22e213
--- /dev/null
+++ b/llvm/test/Instrumentation/RealtimeSanitizer/rtsan_unsafe.ll
@@ -0,0 +1,16 @@
+; RUN: opt < %s -passes=rtsan -S | FileCheck %s
+
+define void @blocking_function() #0 {
+  ret void
+}
+
+define noundef i32 @main() #2 {
+  call void @blocking_function() #4
+  ret i32 0
+}
+
+attributes #0 = { mustprogress noinline sanitize_realtime_unsafe optnone ssp uwtable(sync) }
+
+; RealtimeSanitizer pass should insert __rtsan_expect_not_realtime at function entrypoint
+; CHECK-LABEL: @blocking_function()
+; CHECK-NEXT: call{{.*}}@__rtsan_expect_not_realtime({{ptr .*}})

>From f4c722c4704adf97ca34b4427ff21878057d45ef Mon Sep 17 00:00:00 2001
From: David Trevelyan <david.trevelyan at gmail.com>
Date: Fri, 30 Aug 2024 16:24:06 +0100
Subject: [PATCH 2/4] Rename rtsan pass functions to be more explicit

---
 .../Transforms/Instrumentation/RealtimeSanitizer.cpp   | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/llvm/lib/Transforms/Instrumentation/RealtimeSanitizer.cpp b/llvm/lib/Transforms/Instrumentation/RealtimeSanitizer.cpp
index ae60122c13b079..b977badc9d6c1f 100644
--- a/llvm/lib/Transforms/Instrumentation/RealtimeSanitizer.cpp
+++ b/llvm/lib/Transforms/Instrumentation/RealtimeSanitizer.cpp
@@ -45,13 +45,13 @@ static void insertCallAtAllFunctionExitPoints(Function &Fn,
         insertCallBeforeInstruction(Fn, I, InsertFnName);
 }
 
-static PreservedAnalyses rtsanPreservedAnalyses() {
+static PreservedAnalyses rtsanPreservedCFGAnalyses() {
   PreservedAnalyses PA;
   PA.preserveSet<CFGAnalyses>();
   return PA;
 }
 
-static void transformRealtimeUnsafeFunction(Function &F) {
+static void insertExpectNotRealtimeAtFunctionEntryPoint(Function &F) {
   IRBuilder<> Builder(&F.front().front());
   Value *NameArg = Builder.CreateGlobalString(F.getName());
 
@@ -73,12 +73,12 @@ PreservedAnalyses RealtimeSanitizerPass::run(Function &F,
   if (F.hasFnAttribute(Attribute::SanitizeRealtime)) {
     insertCallAtFunctionEntryPoint(F, "__rtsan_realtime_enter");
     insertCallAtAllFunctionExitPoints(F, "__rtsan_realtime_exit");
-    return rtsanPreservedAnalyses();
+    return rtsanPreservedCFGAnalyses();
   }
 
   if (F.hasFnAttribute(Attribute::SanitizeRealtimeUnsafe)) {
-    transformRealtimeUnsafeFunction(F);
-    return rtsanPreservedAnalyses();
+    insertExpectNotRealtimeAtFunctionEntryPoint(F);
+    return rtsanPreservedCFGAnalyses();
   }
 
   return PreservedAnalyses::all();

>From b354df2ffd8b7d1992ea4dc584bce28c55d01f15 Mon Sep 17 00:00:00 2001
From: David Trevelyan <david.trevelyan at gmail.com>
Date: Sat, 31 Aug 2024 19:04:23 +0100
Subject: [PATCH 3/4] Demangle [[clang::blocking]] function names in rtsan pass

---
 .../Instrumentation/RealtimeSanitizer.cpp           |  3 ++-
 .../RealtimeSanitizer/rtsan_unsafe.ll               | 13 ++++++++-----
 2 files changed, 10 insertions(+), 6 deletions(-)

diff --git a/llvm/lib/Transforms/Instrumentation/RealtimeSanitizer.cpp b/llvm/lib/Transforms/Instrumentation/RealtimeSanitizer.cpp
index b977badc9d6c1f..c3ea1471796bdf 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;
@@ -53,7 +54,7 @@ static PreservedAnalyses rtsanPreservedCFGAnalyses() {
 
 static void insertExpectNotRealtimeAtFunctionEntryPoint(Function &F) {
   IRBuilder<> Builder(&F.front().front());
-  Value *NameArg = Builder.CreateGlobalString(F.getName());
+  Value *NameArg = Builder.CreateGlobalString(demangle(F.getName()));
 
   FunctionType *FuncType =
       FunctionType::get(Type::getVoidTy(F.getContext()),
diff --git a/llvm/test/Instrumentation/RealtimeSanitizer/rtsan_unsafe.ll b/llvm/test/Instrumentation/RealtimeSanitizer/rtsan_unsafe.ll
index 646a9a0e22e213..0df01b95a79aea 100644
--- a/llvm/test/Instrumentation/RealtimeSanitizer/rtsan_unsafe.ll
+++ b/llvm/test/Instrumentation/RealtimeSanitizer/rtsan_unsafe.ll
@@ -1,16 +1,19 @@
 ; RUN: opt < %s -passes=rtsan -S | FileCheck %s
 
-define void @blocking_function() #0 {
+define void @_Z17blocking_functionv() #0 {
   ret void
 }
 
 define noundef i32 @main() #2 {
-  call void @blocking_function() #4
+  call void @_Z17blocking_functionv() #4
   ret i32 0
 }
 
 attributes #0 = { mustprogress noinline sanitize_realtime_unsafe optnone ssp uwtable(sync) }
 
-; RealtimeSanitizer pass should insert __rtsan_expect_not_realtime at function entrypoint
-; CHECK-LABEL: @blocking_function()
-; CHECK-NEXT: call{{.*}}@__rtsan_expect_not_realtime({{ptr .*}})
+; 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_expect_not_realtime(ptr{{.*}}[[GLOBAL_STR]])

>From 80c897e2b6edd891e445001e072e842754a83066 Mon Sep 17 00:00:00 2001
From: David Trevelyan <david.trevelyan at gmail.com>
Date: Sat, 21 Sep 2024 12:47:43 -0600
Subject: [PATCH 4/4] [rtsan] Update notify function name in blocking Pass

---
 llvm/lib/Transforms/Instrumentation/RealtimeSanitizer.cpp   | 6 +++---
 llvm/test/Instrumentation/RealtimeSanitizer/rtsan_unsafe.ll | 2 +-
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/llvm/lib/Transforms/Instrumentation/RealtimeSanitizer.cpp b/llvm/lib/Transforms/Instrumentation/RealtimeSanitizer.cpp
index c3ea1471796bdf..f27cf83cd3cf3d 100644
--- a/llvm/lib/Transforms/Instrumentation/RealtimeSanitizer.cpp
+++ b/llvm/lib/Transforms/Instrumentation/RealtimeSanitizer.cpp
@@ -52,7 +52,7 @@ static PreservedAnalyses rtsanPreservedCFGAnalyses() {
   return PA;
 }
 
-static void insertExpectNotRealtimeAtFunctionEntryPoint(Function &F) {
+static void insertNotifyBlockingCallAtFunctionEntryPoint(Function &F) {
   IRBuilder<> Builder(&F.front().front());
   Value *NameArg = Builder.CreateGlobalString(demangle(F.getName()));
 
@@ -61,7 +61,7 @@ static void insertExpectNotRealtimeAtFunctionEntryPoint(Function &F) {
                         {PointerType::getUnqual(F.getContext())}, false);
 
   FunctionCallee Func = F.getParent()->getOrInsertFunction(
-      "__rtsan_expect_not_realtime", FuncType);
+      "__rtsan_notify_blocking_call", FuncType);
 
   Builder.CreateCall(Func, {NameArg});
 }
@@ -78,7 +78,7 @@ PreservedAnalyses RealtimeSanitizerPass::run(Function &F,
   }
 
   if (F.hasFnAttribute(Attribute::SanitizeRealtimeUnsafe)) {
-    insertExpectNotRealtimeAtFunctionEntryPoint(F);
+    insertNotifyBlockingCallAtFunctionEntryPoint(F);
     return rtsanPreservedCFGAnalyses();
   }
 
diff --git a/llvm/test/Instrumentation/RealtimeSanitizer/rtsan_unsafe.ll b/llvm/test/Instrumentation/RealtimeSanitizer/rtsan_unsafe.ll
index 0df01b95a79aea..fe9f13778e35af 100644
--- a/llvm/test/Instrumentation/RealtimeSanitizer/rtsan_unsafe.ll
+++ b/llvm/test/Instrumentation/RealtimeSanitizer/rtsan_unsafe.ll
@@ -16,4 +16,4 @@ attributes #0 = { mustprogress noinline sanitize_realtime_unsafe optnone ssp uwt
 ; CHECK: [[GLOBAL_STR:@[a-zA-Z0-9\.]+]]
 ; CHECK-SAME: c"blocking_function()\00"
 ; CHECK-LABEL: @_Z17blocking_functionv()
-; CHECK-NEXT: call{{.*}}@__rtsan_expect_not_realtime(ptr{{.*}}[[GLOBAL_STR]])
+; CHECK-NEXT: call{{.*}}@__rtsan_notify_blocking_call(ptr{{.*}}[[GLOBAL_STR]])



More information about the llvm-commits mailing list